Ralitobu
The Rate Limiter with Token Bucket algorithm
This is a fork of ExRated, but with some changes:
- the checkout and inspection always return the same result tuple
- asynchronous counterparts for checkout, inspect and delete
- configuration can be given on initialization and not only via application environment (useful when starting multiple rate limiters with different settings)
Installation
In mix.exs
:
def deps do
[{:ralitobu, "~> 0.1.0"}]
end
def application do
[applications: [:ralitobu]]
end
Usage
Checkouts
Ralitobu.checkout/3
(Ralitobu.checkout(id, limit, lifetime)
using default server)Ralitobu.checkout/4
(Ralitobu.checkout(server, id, limit, lifetime)
)
The result tuple format:
{success, counter, remaining_limit, total_limit, countdown, created_at, updated_at}
# 1st call:
Ralitobu.checkout("my-bucket", 3, 10_000)
#=> {:ok, 1, 2, 3, 7806, 1461432862194, 1461432862194}
# 3rd call:
Ralitobu.checkout("my-bucket", 3, 10_000)
#=> {:ok, 3, 0, 3, 7799, 1461432862194, 1461432862201}
# 4th call fails (over rate limit):
Ralitobu.checkout("my-bucket", 3, 10_000)
#=> {:error, 3, 0, 3, 7795, 1461432862194, 1461432862200}
Inspection
Ralitobu.inspect/3
(Ralitobu.inspect(id, limit, lifetime)
using default server)Ralitobu.inspect/4
(Ralitobu.inspect(server, id, limit, lifetime)
)
The result tuple format:
{success, counter, remaining_limit, total_limit, countdown, created_at, updated_at}
# multiple calls do not increment the counter:
Ralitobu.inspect("my-bucket", 3, 10_000)
#=> {:ok, 2, 3, 3, 4132, 1461432862194, 1461432862200}
Ralitobu.inspect("my-bucket", 3, 10_000)
#=> {:ok, 2, 3, 3, 4130, 1461432862194, 1461432862200}
# bucket does not exists:
Ralitobu.inspect("my-other-bucket", 3, 10_000)
#=> {:ok, 0, 3, 3, 9142, nil, nil}
Deletion
Ralitobu.delete/1
(Ralitobu.delete(id)
using default server)Ralitobu.delete/2
(Ralitobu.delete(server, id)
)
Result is either :ok
or :error
, depending if the bucket existed or not.
# bucket (still) exists:
Ralitobu.delete("my-bucket")
#=> :ok
# bucket is not present (anymore):
Ralitobu.delete("my-bucket")
#=> :error
New server instance
The application always starts a default server, but you can run your own instance(s) as well.
Ralitobu.start_server/2
(Ralitobu.start_server(configuration, gen_server_opts)
)
{:ok, server} = Ralitobu.start_server(
[table_name: :another_ralitobu_table, timeout: 30_000_000, cleanup_rate: 15_000],
[name: :another_ralitobu_server]
)
You must provide the name
parameter for the GenServer options,
otherwise it will take the default name and therefore not starting a new server.
Also the table_name
must be different, too.
Server configuration
table_name
(atom): The name of the ETS tabletimeout
(integer): milliseconds; buckets older than (now - timeout) will be cleaned up (based on last update timestamp)cleanup_rate
(integer): milliseconds; the interval for the clean up task