Class: ActiveHarness::RateLimit::RequestLimiter

Inherits:
Object
  • Object
show all
Defined in:
lib/active_harness/rate_limit/request_limiter.rb

Overview

Sliding-window rate limiter. Tracks request timestamps per user_id and raises if the limit is exceeded within the rolling time window.

Storage is in-memory (per-process). For multi-process environments, replace with a shared backend (Redis, Memcached, etc.) by subclassing and overriding #timestamps_for / #record_timestamp.

Usage:

limiter = RequestLimiter.new(max_requests: 10, window_seconds: 60)
limiter.check!(user_id)   # call before each request; raises if over limit

Constant Summary collapse

DEFAULT_MAX =
10
DEFAULT_WINDOW =

seconds

60

Instance Method Summary collapse

Constructor Details

#initialize(max_requests: DEFAULT_MAX, window_seconds: DEFAULT_WINDOW) ⇒ RequestLimiter

Returns a new instance of RequestLimiter.

Parameters:

  • max_requests (Integer) (defaults to: DEFAULT_MAX)

    maximum allowed requests per window

  • window_seconds (Integer) (defaults to: DEFAULT_WINDOW)

    length of the sliding window in seconds



20
21
22
23
24
25
# File 'lib/active_harness/rate_limit/request_limiter.rb', line 20

def initialize(max_requests: DEFAULT_MAX, window_seconds: DEFAULT_WINDOW)
  @max_requests   = max_requests
  @window_seconds = window_seconds
  @log            = Hash.new { |h, k| h[k] = [] }
  @mutex          = Mutex.new
end

Instance Method Details

#check!(user_id) ⇒ Object

Records this request and raises if the limit has been reached.

Parameters:

  • user_id (String, Integer, nil)

    nil disables the check

Raises:



30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# File 'lib/active_harness/rate_limit/request_limiter.rb', line 30

def check!(user_id)
  return if user_id.nil?

  key    = user_id.to_s
  now    = Time.now.to_f
  cutoff = now - @window_seconds

  @mutex.synchronize do
    @log[key].reject! { |t| t < cutoff }

    if @log[key].size >= @max_requests
      raise Errors::RequestThrottledError,
        "Rate limit exceeded: #{@max_requests} requests/#{@window_seconds}s (user: #{key})"
    end

    @log[key] << now
  end
end