Class: Parse::Agent::RateLimiter

Inherits:
Object
  • Object
show all
Defined in:
lib/parse/agent/rate_limiter.rb

Overview

Thread-safe rate limiter using a sliding window algorithm.

Prevents resource exhaustion by limiting the number of requests an agent can make within a time window.

Examples:

Basic usage

limiter = RateLimiter.new(limit: 60, window: 60)  # 60 requests per minute

limiter.check!  # Passes
# ... after too many requests ...
limiter.check!  # raises RateLimitExceeded

Check without raising

if limiter.available?
  # Make request
else
  puts "Rate limited, retry after #{limiter.retry_after}s"
end

Defined Under Namespace

Classes: RateLimitExceeded

Constant Summary collapse

DEFAULT_LIMIT =

Default requests allowed per window

60
DEFAULT_WINDOW =

Default time window in seconds

60

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(limit: DEFAULT_LIMIT, window: DEFAULT_WINDOW) ⇒ RateLimiter

Create a new rate limiter.

Parameters:

  • limit (Integer) (defaults to: DEFAULT_LIMIT)

    maximum requests per window (default: 60)

  • window (Integer) (defaults to: DEFAULT_WINDOW)

    time window in seconds (default: 60)



56
57
58
59
60
61
# File 'lib/parse/agent/rate_limiter.rb', line 56

def initialize(limit: DEFAULT_LIMIT, window: DEFAULT_WINDOW)
  @limit = limit
  @window = window
  @requests = []
  @mutex = Mutex.new
end

Instance Attribute Details

#limitInteger (readonly)

Returns maximum requests allowed per window.

Returns:

  • (Integer)

    maximum requests allowed per window



47
48
49
# File 'lib/parse/agent/rate_limiter.rb', line 47

def limit
  @limit
end

#windowInteger (readonly)

Returns time window in seconds.

Returns:

  • (Integer)

    time window in seconds



50
51
52
# File 'lib/parse/agent/rate_limiter.rb', line 50

def window
  @window
end

Instance Method Details

#available?Boolean

Check if a request can be made without blocking.

Returns:

  • (Boolean)

    true if request would be allowed



88
89
90
91
92
93
# File 'lib/parse/agent/rate_limiter.rb', line 88

def available?
  @mutex.synchronize do
    cleanup_old_requests
    @requests.size < @limit
  end
end

#check!true

Check rate limit and record request. Raises if limit exceeded.

Returns:

  • (true)

    if request is allowed

Raises:



67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
# File 'lib/parse/agent/rate_limiter.rb', line 67

def check!
  @mutex.synchronize do
    cleanup_old_requests

    if @requests.size >= @limit
      retry_after = calculate_retry_after
      raise RateLimitExceeded.new(
        retry_after: retry_after,
        limit: @limit,
        window: @window,
      )
    end

    @requests << Time.now.to_f
    true
  end
end

#remainingInteger

Get the number of remaining requests in current window.

Returns:

  • (Integer)

    remaining requests



98
99
100
101
102
103
# File 'lib/parse/agent/rate_limiter.rb', line 98

def remaining
  @mutex.synchronize do
    cleanup_old_requests
    [@limit - @requests.size, 0].max
  end
end

#reset!void

This method returns an undefined value.

Reset the rate limiter (clear all recorded requests).



119
120
121
122
123
# File 'lib/parse/agent/rate_limiter.rb', line 119

def reset!
  @mutex.synchronize do
    @requests.clear
  end
end

#retry_afterFloat?

Get seconds until rate limit resets (oldest request expires).

Returns:

  • (Float, nil)

    seconds until reset, or nil if not limited



108
109
110
111
112
113
114
# File 'lib/parse/agent/rate_limiter.rb', line 108

def retry_after
  @mutex.synchronize do
    cleanup_old_requests
    return nil if @requests.size < @limit
    calculate_retry_after
  end
end

#statsHash

Get rate limiter statistics.

Returns:

  • (Hash)

    current state information



128
129
130
131
132
133
134
135
136
137
138
139
# File 'lib/parse/agent/rate_limiter.rb', line 128

def stats
  @mutex.synchronize do
    cleanup_old_requests
    {
      limit: @limit,
      window: @window,
      used: @requests.size,
      remaining: [@limit - @requests.size, 0].max,
      retry_after: @requests.size >= @limit ? calculate_retry_after : nil,
    }
  end
end