Class: GRApiManager::RateLimiter

Inherits:
Object
  • Object
show all
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

Instance Method Summary collapse

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_requestsObject (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_secondsObject (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.

Returns:

  • (Boolean)


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

#statsObject

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