shopify/limiter

repository·main·Indexed 19 days ago

https://github.com/shopify/limiter

A Ruby gem for throttling or rate-limiting operations. It provides Limiter::RateQueue for manual rate limiting and Limiter::Mixin for rate-limiting instance methods via the limit_method macro, supporting configurable rates, intervals, and balanced execution.

Tokens
1.3K
Snippets
7
Records
7
Agent score
16%

What's inside shopify-limiter

  1. Rate limit instance methods using Limiter::Mixin

    main

    You can rate limit instance methods by extending Limiter::Mixin in your class and using the limit_method macro.

    By default, the rate is defined as the number of requests per minute. When the rate is exceeded, calls to the method will block until the rate limit allows for another execution.

    Key parameters:

    • rate: The maximum number of calls allowed within the interval.
    • interval: The throttling period in seconds (defaults to 60 seconds/1 minute).
    • balanced: A boolean. If true, calls are interleaved (e.g., one call every second) instead of bursting all calls at once at the start of the interval.
    class Widget
      extend Limiter::Mixin
    
      # Limit to 300 calls per minute
      limit_method :tick, rate: 300
    
      # Limit to 5 calls per second and execute a block when the limit is reached
      limit_method(:tick, rate: 5, interval: 1) do
        puts 'Limit reached'
      end
    
      # Evenly distribute 60 calls over 60 seconds
      limit_method :tick, rate: 60, balanced: true
    end
  2. Install the ruby-limiter gem

    main

    To use Limiter in your Ruby application, add it to your Gemfile:

    gem 'ruby-limiter'

    Then run bundle. Alternatively, you can install it directly via the command line:

    $ gem install ruby-limiter
  3. Reset rate limits for methods

    main

    To reset the rate limiter for a specific method (useful for testing or after external resets), call reset_<method_name>_limit! on the class that includes the mixin. For a method named :tick, use reset_tick_limit!.

    class WidgetTest < Minitest::Test
      def setup
        Widget.reset_tick_limit!
      end
    end
  4. Use Limiter::RateQueue for manual rate limiting

    main

    If the mixin is not suitable, use Limiter::RateQueue directly. You can initialize a queue with a specific capacity and interval. Calling .shift on the queue will block if the rate limit has been reached.

    You can also provide a block to the constructor, which executes every time the limit is hit (useful for logging or metrics).

    class Widget
      def initialize
        # Allows 10,000 operations per hour (3600 seconds)
        @queue = Limiter::RateQueue.new(10000, interval: 3600) do
          puts "Hit the limit, waiting"
        end
      end
    
      def tick
        @queue.shift
        # perform operation
      end
    end
  5. Reset a Limiter::RateQueue

    main

    To reset the state of a Limiter::RateQueue instance, call the .reset method on the queue object. This is useful if an external API reset occurs and you need to synchronize your local limiter.

    @queue.reset
  6. Rate-limit instance methods with `limit_method`

    main

    The limit_method DSL allows you to wrap an existing instance method with rate-limiting logic. When the method is called, it will block (via RateQueue#shift) until the rate limit allows the execution to proceed.

    Parameters:

    • method: The name of the method to rate-limit.
    • rate: The maximum number of allowed executions within the interval.
    • interval: The time window in seconds (defaults to 60).
    • balanced: A boolean indicating if the rate should be distributed evenly across the interval (defaults to false).
    • &b: An optional block passed to the underlying RateQueue.

    When using limit_method, the original method is prepended to the class, ensuring the rate-limiting logic runs before the original implementation. The method preserves both positional arguments (*args) and keyword arguments (**options).

    # Example usage within a class
    class MyClient
      include Limiter::Mixin
    
      limit_method :call_api, rate: 300, interval: 60
    
      def call_api(query)
        # Original implementation
        puts "Calling API with #{query}"
      end
    end
  7. Reset a rate-limited method's limit with `reset_{method}_limit!`

    main

    When you use limit_method to rate-limit a method, a corresponding singleton method is automatically defined to reset the rate limit for that specific method.

    If you have limited a method named :my_method, you can call reset_my_method_limit! on the instance to clear the current queue and reset the rate tracking.

    client = MyClient.new
    # ... some calls that consume the rate limit ...
    
    # Reset the limit for the :call_api method
    client.reset_call_api_limit!