Skip to content

Limiters

Daniel Pepper edited this page Aug 13, 2024 · 7 revisions

Berater::Limiter

The basic interface for all limiters.

.limit - acquire a lock. Raises a Berater::Overloaded error if limits have been exceeded. When passed a block, it will execute the block unless the limit has been exceeded. Otherwise it returns the lock, which should be released once completed.

  • capacity - override the limiter's capacity for this call
  • cost - the relative cost of this piece of work, default is 1

.utilization - a Float representing how much capacity is being used, as a percentage of the limit (0-100). A value of 100 indicates the limiter is overloaded and calls to .limit will fail.

limiter.limit(**opts) do
  # limited work
end

lock = limiter.limit
# do work inline, then manually release lock
lock.release

limiter.limit(cost: 2) do
# do extra expensive work
end

limiter.limit(capacity: 3) do
# do work within a different capacity limit
end

ConcurrencyLimiter

Limit the amount of work done concurrently, ie. simultaneously.

Berater::ConcurrencyLimiter.new(key, capacity, **opts)
  • key - name of limiter
  • capacity - maximum number of simultaneous requests
  • opts
    • timeout - maximum seconds a lock may be held (optional, but recommended)
    • redis - a redis instance
limiter = Berater::ConcurrencyLimiter.new(:key, 3, timeout: 30)
limiter.limit do
  # allow only three simultaneous requests at a time, for no more than 30 seconds each
end

# or, more conveniently
Berater(:key, 3, timeout: 30) do
  ...
end

RateLimiter

Limit usage within a given time window. It uses the leaky bucket algorithm and supports millisecond level granularity.

Berater::RateLimiter.new(key, capacity, interval, **opts)
  • key - name of limiter
  • capacity - maximum number of requests permitted
  • interval - how often the capacity limit resets. Either number of seconds or a symbol: :second, :minute, :hour
  • opts
    • redis - a redis instance
limiter = Berater::RateLimiter.new(:key, 2, :second)
limiter.limit do
  # do work, up to twice per second
end

# or, more conveniently
Berater(:key, 2, interval: :second) do
  ...
end

Unlimiter

A limiter which always succeeds.

limiter = Berater::Unlimiter.new

1_000_000.times do
  limiter.limit { puts 'yay' }
end

Inhibitor

A limiter which always fails.

limiter = Berater::Inhibitor.new

limiter.limit { never! } # raises Berater::Overloaded
Clone this wiki locally