No release in over 3 years
Ruby gem for distributed rate limiting backed by Redis. Provides a sliding-window limiter with configurable polling and maximum wait time, suitable for multi-process and multi-thread workloads.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
 Dependencies

Runtime

 Project Readme

Hanikamu::RateLimit

ci

Distributed, Redis-backed rate limiting with a sliding window algorithm. Works across processes and threads by coordinating through Redis.

Table of Contents

  1. Why Hanikamu::RateLimit?
  2. Quick Start
  3. Configuration
  4. Usage
  5. Error Handling
  6. Testing
  7. Development
  8. 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 install

2. 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)
end

3. Limit a method

class MyService
  extend Hanikamu::RateLimit::Mixin

  limit_method :execute, rate: 5, interval: 1.0

  def execute
    # work
  end
end

4. Call it

MyService.new.execute

Configuration

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:

  • rate and interval (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.0

Use a registered limit shared across classes:

class ExternalApiClient
  extend Hanikamu::RateLimit::Mixin

  limit_with :execute, registry: :external_api

  def execute
    # work
  end
end

Registry precedence (highest to lowest):

  1. Per-method overrides passed to limit_with.
  2. Registered limit options.
  3. 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
end

You 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 rake

License

MIT