# frozen_string_literal: true module PlainApm class Backoff ## # Exponential backoff with jitter. DEFAULT_BASE_SECONDS = 1.5 DEFAULT_MAX_RETRIES = 10 # sum_0^10 1.5 ** k ~ 170s DEFAULT_JITTER_MULTIPLIER = 0.2 # % of the current retry interval ## # @param base_seconds [Integer] base of the exponential retry. # @param max_retries [Integer] maximum retries to perform. # @param jitter_multiplier [Float] % of the current retry interval to use for jitter. def initialize(base_seconds: nil, max_retries: nil, jitter_multiplier: nil) @base_seconds = base_seconds || DEFAULT_BASE_SECONDS @max_retries = max_retries || DEFAULT_MAX_RETRIES @jitter_multiplier = jitter_multiplier || DEFAULT_JITTER_MULTIPLIER end ## # @param retries [Integer] Number of current retry attempts. # # @return [Integer|nil] Amount of time slept, or nil if out of retries. def delay_time(retries:) return if retries >= max_retries base_interval = (base_seconds**retries) jitter_interval = base_interval * jitter_multiplier # The random factor is never -1, but that shouldn't be an issue. base_interval + jitter_interval * (1.0 - 2.0 * rand) end private attr_reader :base_seconds, :max_retries, :jitter_multiplier end end