/
foretoken.ex
42 lines (34 loc) · 1.8 KB
/
foretoken.ex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
use Croma
defmodule Foretoken do
readme_contents = File.read!(Path.join([__DIR__, "..", "README.md"])) |> String.replace_prefix("# #{inspect(__MODULE__)}\n\n", "")
@moduledoc """
#{readme_contents}
---
See also `Foretoken.Config`.
"""
@type reason :: {:not_enough_token, milliseconds_to_wait :: pos_integer}
| :nonexisting_bucket
@doc """
Tries to take the specified number of token(s) from the bucket identified by `bucket`.
If the specified bucket does not exist, it's created on-demand filled with `max_tokens` tokens.
If the bucket exists, current number of tokens is computed (using `milliseconds_per_token` and `max_tokens`)
and, if available, `tokens_to_take` tokens are removed from the bucket.
When there are no enough tokens in the bucket this function returns `{:error, milliseconds_to_wait}`,
where `milliseconds_to_wait` is the duration after which the requested tokens become available.
Note that waiting for `milliseconds_to_wait` doesn't guarantee success at the next `take/5`;
another concurrent process may precede the current process.
Although you can use basically arbitrary term for `bucket` argument,
atoms with `$` and integers (such as `:"$1"`, `:"$2"`, ...) are not usable.
"""
defun take(bucket :: any,
milliseconds_per_token :: g[pos_integer],
max_tokens :: g[pos_integer],
tokens_to_take :: g[pos_integer] \\ 1,
create_nonexisting_bucket? :: g[boolean] \\ true) :: :ok | {:error, reason} do
if tokens_to_take <= max_tokens do
Foretoken.Ets.take(bucket, tokens_to_take, milliseconds_per_token, max_tokens, create_nonexisting_bucket?)
else
raise ArgumentError, "`tokens_to_take` cannot exceed `max_tokens`"
end
end
end