diff --git a/.changeset/ipubsub-docblock-staleness-retire.md b/.changeset/ipubsub-docblock-staleness-retire.md new file mode 100644 index 0000000000..7678d9bc05 --- /dev/null +++ b/.changeset/ipubsub-docblock-staleness-retire.md @@ -0,0 +1,33 @@ +--- +"@objectstack/core": patch +"@objectstack/service-cluster": patch +--- + +docs(core,service-cluster): retire the two docblocks left stale by `IPubSub`'s corrected delivery guarantee (#12836) + +#12651 corrected `IPubSub`'s contract docblock: delivery is whatever the +configured driver declares, no shipped driver exceeds at-most-once, a missed +message is EXPECTED, and handlers must be idempotent **and** tolerate loss. +Two docblocks elsewhere still described the world before that correction. + +**`@objectstack/core` — `security/authz-invalidation-channel.ts`.** It carried a +paragraph asserting, in the present tense, that the interface docblock "still +says" *At-least-once delivery*, and that repairing it was a `packages/spec` +change filed separately. That filing was #12651 and it has landed, so the +paragraph is now false rather than merely stale — it sends the next reader +looking for a live disagreement between the interface and the drivers that no +longer exists. Replaced with a plain pointer to the interface docblock. +Everything else in that docblock is unchanged: the at-most-once reasoning, the +TTL-is-the-bound rule, and the best-effort-at-the-publish-site note all still +hold. + +**`@objectstack/service-cluster` — `memory/pubsub.ts`.** The line "At-least-once +semantics held vacuously (a single in-process delivery)" was wrong on its own +terms even before #12651: the same docblock states that handler errors are +swallowed and logged via `onError`, so a handler that throws loses the message +with no retry and no persistence. That is not at-least-once in any sense, and +"vacuously" does not save it. Replaced with the honest statement — one +synchronous in-process delivery attempt per subscriber, no persistence, no +retry, no replay. + +Prose only. No behaviour change, and no test changed. diff --git a/packages/core/src/security/authz-invalidation-channel.ts b/packages/core/src/security/authz-invalidation-channel.ts index 95dd09f9cc..c42faf0fb4 100644 --- a/packages/core/src/security/authz-invalidation-channel.ts +++ b/packages/core/src/security/authz-invalidation-channel.ts @@ -42,13 +42,12 @@ * write that triggered it. A grant revocation must not fail because a cache * hint could not be delivered — the TTL already covers exactly that case. * - * ⚠️ Known contradiction in the surrounding docs, recorded so nobody resolves it - * the wrong way: `IPubSub`'s own interface docblock - * (`@objectstack/spec/contracts`) still says *"At-least-once delivery"*, which - * no shipped driver provides. `cluster.mdx` §4.2 and the redis driver are the - * measured statements and are the ones this module follows. Repairing that - * docblock is a `packages/spec` change and is filed separately, deliberately - * not made here. + * `IPubSub`'s own interface docblock (`@objectstack/spec/contracts`) states the + * same thing from the contract side — delivery is whatever the configured + * driver declares, no shipped driver exceeds at-most-once, and handlers must be + * idempotent **and** tolerate loss. That docblock, `cluster.mdx` §4.2 and the + * redis driver agree; there is no disagreement here for a later reader to go + * looking for. * * ## Why a new channel on the existing bus, and not a new transport * diff --git a/packages/services/service-cluster/src/memory/pubsub.ts b/packages/services/service-cluster/src/memory/pubsub.ts index faeee11ca0..2449ca6bad 100644 --- a/packages/services/service-cluster/src/memory/pubsub.ts +++ b/packages/services/service-cluster/src/memory/pubsub.ts @@ -15,7 +15,13 @@ import type { * - Synchronous fan-out: every subscriber's handler is invoked in the * same tick that `publish()` resolves. Handler errors are swallowed * and logged via `onError` (so one bad subscriber can't poison the bus). - * - At-least-once semantics held vacuously (a single in-process delivery). + * - Delivery is at-most-once: one synchronous in-process attempt per + * subscriber, with no persistence, no retry and no replay. A handler that + * throws loses that message outright — the error is swallowed above and + * nothing redelivers it — and a publish to a channel nobody is subscribed + * to at that moment is a silent no-op. This matches what `IPubSub` + * documents: no shipped driver exceeds at-most-once, so handlers must be + * idempotent **and** tolerate loss. * - No cross-process delivery — use the redis/postgres/nats driver for * real multi-node setups. */