Per-account rate gate for Shelly Cloud traffic. The cloud allows ~1 request/second/account and returns 429 for anything faster — and polling, control commands and one-off calls all draw from the same budget, so every call should flow through one gate.
Callers reserve the next free slot for their key and sleep in their
own process until it arrives; the gate itself never blocks. When the
backlog for a key exceeds one minute, callers are rejected with
{:error, :throttled} instead of being queued into a burst:
Shelly.RateGate.run({server, auth_key}, fn -> Req.request(...) end)Start it in your supervision tree:
children = [Shelly.RateGate, ...]When the gate isn't running (scripts, tests), calls pass straight through unpaced.
Summary
Functions
Returns a specification to start this module under a supervisor.
Run fun in the calling process, no earlier than the key's next free
slot. key is any term identifying the budget (typically
{server, auth_key} or an account id).
Functions
Returns a specification to start this module under a supervisor.
See Supervisor.
@spec run(term(), (-> result), GenServer.server()) :: result | {:error, :throttled} when result: var
Run fun in the calling process, no earlier than the key's next free
slot. key is any term identifying the budget (typically
{server, auth_key} or an account id).
Returns fun's result, or {:error, :throttled} when the key's
backlog already exceeds one minute.