View Source speed_trap (speed_trap v1.1.0)
speed_trap a simple yet effective rate_limiter for Erlang
speed_trap uses the token bucket algorithm and implements this algorithm by making use of atomics to represent a bucket.
Granularity of buckets is provided by allowing new buckets (see: speed_trap:new/2) to be created using any arbitrary speed_trap:id().
An example of such id could be: {<<localhost:8080/path/to/resource">>, <<"POST">>} with a generic id being <<"POST">>
Generic buckets can subsequently be setup (see: speed_trap:new/2) using a more generic speed_trap:id() such as just <<"username">>.
Each time a request is made, one simpy checks if allowed by using speed_trap:try_pass/1.
speed_trap:modify/2 in order to adjust the limits.
Summary
Functions
Deletes a token bucket as well as a possibly stored template based token bucket override and hence removes a rate limiter.
Deletes a token bucket by id. If the second argument is
true also deletes a possibly stored template based token bucket override.Deletes a dynamic rate limiter.
Deletes a template based token bucket override by id.
Modify an existing TokenBucket and decrease/increase its size and/or refill interval. Modifying a TokenBucket always fully refills it.
Setup a new TokenBucket. This is where a rate_limiter is setup for any arbitrary identifier.
Creates a new dynamic rate limiter that automatically adjusts bucket_size based on rejection rates. The bucket starts at min_bucket_size and adjusts up to max_bucket_size based on the rejection_rate_threshold.
Try grabbing a token from a TokenBucket. As long as this function is ok, the request is not rate limited.
If any of the ids, do not pass then there's a small chance that we end up with a RefillCount + 1 token if the timer has fired in between the timer calling speed_trap_token_bucket:add_token/4 and us returning the token that we have just grabbed for the speed_traps which we successfully managed to take a token for. This way, you will end up with RefillCount + 1 token
Types
-type already_exists() :: already_exists.
-type blocked() :: blocked.
-type bucket_size() :: non_neg_integer().
-type id() :: term().
-type modify_options() :: #{bucket_size => bucket_size(), refill_interval => refill_interval(), refill_count => refill_count(), delete_when_full => boolean(), override => override(), dynamic_rate_limiter => boolean(), max_bucket_size => bucket_size(), min_bucket_size => bucket_size(), scaling_time_interval => scaling_interval(), scaling_bucket_size_adjust_count => scaling_bucket_size_adjust_count(), rejection_rate_threshold => rejection_rate_threshold(), any() => any()}.
-type no_such_speed_trap() :: no_such_speed_trap.
-type options() :: #{bucket_size := bucket_size(), refill_interval := refill_interval(), refill_count := refill_count(), delete_when_full := boolean(), override := override(), template_id => speed_trap_template:id(), dynamic_rate_limiter => boolean(), max_bucket_size => bucket_size(), min_bucket_size => bucket_size(), scaling_time_interval => scaling_interval(), scaling_bucket_size_adjust_count => scaling_bucket_size_adjust_count(), rejection_rate_threshold => rejection_rate_threshold(), any() => any()}.
-type override() :: none | not_enforced | blocked.
-type rate_limit_not_enforced() :: rate_limit_not_enforced.
-type refill_count() :: pos_integer().
-type refill_interval() :: pos_integer().
-type rejection_rate_threshold() :: pos_integer().
-type scaling_bucket_size_adjust_count() :: pos_integer().
-type scaling_interval() :: pos_integer().
-type stored_options() :: #{bucket_size := bucket_size(), refill_interval := refill_interval(), refill_count := refill_count(), delete_when_full := boolean(), override := override(), template_id => speed_trap_template:id(), dynamic_rate_limiter => boolean(), max_bucket_size => bucket_size(), min_bucket_size => bucket_size(), scaling_time_interval => scaling_interval(), scaling_bucket_size_adjust_count => scaling_bucket_size_adjust_count(), rejection_rate_threshold => rejection_rate_threshold(), any() => any()}.
-type too_many_requests() :: too_many_requests.
-type try_pass_all_result() :: ok | {error, id(), try_pass_failure()}.
-type try_pass_failure() :: blocked() | no_such_speed_trap() | too_many_requests() | speed_trap_options:bad_options().
-type try_pass_result() :: {ok, try_pass_success()} | {error, try_pass_failure()}.
-type try_pass_success() :: non_neg_integer() | rate_limit_not_enforced().
Functions
-spec all() -> [{id(), stored_options()}].
-spec block(id()) -> ok | {error, speed_trap_options:bad_options() | no_such_speed_trap()}.
-spec delete(id()) -> ok | {error, no_such_speed_trap()}.
-spec delete(id(), boolean()) -> ok | {error, no_such_speed_trap()}.
true also deletes a possibly stored template based token bucket override.
-spec delete_dynamic(id()) -> ok | {error, no_such_speed_trap()}.
-spec delete_override(id()) -> ok.
-spec modify(id(), modify_options()) -> ok | {error, no_such_speed_trap() | speed_trap_options:bad_options()}.
-spec new(id(), options()) -> ok | {error, already_exists() | speed_trap_options:bad_options()}.
-spec new_dynamic(id(), options()) -> ok | {error, already_exists() | speed_trap_options:bad_options()}.
-spec options(id()) -> {ok, stored_options()} | {error, no_such_speed_trap()}.
-spec try_pass(id()) -> try_pass_result().
-spec try_pass_all([id()]) -> try_pass_all_result().
-spec unblock(id()) -> ok | {error, no_such_speed_trap()}.