Redis-backed rate limiting based on generic cell rate algorithm
Ruby
Clone or download
Latest commit 6594619 Jun 11, 2018

README.md

RedisGCRA

Build Status Gem Version

This gem is an implementation of GCRA for rate limiting based on Redis. The code requires Redis version 3.2 or newer since it relies on replicate_commands feature.

Installation

gem "redis-gcra"

And then execute:

$ bundle

Or install it yourself as:

$ gem install redis-gcra

Usage

In order to perform rate limiting, you need to call the limit method.

In this example the rate limit bucket has 1000 tokens in it and recovers at speed of 100 tokens per minute.

redis = Redis.new

result = RedisGCRA.limit(
  redis: redis,
  key: "overall-account/bob@example.com",
  burst: 1000,
  rate: 100,
  period: 60, # seconds
  cost: 2
)

result.limited?    # => false - request should not be limited
result.remaining   # => 998   - remaining number of requests until limited
result.retry_after # => nil   - can retry without delay
result.reset_after # => ~0.6  - in 0.6 seconds rate limiter will completely reset

# call limit 499 more times in rapid succession and you get:

result.limited?    # => true  - request should be limited
result.remaining   # => 0     - no requests can be made at this point
result.retry_after # => ~1.2  - can retry in 1.2 seconds
result.reset_after # => ~600  - in 600 seconds rate limiter will completely reset

The implementation utilizes single key in Redis that matches the key you pass to the limit method. If you need to reset rate limiter for particular key, just delete the key from Redis:

# Let's imagine `overall-account/bob@example.com` is limited.
# This will effectively reset limit for the key:
redis.del "overall-account/bob@example.com"

You call also retrieve the current state of rate limiter for particular key without actually modifying the state. In order to do that, use the peek method:

result = RedisGCRA.peek(
  redis: redis,
  key: "overall-account/bob@example.com",
  burst: 1000,
  rate: 100,
  period: 60 # seconds
)

result.limited?    # => true  - current state is limited
result.remaining   # => 0     - no requests can be made
result.retry_after # => ~0.6  - in 0.6 seconds remaining will become 1
result.reset_after # => ~600  - in 600 seconds rate limiter will completely reset

Inspiration

This code was inspired by this great blog post by Brandur Leach and his amazing work on throttled Go package.

License

The gem is available as open source under the terms of the MIT License.