gitlab-org--gitlab-foss/lib/gitlab/ci/runner/backoff.rb

75 lines
2.3 KiB
Ruby

# frozen_string_literal: true
module Gitlab
module Ci
module Runner
##
# Runner Backoff class is an implementation of an exponential backoff
# used when a runner communicates with GitLab. We typically use it when a
# runner retries sending a build status after we created a build pending
# state.
#
# Backoff is calculated based on the backoff slot which is always a power
# of 2:
#
# 0s - 3s duration -> 1 second backoff
# 4s - 7s duration -> 2 seconds backoff
# 8s - 15s duration -> 4 seconds backoff
# 16s - 31s duration -> 8 seconds backoff
# 32s - 63s duration -> 16 seconds backoff
# 64s - 127s duration -> 32 seconds backoff
# 127s - 256s+ duration -> 64 seconds backoff
#
# It means that first 15 requests made by a runner will need to respect
# following backoffs:
#
# 0s -> 1 second backoff (backoff started, slot 0, 2^0 backoff)
# 1s -> 1 second backoff
# 2s -> 1 second backoff
# 3s -> 1 seconds backoff
# (slot 1 - 2^1 backoff)
# 4s -> 2 seconds backoff
# 6s -> 2 seconds backoff
# (slot 2 - 2^2 backoff)
# 8s -> 4 seconds backoff
# 12s -> 4 seconds backoff
# (slot 3 - 2^3 backoff)
# 16s -> 8 seconds backoff
# 24s -> 8 seconds backoff
# (slot 4 - 2^4 backoff)
# 32s -> 16 seconds backoff
# 48s -> 16 seconds backoff
# (slot 5 - 2^5 backoff)
# 64s -> 32 seconds backoff
# 96s -> 32 seconds backoff
# (slot 6 - 2^6 backoff)
# 128s -> 64 seconds backoff
#
# There is a cap on the backoff - it will never exceed 64 seconds.
#
class Backoff
def initialize(started)
@started = started
if duration < 0
raise ArgumentError, 'backoff duration negative'
end
end
def duration
(Time.current - @started).ceil
end
def slot
return 0 if duration < 2
Math.log(duration, 2).floor - 1
end
def to_seconds
2**[slot, 6].min
end
end
end
end
end