A throttle's token bucket as a value: a rate per minute, a burst, the tokens it holds and the instant it last counted them.
Every function that needs the time takes it as an argument — a
System.monotonic_time(:millisecond) instant, which YmerNode.Scripts.Throttle
passes — so the arithmetic a throttle's waits and refusals rest on is proven
here without a clock. There is no tick: a bucket is refilled from the elapsed
time whenever it is read, so an idle throttle costs nothing between requests,
and its tokens are a float, because a rate per minute refills by fractions.
Summary
Functions
Whether the bucket holds a whole token.
The bucket at now under new parameters: refilled at the rate it had until
now, then held to the new burst. A throttle's parameters arrive with every
request, so this is how a changed declaration takes effect without losing what
the old one had counted.
A full bucket at now.
The bucket at now: what the elapsed time refilled, never more than the burst.
An instant earlier than the last one counts no time at all.
The bucket with one token spent — only ever a bucket available?/1 answers
true for.
How long, in milliseconds, until the bucket holds a token for a request with
ahead requests waiting in front of it — 0 when it holds one now.
Types
@type t() :: %YmerNode.Scripts.Throttle.Bucket{ at: integer(), burst: pos_integer(), rate: pos_integer(), tokens: float() }
Functions
Whether the bucket holds a whole token.
The bucket at now under new parameters: refilled at the rate it had until
now, then held to the new burst. A throttle's parameters arrive with every
request, so this is how a changed declaration takes effect without losing what
the old one had counted.
Examples
iex> bucket = %YmerNode.Scripts.Throttle.Bucket{rate: 60, burst: 2, tokens: 2.0, at: 0}
iex> YmerNode.Scripts.Throttle.Bucket.configure(bucket, 30, 1, 0)
%YmerNode.Scripts.Throttle.Bucket{rate: 30, burst: 1, tokens: 1.0, at: 0}
A full bucket at now.
Examples
iex> YmerNode.Scripts.Throttle.Bucket.new(60, 2, 0)
%YmerNode.Scripts.Throttle.Bucket{rate: 60, burst: 2, tokens: 2.0, at: 0}
The bucket at now: what the elapsed time refilled, never more than the burst.
An instant earlier than the last one counts no time at all.
Examples
iex> bucket = %YmerNode.Scripts.Throttle.Bucket{rate: 60, burst: 2, tokens: 0.0, at: 0}
iex> YmerNode.Scripts.Throttle.Bucket.refill(bucket, 1_500).tokens
1.5
iex> YmerNode.Scripts.Throttle.Bucket.refill(bucket, 10_000).tokens
2.0
The bucket with one token spent — only ever a bucket available?/1 answers
true for.
Examples
iex> bucket = %YmerNode.Scripts.Throttle.Bucket{rate: 60, burst: 2, tokens: 1.5, at: 0}
iex> YmerNode.Scripts.Throttle.Bucket.take(bucket).tokens
0.5
How long, in milliseconds, until the bucket holds a token for a request with
ahead requests waiting in front of it — 0 when it holds one now.
An estimate, and a conservative one: every request ahead is counted as taking its token and none as leaving. Rounded up, so a wait it names is never short.
Examples
iex> bucket = %YmerNode.Scripts.Throttle.Bucket{rate: 60, burst: 2, tokens: 1.5, at: 0}
iex> YmerNode.Scripts.Throttle.Bucket.wait(bucket, 0)
0
iex> YmerNode.Scripts.Throttle.Bucket.wait(bucket, 1)
500
iex> YmerNode.Scripts.Throttle.Bucket.wait(bucket, 100)
99500