Install the ratelimit gem
masterTo use ratelimit in your Ruby application, add it to your Gemfile:
gem 'ratelimit'Then run bundle to install, or install it directly via the command line using gem install ratelimit.
repository·master·Indexed 42 days ago
https://github.com/ejfinneran/ratelimitA Ruby library for distributed rate limiting across multiple servers using Redis. It allows developers to manage execution thresholds for specific subjects, such as users or API keys, providing methods to track activity counts, check if limits are exceeded, and execute code blocks within defined thresholds.
To use ratelimit in your Ruby application, add it to your Gemfile:
gem 'ratelimit'Then run bundle to install, or install it directly via the command line using gem install ratelimit.
By default, Ratelimit connects to a Redis server at 127.0.0.1:6379. To use a remote or custom Redis server, instantiate a Redis client with your specific configuration and pass it to the Ratelimit.new constructor using the redis: keyword argument.
redis = Redis.new(host: "10.0.1.1", port: 6380, db: 15)
ratelimit = Ratelimit.new("messages", redis: redis)Initialize a Ratelimit instance with a unique name (e.g., for a specific type of action like "messages"). Use the add method to increment the count for a specific subject (like a phone number, email, or URL) and the count method to retrieve the number of executions for that subject within a specific time interval in seconds.
ratelimit = Ratelimit.new("messages")
5.times do
ratelimit.add(phone_number)
end
ratelimit.count(phone_number, 30)
# => 5You can check the current rate status using exceeded? or within_bounds?. Both methods require a subject, a threshold (maximum allowed executions), and an interval (time window in seconds).
exceeded?(subject, threshold: X, interval: Y): Returns true if the count for the subject in the given interval is greater than the threshold.within_bounds?(subject, threshold: X, interval: Y): Returns true if the count is within the allowed threshold.ratelimit.exceeded?(phone_number, threshold: 10, interval: 30)
# => false
ratelimit.within_bounds?(phone_number, threshold: 10, interval: 30)
# => trueThe exec_within_threshold method allows you to wrap code in a block that only executes if the rate limit has not been exceeded.
Warning: This method is blocking; it will block execution until the block can be run (i.e., until the rate limit allows it). You must manually call ratelimit.add(subject) inside the block to record the execution.
ratelimit.exec_within_threshold phone_number, threshold: 10, interval: 30 do
some_rate_limited_code
ratelimit.add(phone_number)
endTo use Ratelimit, instantiate a new object with a unique key to identify the specific rate limit category (e.g., 'emails'). You can provide an optional options hash to configure the bucket behavior and the Redis connection.
Options:
bucket_span (Integer): The total time span to track in seconds. Defaults to 600.bucket_interval (Integer): The duration of each individual bucket in seconds. Defaults to 5.bucket_expiry (Integer): How long data is kept in each bucket before auto-expiry. Cannot be larger than bucket_span. Defaults to @bucket_span.redis (Redis): A Redis client instance. If not provided, a new Redis.new instance is used.Constraints:
bucket_expiry must be $\le$ bucket_span.bucket_span / bucket_interval) must be at least 3.ratelimit = Ratelimit.new('emails', {
bucket_span: 600,
bucket_interval: 10,
redis: Redis.new
})Increment the counter for a specific subject (e.g., a user ID or email address) within the current time bucket.
subject (String): A unique identifier for the entity being rate-limited.count (Integer): The amount to increase the counter by. Defaults to 1.Returns the new counter value for that subject.
# Increment the count for 'user@example.com' by 1
new_count = ratelimit.add('user@example.com')Executes a provided block only when the rate limit is within bounds.
WARNING: This method is blocking. If the rate limit is exceeded, it will sleep for the duration of @bucket_interval and retry until the limit is no longer exceeded.
subject (String): The unique identifier.options[:threshold] (Integer): Maximum actions allowed. Defaults to 30.options[:interval] (Integer): Time window in seconds. Defaults to 30.Usage Pattern:
Inside the block, you should perform your action and then manually call add(subject) to record the activity.
ratelimit.exec_within_threshold('user@example.com', threshold: 5, interval: 600) do |rl|
send_email()
rl.add('user@example.com')
endratelimit.exec_within_threshold('user@example.com', threshold: 5, interval: 600) do |rl|
send_another_email
rl.add('user@example.com')
endDetermine if a subject has surpassed a specific threshold within a given interval.
exceeded?(subject, options = {})
subject (String): The unique identifier.options[:interval] (Integer): How far back to look.options[:threshold] (Integer): The maximum allowed actions.true if the count is $\ge$ threshold.within_bounds?(subject, options = {})
exceeded?.# Check if user has sent more than 5 emails in the last 10 minutes
if ratelimit.exceeded?('user@example.com', interval: 600, threshold: 5)
puts "Too many requests!"
endReturns the total count for a given subject over a specific time interval.
subject (String): The unique identifier for the entity.interval (Integer): How far back in seconds to retrieve activity. The interval is clamped between @bucket_interval and @bucket_span.Returns the sum of counts across all relevant buckets.
# Get the count for 'user@example.com' over the last 60 seconds
activity_count = ratelimit.count('user@example.com', 60)