Class: Datadog::Core::TokenBucket

Inherits:
RateLimiter show all
Defined in:
lib/datadog/core/rate_limiter.rb

Overview

Implementation of the Token Bucket metering algorithm for rate limiting.

See Also:

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(rate, max_tokens = rate) ⇒ TokenBucket

Returns a new instance of TokenBucket.

Parameters:

  • rate (Numeric)

    Allowance rate, in units per second if rate is negative, always allow if rate is zero, never allow

  • max_tokens (Numeric) (defaults to: rate)

    Limit of available tokens

Raises:

  • (ArgumentError)


40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/datadog/core/rate_limiter.rb', line 40

def initialize(rate, max_tokens = rate)
  super()

  raise ArgumentError, "rate must be a number: #{rate}" unless rate.is_a?(Numeric)
  raise ArgumentError, "max_tokens must be a number: #{max_tokens}" unless max_tokens.is_a?(Numeric)

  @rate = rate
  @max_tokens = max_tokens

  @tokens = max_tokens
  @total_messages = 0
  @conforming_messages = 0
  @prev_conforming_messages = nil
  @prev_total_messages = nil
  @current_window = nil

  @last_refill = Core::Utils::Time.get_time
end

Instance Attribute Details

#max_tokensObject (readonly)

Returns the value of attribute max_tokens.



34
35
36
# File 'lib/datadog/core/rate_limiter.rb', line 34

def max_tokens
  @max_tokens
end

#rateObject (readonly)

Returns the value of attribute rate.



34
35
36
# File 'lib/datadog/core/rate_limiter.rb', line 34

def rate
  @rate
end

Instance Method Details

#allow?(size = 1) ⇒ Boolean

Checks if a message of provided size conforms with the current bucket limit.

If it does, return true and remove size tokens from the bucket. If it does not, return false without affecting the tokens from the bucket.

Returns:

  • (Boolean)

    true if message conforms with current bucket limit



68
69
70
71
72
# File 'lib/datadog/core/rate_limiter.rb', line 68

def allow?(size = 1)
  allowed = should_allow?(size)
  update_rate_counts(allowed)
  allowed
end

#available_tokensNumeric

Returns number of tokens currently available.

Returns:

  • (Numeric)

    number of tokens currently available



106
107
108
# File 'lib/datadog/core/rate_limiter.rb', line 106

def available_tokens
  @tokens
end

#current_window_rateFloat

Ratio of ‘conformance’ per ‘total messages’ checked on this bucket

Returns 1.0 when no messages have been checked yet.

Returns:

  • (Float)

    Conformance ratio, between [0,1]



99
100
101
102
103
# File 'lib/datadog/core/rate_limiter.rb', line 99

def current_window_rate
  return 1.0 if @total_messages.zero?

  @conforming_messages.to_f / @total_messages
end

#effective_rateFloat

Ratio of ‘conformance’ per ‘total messages’ checked averaged for the past 2 buckets

Returns 1.0 when no messages have been checked yet.

Returns:

  • (Float)

    Conformance ratio, between [0,1]



80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/datadog/core/rate_limiter.rb', line 80

def effective_rate
  return 0.0 if @rate.zero?
  return 1.0 if @rate < 0 || @total_messages.zero?

  return current_window_rate if @prev_conforming_messages.nil? || @prev_total_messages.nil?

  # Steep: Due to https://github.com/soutaro/steep/issues/477,
  # the previous nil check does not narrow type to Integer
  # The annotation fixes the typing error, but it takes effect in the method
  # @type ivar @prev_total_messages: Integer
  (@conforming_messages.to_f + @prev_conforming_messages.to_f) / (@total_messages + @prev_total_messages)
end