Class: GRApiManager::RateLimiter
- Inherits:
-
Object
- Object
- GRApiManager::RateLimiter
- Defined in:
- lib/gr_api_manager.rb
Overview
RateLimiter — thread-safe sliding-window rate limiter per IP address.
Prevents any single client from flooding the server. Uses a mutex-protected in-memory store with automatic cleanup to avoid unbounded memory growth.
Instance Attribute Summary collapse
-
#max_requests ⇒ Object
readonly
Returns the value of attribute max_requests.
-
#window_seconds ⇒ Object
readonly
Returns the value of attribute window_seconds.
Instance Method Summary collapse
-
#allow?(ip) ⇒ Boolean
Returns true if the request from ip is within the allowed rate.
-
#cleanup! ⇒ Object
Removes stale entries — call periodically to prevent memory growth.
-
#initialize(max_requests:, window_seconds:) ⇒ RateLimiter
constructor
A new instance of RateLimiter.
-
#remaining(ip) ⇒ Object
Remaining requests allowed for ip in the current window.
-
#stats ⇒ Object
Summary hash — safe to log or expose on a diagnostic endpoint.
Constructor Details
#initialize(max_requests:, window_seconds:) ⇒ RateLimiter
Returns a new instance of RateLimiter.
23 24 25 26 27 28 |
# File 'lib/gr_api_manager.rb', line 23 def initialize(max_requests:, window_seconds:) @max = max_requests @window = window_seconds @store = Hash.new { |h, k| h[k] = [] } @mutex = Mutex.new end |
Instance Attribute Details
#max_requests ⇒ Object (readonly)
Returns the value of attribute max_requests.
21 22 23 |
# File 'lib/gr_api_manager.rb', line 21 def max_requests @max_requests end |
#window_seconds ⇒ Object (readonly)
Returns the value of attribute window_seconds.
21 22 23 |
# File 'lib/gr_api_manager.rb', line 21 def window_seconds @window_seconds end |
Instance Method Details
#allow?(ip) ⇒ Boolean
Returns true if the request from ip is within the allowed rate. Increments the counter for that IP on each allowed request.
32 33 34 35 36 37 38 39 40 41 |
# File 'lib/gr_api_manager.rb', line 32 def allow?(ip) @mutex.synchronize do now = Time.now.to_f cutoff = now - @window @store[ip].reject! { |t| t < cutoff } return false if @store[ip].size >= @max @store[ip] << now true end end |
#cleanup! ⇒ Object
Removes stale entries — call periodically to prevent memory growth.
53 54 55 56 57 58 59 |
# File 'lib/gr_api_manager.rb', line 53 def cleanup! @mutex.synchronize do cutoff = Time.now.to_f - @window @store.each_value { |times| times.reject! { |t| t < cutoff } } @store.delete_if { |_, times| times.empty? } end end |
#remaining(ip) ⇒ Object
Remaining requests allowed for ip in the current window.
44 45 46 47 48 49 50 |
# File 'lib/gr_api_manager.rb', line 44 def remaining(ip) @mutex.synchronize do cutoff = Time.now.to_f - @window active = @store[ip].count { |t| t >= cutoff } [@max - active, 0].max end end |
#stats ⇒ Object
Summary hash — safe to log or expose on a diagnostic endpoint.
62 63 64 65 66 |
# File 'lib/gr_api_manager.rb', line 62 def stats @mutex.synchronize do { tracked_ips: @store.size, max_requests: @max, window_seconds: @window } end end |