A tier upgrade used to keep the old bucket's tokens, capped at the new\nburst, so a bigger tier did not add its difference. The bucket now shifts\nby the burst difference both ways, down to a debt of one burst.\n\nemails_remaining is now capped by what the email bucket holds, so it no\nlonger shows emails the visitor cannot send (e.g. after the daily reset).\n\nClaude-Session: https://claude.ai/code/session_01DeXuhSjq5Gta5qPJgSceFe
The limit service reads a zero cap as no cap, so a tier with no calls
(or no messages, or no attachment bandwidth on upload) became unlimited
where main's FixedLimiter(0) and bytes limiter admitted nothing. The
visitor now answers zero limits itself before asking the service, like
it already did for requests, emails and bandwidth downloads.
Allow(key, rule, n), Refund, Tokens and Usage on a service that keeps a
token bucket and a daily count per key and kind, sharded by key, with the
rule passed in on every call. The server's visitor keeps its identity and
the resolved limits and asks the service; rate.Limiter, util.RateLimiter
and util.FixedLimiter are gone from it (subscriptions stay a per-node
gauge). util.RateLimiter, NewBytesLimiter and BurnTokens had no users
left and are removed.
Behavior changes: refunds work for every kind (a revert on the bandwidth
limiter hands the bytes back), a tier change applies on the next request
without handing back full buckets, a changed tier limit applies without
a tier ID change, and the daily email limit is a real daily cap on top
of the bucket. Step 2 of plans/261025-shared-rate-limits.md.
Since #1989 the stats queue holds increments, so a batch whose write
failed was lost for good: nothing writes those increments again. It is
now put back and retried with the next flush, unless the daily reset ran
in between.
When a user with a tier took over an IP-keyed visitor, the visitor
handed over its counts after switching to the new user, so anonymous
usage on that IP was added to the account. Counts are now handed over
under the old account, and an IP-keyed visitor's counts are not.
UPDATE ... SET stats_messages = $1 lets two ntfy processes that share one
database overwrite each other's counts for the same user: whoever flushed
last won, and the other's publishes were never counted. The queue now
accumulates deltas and flushes SQL increments, and visitors track what they
already handed to the queue and enqueue only the difference. Nothing changes
on a single node; this is groundwork for running more than one.