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
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.
Every app server that drops a subscription on a 404 also drops it on a 410, and Misskey and its forks only do on a 410. 410 is also what Mozilla's and Apple's push services answer for an expired subscription.
Half the memory per node; an abandoned UnifiedPush topic still has its row when a push 7+ days later decides on the 404, as long as it is pushed to at least every two weeks.
One row per topic (first_seen_at, subscribed_at, rate visitor user and IP,
updated_at), merged by rule (earliest, latest, last written) so the nodes can
write in any order, synced by the package's own 5 s worker from a dirty set.
User schema 10 -> 11 now creates this table.