Class: Philiprehberger::RateLimiter::FixedWindow
- Inherits:
-
Object
- Object
- Philiprehberger::RateLimiter::FixedWindow
- Includes:
- StatsTracking
- Defined in:
- lib/philiprehberger/rate_limiter/fixed_window.rb
Overview
Fixed-window rate limiter. Each key stores a single counter plus the timestamp when its current window opened — O(1) memory per key. The counter resets to zero once the window duration elapses.
Instance Attribute Summary collapse
-
#limit ⇒ Object
readonly
Returns the value of attribute limit.
-
#window ⇒ Object
readonly
Returns the value of attribute window.
Instance Method Summary collapse
-
#allow?(key, weight: 1) ⇒ Boolean
Check if a request is allowed and consume slot(s) in the window.
-
#allow_batch(keys) ⇒ Hash{Object => Boolean}
Check many keys in a single mutex acquisition.
-
#clear ⇒ void
Clear state for all keys (resets quotas and stats for every tracked key).
-
#info(key) ⇒ Hash
Build a usage info hash for a key.
-
#info_batch(keys) ⇒ Hash{Object => Hash}
Build info hashes for many keys in a single mutex acquisition.
-
#initialize(limit:, window:) ⇒ FixedWindow
constructor
A new instance of FixedWindow.
-
#peek(key) ⇒ Boolean
Check if a request would be allowed without consuming a slot.
-
#refund(key, amount: 1) ⇒ nil
Return previously consumed slot(s) to a key (e.g. on downstream failure).
-
#remaining(key) ⇒ Integer
Return the number of remaining slots in the current window.
-
#reset(key) ⇒ void
Clear the window for a key.
-
#retry_after(key = :default) ⇒ Float
Seconds until the next request would be allowed, suitable for the HTTP Retry-After header.
-
#used(key) ⇒ Integer
Number of currently consumed slots for a key in the active window.
-
#wait_time(key = :default, weight: 1) ⇒ Float
Seconds until the next request would be allowed.
Methods included from StatsTracking
#allow!, #block, #keys, #on_reject, #stats, #throttle
Constructor Details
#initialize(limit:, window:) ⇒ FixedWindow
Returns a new instance of FixedWindow.
17 18 19 20 21 22 23 |
# File 'lib/philiprehberger/rate_limiter/fixed_window.rb', line 17 def initialize(limit:, window:) @limit = limit @window = window @store = {} @mutex = Mutex.new init_stats end |
Instance Attribute Details
#limit ⇒ Object (readonly)
Returns the value of attribute limit.
13 14 15 |
# File 'lib/philiprehberger/rate_limiter/fixed_window.rb', line 13 def limit @limit end |
#window ⇒ Object (readonly)
Returns the value of attribute window.
13 14 15 |
# File 'lib/philiprehberger/rate_limiter/fixed_window.rb', line 13 def window @window end |
Instance Method Details
#allow?(key, weight: 1) ⇒ Boolean
Check if a request is allowed and consume slot(s) in the window.
30 31 32 |
# File 'lib/philiprehberger/rate_limiter/fixed_window.rb', line 30 def allow?(key, weight: 1) @mutex.synchronize { try_acquire(key, weight) } end |
#allow_batch(keys) ⇒ Hash{Object => Boolean}
Check many keys in a single mutex acquisition.
38 39 40 41 42 |
# File 'lib/philiprehberger/rate_limiter/fixed_window.rb', line 38 def allow_batch(keys) @mutex.synchronize do keys.to_h { |key| [key, try_acquire(key, 1)] } end end |
#clear ⇒ void
This method returns an undefined value.
Clear state for all keys (resets quotas and stats for every tracked key)
79 80 81 82 83 84 85 |
# File 'lib/philiprehberger/rate_limiter/fixed_window.rb', line 79 def clear @mutex.synchronize do @store.clear @stats_store.clear end nil end |
#info(key) ⇒ Hash
Build a usage info hash for a key.
91 92 93 |
# File 'lib/philiprehberger/rate_limiter/fixed_window.rb', line 91 def info(key) @mutex.synchronize { build_info(key) } end |
#info_batch(keys) ⇒ Hash{Object => Hash}
Build info hashes for many keys in a single mutex acquisition.
99 100 101 102 103 |
# File 'lib/philiprehberger/rate_limiter/fixed_window.rb', line 99 def info_batch(keys) @mutex.synchronize do keys.to_h { |key| [key, build_info(key)] } end end |
#peek(key) ⇒ Boolean
Check if a request would be allowed without consuming a slot.
48 49 50 |
# File 'lib/philiprehberger/rate_limiter/fixed_window.rb', line 48 def peek(key) @mutex.synchronize { count_remaining(key).positive? } end |
#refund(key, amount: 1) ⇒ nil
Return previously consumed slot(s) to a key (e.g. on downstream failure).
110 111 112 113 114 115 116 |
# File 'lib/philiprehberger/rate_limiter/fixed_window.rb', line 110 def refund(key, amount: 1) @mutex.synchronize do bucket = roll(key) bucket[:count] = [bucket[:count] - amount, 0].max nil end end |
#remaining(key) ⇒ Integer
Return the number of remaining slots in the current window.
56 57 58 |
# File 'lib/philiprehberger/rate_limiter/fixed_window.rb', line 56 def remaining(key) @mutex.synchronize { count_remaining(key) } end |
#reset(key) ⇒ void
This method returns an undefined value.
Clear the window for a key.
72 73 74 |
# File 'lib/philiprehberger/rate_limiter/fixed_window.rb', line 72 def reset(key) @mutex.synchronize { @store.delete(key.to_s) } end |
#retry_after(key = :default) ⇒ Float
Seconds until the next request would be allowed, suitable for the HTTP Retry-After header. Returns 0.0 when a request is allowed right now.
138 139 140 141 142 143 144 145 146 |
# File 'lib/philiprehberger/rate_limiter/fixed_window.rb', line 138 def retry_after(key = :default) @mutex.synchronize do bucket = roll(key) return 0.0 if bucket[:count] < @limit wait = (bucket[:window_start] + @window) - now [wait, 0.0].max end end |
#used(key) ⇒ Integer
Number of currently consumed slots for a key in the active window.
64 65 66 |
# File 'lib/philiprehberger/rate_limiter/fixed_window.rb', line 64 def used(key) @mutex.synchronize { roll(key)[:count] } end |
#wait_time(key = :default, weight: 1) ⇒ Float
Seconds until the next request would be allowed.
123 124 125 126 127 128 129 130 131 |
# File 'lib/philiprehberger/rate_limiter/fixed_window.rb', line 123 def wait_time(key = :default, weight: 1) @mutex.synchronize do bucket = roll(key) return 0.0 if bucket[:count] + weight <= @limit wait = (bucket[:window_start] + @window) - now [wait, 0.0].max end end |