v2.37.0 wrote every topic with an open stream every 5 minutes, and the topics touched together after a restart stayed in step, so ntfy.sh wrote ~45k topic rows in the same minute every 5 minutes (~145 rows/s on average, more than the message inserts), stalling the primary. Each topic now has its own interval between 30 and 60 minutes (by hash), and a subscribe or poll that only repeats the same rate visitor is a touch.
Claude-Session: https://claude.ai/code/session_01DeXuhSjq5Gta5qPJgSceFe
ban-window defaults to 10m and ban-weights to 42909:2, 50701:0.05 and 41002:0.05 (weights may be fractional); database-replica-urls was missing; the cluster note now says daily limits are shared.
A weight like 1e16 overflowed to a negative token count, which the limiter always allows, so the code never banned. Weights above 1,000,000 and thresholds above 1,000,000,000 are now rejected at startup.
Strikes are counted in hundredths, so a weight like 0.05 makes 20 rejections one strike. An app server pushing to many offline phones gets many 507s without being an attacker; auth-failure floods still ban at half the threshold.
The server package's race run takes about as long as all other packages together, so it gets its own job. Coverage was only printed to the log, and its instrumentation slowed the race run.
make checkv ran every Go test without -race, then make coverage ran them all again with -race and coverage. The check step now runs only the web tests, formatting, vetting and linting.
Earlier cmd tests leave servers running whose sync workers keep logging into the global log output; the test read a plain bytes.Buffer while they wrote to it.
Nothing outside the limit package read it: enforcement is the bandwidth
token bucket, and stats only report messages, emails and calls. Without
the tally, a key is forgotten once all of its buckets are full again and
it was idle for settledIdle, like a key that used no bandwidth. Usage now
holds only the daily counts.
The manager touches the topics of open streams every minute; subscribes, polls and closing streams use the same throttle, which shifts subscriber activity by at most 5 minutes.
A refused push's Note returns the topic's state for the publish path, prune forgets unwritten
changes along with the topic, and SetForTest is gone: tests age rows in the database.
Drops the background refresh, its cache and Manager.UsersByIDs: one primary key read per UnifiedPush push to a topic whose subscriber has an account (none for anonymous subscribers), and a tier change applies to the next message.