Applies multiple rate limits at the same time, in a single atomic operation.
Options
See __using__/1.
Examples
defmodule MyRateLimiter do
use AtomicBucket.MultiRateLimiter
end
# application.ex
children = [.., MyRateLimiter, ..]
defmodule CallerModule do
require MyRateLimiter
@sub_buckets %{
second: {330, 2},
minute: {3_000, 7},
hour: {60_000, 30}
}
MyRateLimiter.request(:mybucket, @sub_buckets)
# Or with details.
MyRateLimiter.request_details(:mybucket, @sub_buckets)
end
Summary
Functions
Converts the current module to a multi-rate limiter
Generated API
Checks if the request is allowed according to multiple rate limits.
Checks if the request is allowed according to multiple rate limits and returns results of each bucket check.
Starts AtomicBucket server managing buckets for the limiter.
Functions
Converts the current module to a multi-rate limiter:
generates rate limiter API
adds AtomicBucket server child spec so that the module can be added to a supervision tree
This macro does only basic validation of the cleanup parameters. Developers must ensure that buckets idling for more than ~24 days are deleted: longer periods are not supported by the wrapping timer used by the library.
Options:
:tableETS table name atom. By default is implementing module name.:cleanup_intervalinterval in ms defining how often the server will try to delete idle buckets. It is applied on completion of a cleanup. Default is 1 hour.:max_idle_periodmax period in ms since last bucket update before it is deleted by the server. Default is 24 hours.persistentif true, bucket references will be cached in:persistent_term. Default is false.
Generated API
@macrocallback request( bucket_id :: any(), sub_buckets :: %{ required(name :: atom()) => {request_interval :: pos_integer(), burst :: pos_integer()} }, cost_factor :: integer(), opts :: keyword() ) :: {AtomicBucket.verdict(), :atomics.atomics_ref()}
Checks if the request is allowed according to multiple rate limits.
Uses simplified algorithm, where each rate is represented as request interval in milliseconds instead of window and requests. The bucket is updated in a single atomic operation. By default fixed request cost is assumed, but variable cost is also supported via cost factor.
Multiple buckets are initialized in full state. Every request will
refill each bucket if needed and check if all buckets have enough
tokens to make the request. If true, the request tokens are
removed from each bucket and the call returns {:allow, bucket_ref}.
Otherwise, each bucket is left untouched and the call returns
{:deny, bucket_ref}. bucket_ref is a reference to the bucket
atomic.
There must be no duplicate intervals inside the sub-buckets, and lower rate buckets must have bigger bursts (otherwise they kick in too soon).
Arguments:
bucket_idany id unique within the bucket tablesub_bucketsa map describing sub-buckets, with sub-bucket names as keys and{request interval in milliseconds, burst requests}tuples as valuescost_factorinteger multiplier for the request cost
Options:
refbucket atomic reference. If provided, the call will try to use it instead of refetching.
@macrocallback request_details( bucket_id :: any(), sub_buckets :: %{ required(name :: atom()) => {request_interval :: pos_integer(), burst :: pos_integer()} }, cost_factor :: integer(), opts :: keyword() ) :: {:allow, %{required(name :: any()) => non_neg_integer()}, :atomics.atomics_ref()} | {:deny, %{required(name :: any()) => {AtomicBucket.verdict(), non_neg_integer()}}, :atomics.atomics_ref()}
Checks if the request is allowed according to multiple rate limits and returns results of each bucket check.
Unless you really need the results, consider using request/4
instead, which skips unnecessary calculations.
Multiple buckets are initialized in full state. Every request will
refill each bucket if needed and check if all buckets have enough
tokens to make the request.
If true, the call returns {:allow, requests, bucket_ref},
where requests is a map with remaining requests of each sub-bucket.
Otherwise, each bucket is left untouched and the call returns
{:deny, results, bucket_ref}, where results is a map with sub-bucket
name keys and result tuples ({:allow, remaining requests} or
{:deny, timeout}) as values.
Note that remaining requests in the result tuple reflect final number of available requests in the sub-bucket after the call.
There must be no duplicate intervals inside the sub-buckets, and lower rate buckets must have bigger bursts (otherwise they kick in too soon).
Arguments:
bucket_idany id unique within the bucket tablesub_bucketsa map describing sub-buckets, with sub-bucket names as keys and{request interval in milliseconds, burst requests}tuples as values.cost_factorinteger multiplier for the request cost
Options:
refbucket atomic reference. If provided, the call will try to use it instead of refetching.
@callback start_link() :: GenServer.on_start()
Starts AtomicBucket server managing buckets for the limiter.
Normally users don't need to call this function directly - instead the implementing module can be added to a supervision tree and the server is then started by a supervisor.