Class: RubyReactor::RateLimit
- Inherits:
-
Object
- Object
- RubyReactor::RateLimit
- Defined in:
- lib/ruby_reactor/rate_limit.rb
Overview
Distributed rate limiter (fixed-window counter, multi-window aware).
A ‘RateLimit` is configured with one or more (period, limit) tuples. `check_and_increment!` atomically verifies every window has headroom and, if so, increments all of them. The check uses a single Lua script so nothing slips through between read and write.
When any window is over-limit the call raises ‘ExceededError` carrying a `retry_after_seconds` hint (time until the tightest failing bucket rolls). The Sidekiq worker uses this hint to schedule a precise snooze.
Defined Under Namespace
Classes: ExceededError
Instance Attribute Summary collapse
-
#key_base ⇒ Object
readonly
Returns the value of attribute key_base.
-
#limits ⇒ Object
readonly
Returns the value of attribute limits.
Instance Method Summary collapse
- #check_and_increment! ⇒ Object
-
#initialize(key_base, limits:) ⇒ RateLimit
constructor
A new instance of RateLimit.
Constructor Details
#initialize(key_base, limits:) ⇒ RateLimit
Returns a new instance of RateLimit.
33 34 35 36 |
# File 'lib/ruby_reactor/rate_limit.rb', line 33 def initialize(key_base, limits:) @key_base = key_base @limits = limits end |
Instance Attribute Details
#key_base ⇒ Object (readonly)
Returns the value of attribute key_base.
28 29 30 |
# File 'lib/ruby_reactor/rate_limit.rb', line 28 def key_base @key_base end |
#limits ⇒ Object (readonly)
Returns the value of attribute limits.
28 29 30 |
# File 'lib/ruby_reactor/rate_limit.rb', line 28 def limits @limits end |
Instance Method Details
#check_and_increment! ⇒ Object
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
# File 'lib/ruby_reactor/rate_limit.rb', line 38 def check_and_increment! now = Time.now.to_i keys = @limits.map { |spec| bucket_key(spec, now) } argv = [now] @limits.each do |spec| argv << spec[:period_seconds] argv << spec[:limit] argv << spec[:period_seconds] * 2 # TTL: generous, auto-cleans stale buckets end allowed, retry_after, failed_index = adapter.rate_limit_check_and_increment(keys, argv) return true if allowed == 1 failed = @limits[failed_index - 1] raise ExceededError.new( "Rate limit '#{@key_base}' exceeded (#{failed[:limit]}/#{failed[:name]}); " \ "retry in #{retry_after}s", retry_after_seconds: retry_after, key_base: @key_base, limit: failed[:limit], period_seconds: failed[:period_seconds], period_name: failed[:name] ) end |