Hanikamu::RateLimit
Distributed, Redis-backed rate limiting with a sliding window algorithm. Works across processes and threads by coordinating through Redis.
Table of Contents
- Why Hanikamu::RateLimit?
- Quick Start
- Configuration
- Usage
- Error Handling
- Testing
- Development
- License
Why Hanikamu::RateLimit?
- Use case: You run 40 Sidekiq workers that all hit the same external marketing API capped at 20 requests per second. Without coordination, they’ll burst and trigger throttling. With a shared limit, every worker routes through the same Redis-backed window so aggregate throughput stays at 20 req/s across the whole fleet.
- Distributed by design: Limits are enforced through Redis so multiple app instances share a single limit.
- Sliding window: Limits are based on the most recent interval window, not fixed buckets.
- Backoff with polling: When a limit is hit, the limiter sleeps in short intervals until a slot opens.
- Bounded waiting: Callers can set a max wait time to avoid waiting indefinitely.
- Minimal surface area: A single mixin and a compact queue implementation.
Quick Start
1. Install the gem
Requires Ruby 4.0 or later.
# Gemfile
gem "hanikamu-rate-limit", "~> 0.1.0"bundle install2. Configure Redis
Hanikamu::RateLimit.configure do |config|
config.redis_url = ENV.fetch("REDIS_URL")
config.check_interval = 0.25
config.max_wait_time = 1.5
config.register_limit(:external_api, rate: 5, interval: 0.5, check_interval: 0.5, max_wait_time: 5)
end3. Limit a method
class MyService
extend Hanikamu::RateLimit::Mixin
limit_method :execute, rate: 5, interval: 1.0
def execute
# work
end
end4. Call it
MyService.new.executeConfiguration
Available settings:
-
redis_url: Redis connection URL (required). -
check_interval: default sleep interval between retries (default: 0.5 seconds). -
max_wait_time: max time to wait before raising (default: 2.0 seconds). -
register_limit: define a named limit shared across classes.
Registered limit options:
-
rateandinterval(required). -
check_interval,max_wait_time(optional). -
key_prefix(optional) to force a shared Redis key; defaults to a registry-based prefix.
Usage
Optional per-method overrides:
limit_method :execute, rate: 5, interval: 1.0, check_interval: 0.1, max_wait_time: 3.0Use a registered limit shared across classes:
class ExternalApiClient
extend Hanikamu::RateLimit::Mixin
limit_with :execute, registry: :external_api
def execute
# work
end
endRegistry precedence (highest to lowest):
- Per-method overrides passed to
limit_with. - Registered limit options.
- Global defaults from
Hanikamu::RateLimit.configure.
Reset method is generated automatically:
MyService.reset_execute_limit!
### Class methods
To rate limit class methods, apply the mixin to the singleton class:
```ruby
class MyService
class << self
extend Hanikamu::RateLimit::Mixin
limit_method :call, rate: 5, interval: 1.0
def call
# work
end
end
endYou can also use registered limits:
Hanikamu::RateLimit.configure do |config|
config.register_limit(:external_api, rate: 5, interval: 1.0)
end
class MyService
class << self
extend Hanikamu::RateLimit::Mixin
limit_with :call, registry: :external_api
def call
# work
end
end
end
## Error Handling
If Redis is unavailable, `RateQueue#shift` logs a warning and returns `nil`.
## Testing
```bash
bundle exec rspec
Development
bundle exec rakeLicense
MIT