mirror of
https://github.com/HeyPuter/puter.git
synced 2026-09-23 13:46:11 +00:00
feat: pending event deliveries and delivery-class invariants (PUT-1676) (#3680)
* feat: pending event deliveries and delivery-class invariants (PUT-1676)
* fix: make pending delivery claims and drains atomic, keep the region under its ceiling (PUT-1676)
- claim() and the drain-time reindex run as Lua over the subscription's own
{subId}-tagged keys. Two claimers can no longer both lease the head, and a
drain that finds the queue empty deletes it in the same step it checks, so a
concurrent enqueue is never wiped between the two.
- An append writes the entry and its queue position in one MULTI (same slot),
with the index seeded before it and corrected after, so an entry is never
visible without its position and never left out of the sweeper's index.
- Pipelines no longer mix slots (index/counter vs. per-subscription keys), so
the store works on a multi-shard cluster, not only a single-shard one.
- Region shedding counts the marker it leaves behind; it used to stop one over
the ceiling and convert a real event into a marker on every enqueue after.
- A claimed or suspended subscription moves to the back of the sweeper's index,
so a delivery nobody settles cannot hold the head against every other backlog.
- `single` rows must carry a `worker` target: with sockets exhausted and no
handler, an unacknowledged delivery would sit at the head forever.
- A gap marker for a row with no socket target is dropped rather than counted
as a delivery of nothing.
- Backlog keys carry a 7-day TTL, refreshed by every claim, as a backstop for
keys a purge/enqueue race left unindexed.
This commit is contained in:
@@ -168,6 +168,8 @@ One write can reach many subscriptions, so events are bounded on both halves: ho
|
||||
| Matched subscriptions per event | 50 |
|
||||
| Filter evaluations per event | 200 |
|
||||
| Deliveries per minute, per subscription | 600 |
|
||||
| Acknowledgements per minute | 600 |
|
||||
| Undelivered deliveries per subscription | 10,000 |
|
||||
|
||||
Subscriptions come in two kinds. A **session** subscription lives with the connection that made it: it is dropped when the connection closes, and a reconnecting client subscribes again. A **durable** subscription outlives every connection — it is created over the API, listed and revoked from the account, and keeps delivering until you remove it or it expires.
|
||||
|
||||
@@ -181,6 +183,8 @@ Match patterns are compiled once when you subscribe and are capped at **256 char
|
||||
|
||||
The three per-event ceilings do not fail your call — they truncate the delivery and send a `gap` marker in its place, an event with `op: 'gap'` and no `uid` or `path`. A gap means something happened that you were not told the details of, so a client that must not miss changes should re-read the anchor when it sees one rather than treat the silence as "nothing changed".
|
||||
|
||||
A `single` subscription is delivered to exactly one consumer, which has **30 seconds** to acknowledge each delivery before it is offered again — twice to a connected client, then to the subscription's handler. Until it is acknowledged it is held for you, so a consumer that is away is a backlog that grows: **10,000** undelivered deliveries per subscription, after which the oldest are dropped and one `gap` marker with `reason: 'backlog_overflow'` takes their place. Each region also holds at most **1,000,000** undelivered deliveries across every subscription it serves, and sheds the oldest first — with the same marker — before it reaches that. A redelivery after a missed acknowledgement is normal and expected: deliveries are at-least-once, `event.id` is stable across them, and a handler that runs twice on the same id should do nothing the second time.
|
||||
|
||||
### Peer connections
|
||||
|
||||
| Limit | Paid | Free | Anonymous |
|
||||
|
||||
Reference in New Issue
Block a user