View Source speed_trap_token_bucket (speed_trap v1.1.1)

speed_trap_token_bucket implementation with atomics.

The current number of tokens is stored as a mutable atomic variable. When a token is taken from the bucket, the counter is decremented. It is allowed for the counter to become negative.

Adding tokens to the counter requires multiple operations. Since there is no operation to add with a threshold (contrary to ets:update_counter/3 for example), the value is first read and then incremented if necessary. It is possible that the value will be decremented between the two operations, but it cannot be incremented (given there's only one updater running at a time), so there's no risk of adding tokens above the bucket's capacity. On the other hand, the counter going below 0 is an issue that needs special handling: whenever the update operation finds a negative value, it sets the counter to 1. Incrementing it is not safe in this case, as concurrent processes may try to get tokens from the bucket, pushing its value to even lower between the read and the update.

It's worth mentioning that although counters can underflow, it is not a practical risk to call get_token/1 2^63 times in a single refill interval.

For simplicity, the actual updates are scheduled via timer:apply_interval/4, however an interval timer is linked to the process creating it. Therefore we need a simple server to own and manage these timers.

Summary

Functions

Deletes a scheduled function by its id. The second parameter indicates whether to try deleting a store template based token bucket override by id or not.
Deletes a possibly stored token bucket override from the store.
Cleans up the overrides store.
Gets an override from the overrides store.
Modifies the scheduled function already registered under the given id.
Add a new function to the scheduler. The provided MFA will be registered under the given id and applied every refill interval milliseconds.
Start the scheduler server.

Types

-type state() :: #{speed_trap:id() => timer:tref()}.
-type token_bucket() :: atomics:atomics_ref().

Functions

-spec active_buckets() -> [{speed_trap:id(), speed_trap:stored_options()}].
Link to this function

add_token(Bucket, BucketSize, RefillCount, DeleteWhenFull)

View Source
Link to this function

delete(Id, DeleteOverride)

View Source
-spec delete(speed_trap:id(), boolean()) -> ok | {error, speed_trap:no_such_speed_trap()}.
Deletes a scheduled function by its id. The second parameter indicates whether to try deleting a store template based token bucket override by id or not.
-spec delete_override(speed_trap:id()) -> ok.
Deletes a possibly stored token bucket override from the store.
-spec delete_overrides() -> ok.
Cleans up the overrides store.
-spec get_override(speed_trap:id()) -> {ok, speed_trap:modify_options()} | {error, not_found}.
Gets an override from the overrides store.
Link to this function

handle_call(Request, From, Timers)

View Source
-spec handle_call(term(), {pid(), term()}, state()) -> {reply, ok | {error, term()}, state()}.
Link to this function

handle_cast(Request, Timers)

View Source
-spec handle_cast(any(), state()) -> {noreply, state()}.
Link to this function

handle_info(Request, Timers)

View Source
-spec handle_info(any(), state()) -> {noreply, state()}.
-spec init([]) -> {ok, state()}.
Modifies the scheduled function already registered under the given id.
-spec new(speed_trap:id(), speed_trap:options()) -> ok | {error, speed_trap:already_exists()}.
Add a new function to the scheduler. The provided MFA will be registered under the given id and applied every refill interval milliseconds.
-spec options(speed_trap:id()) ->
                 {ok, speed_trap:stored_options()} | {error, speed_trap:no_such_speed_trap()}.
-spec return_token(speed_trap:id()) -> ok | {error, speed_trap:no_such_speed_trap()}.
-spec start_link() -> {ok, pid()} | {error, term()}.
Start the scheduler server.