Class: R4r::RetryBudget Abstract

Inherits:
Object
  • Object
show all
Defined in:
lib/r4r/retry_budget.rb

Overview

This class is abstract.

Represents a budget for retrying requests.

A retry budget is useful for attenuating the amplifying effects of many clients within a process retrying requests multiple times. This acts as a form of coordination between those retries.

A Ruby port of the finagle’s RetryBudget.

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.create(ttl_ms: nil, min_retries_per_second: nil, percent_can_retry: nil, clock: nil) ⇒ Object

Creates a R4r::RetryBudget that allows for about ‘percent_can_retry` percent of the total #deposit requests to be retried.

Parameters:

  • ttl_ms (Fixnum) (defaults to: nil)

    deposits created by #deposit expire after approximately ‘ttl_ms` time has passed. Must be `>= 1 second` and `<= 60 seconds`.

  • min_retries_per_second (Fixnum) (defaults to: nil)

    the minimum rate of retries allowed in order to accommodate clients that have just started issuing requests as well as clients that do not issue many requests per window. Must be non-negative and if ‘0`, then no reserve is given.

  • percent_can_retry (Float) (defaults to: nil)

    the percentage of calls to ‘deposit()` that can be retried. This is in addition to any retries allowed for via `min_retries_per_second`. Must be >= 0 and <= 1000. As an example, if `0.1` is used, then for every 10 calls to `deposit()`, 1 retry will be allowed. If `2.0` is used then every `deposit` allows for 2 retries.

  • clock (R4r::Clock) (defaults to: nil)

    the current time for testing

Raises:

  • (ArgumentError)


53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# File 'lib/r4r/retry_budget.rb', line 53

def self.create(ttl_ms: nil, min_retries_per_second: nil, percent_can_retry: nil, clock: nil)
  ttl_ms = (ttl_ms || R4r::TokenRetryBudget::DEFAULT_TTL_MS).to_i
  min_retries_per_second = (min_retries_per_second || 10).to_i
  percent_can_retry = (percent_can_retry.to_f || 0.2).to_f

  unless ttl_ms >= 0 && ttl_ms <= 60 * 1000
    raise ArgumentError, "ttl_ms must be in [1.second, 60.seconds], got #{ttl_ms}"
  end
  unless min_retries_per_second >= 0
    raise ArgumentError, "min_retries_per_second cannot be nagative, got #{min_retries_per_second}"
  end
  unless percent_can_retry >= 0.0
    raise ArgumentError, "percent_can_retry cannot be negative, got #{percent_can_retry}"
  end
  unless percent_can_retry <= R4r::TokenRetryBudget::SCALE_FACTOR
    raise ArgumentError, "percent_can_retry cannot be greater then #{R4r::TokenRetryBudget::SCALE_FACTOR}, got #{percent_can_retry}"
  end

  if min_retries_per_second == 0 && percent_can_retry == 0.0
    return R4r::RetryBudget.empty
  end

  deposit_amount = percent_can_retry == 0.0 ? 0 : R4r::TokenRetryBudget::SCALE_FACTOR.to_i
  withdrawal_amount = percent_can_retry == 0.0 ? 1 : (R4r::TokenRetryBudget::SCALE_FACTOR / percent_can_retry).to_i
  reserve = min_retries_per_second * (ttl_ms / 1000) * withdrawal_amount
  bucket = R4r::LeakyTokenBucket.new(ttl_ms: ttl_ms, reserve: reserve, clock: clock)
  R4r::TokenRetryBudget.new(bucket: bucket, deposit_amount: deposit_amount, withdrawal_amount: withdrawal_amount)
end

.emptyObject

Creates an empty retry budget.



83
84
85
# File 'lib/r4r/retry_budget.rb', line 83

def self.empty
  EmptyRetryBudget.new
end

.infiniteObject

Creates an infinite retry budget.



88
89
90
# File 'lib/r4r/retry_budget.rb', line 88

def self.infinite
  InfiniteRetryBudget.new
end

Instance Method Details

#balanceObject

The balance or number of retries that can be made now.

Raises:

  • (NotImplementedError)


31
32
33
# File 'lib/r4r/retry_budget.rb', line 31

def balance
  raise NotImplementedError
end

#depositObject

Indicates a deposit, or credit, which will typically permit future withdrawals.

Raises:

  • (NotImplementedError)


16
17
18
# File 'lib/r4r/retry_budget.rb', line 16

def deposit
  raise NotImplementedError
end

#try_withdrawBoolean

Check whether or not there is enough balance remaining to issue a retry, or make a withdrawal.

Returns:

  • (Boolean)

    ‘true`, if the retry is allowed and a withdrawal will take place. `false`, the balance should remain untouched.

Raises:

  • (NotImplementedError)


26
27
28
# File 'lib/r4r/retry_budget.rb', line 26

def try_withdraw
  raise NotImplementedError
end