From d152243d84da96a30fc479c661df10aafe79d8cc Mon Sep 17 00:00:00 2001 From: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com> Date: Thu, 23 Jul 2026 13:59:05 +0400 Subject: [PATCH] wip --- plans/2026-07-15-channel-multi-owner.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plans/2026-07-15-channel-multi-owner.md b/plans/2026-07-15-channel-multi-owner.md index 7ea1d8a6e0..1adddc6253 100644 --- a/plans/2026-07-15-channel-multi-owner.md +++ b/plans/2026-07-15-channel-multi-owner.md @@ -59,7 +59,7 @@ Link data and keys already *have* a sequencer — the server — so they need on **Source — max across relays, with a timestamp.** Each relay batches its own *current absolute* subscriber count and broker timestamp with the join/leave/removal event it already sends owners — no extra SMP block, restated only when the count changes. An owner keeps the latest `(count, ts)` **per relay** (a field on the relay's member row); its estimate is the **max** count, carrying that relay's `ts`. The max is what ignores a newly-added relay that subscribers are still slowly connecting to (its low count stays below the max until it catches up) while still decreasing on a real departure (the max-holding relay's own count drops — a lagging second relay reporting a slightly lower number never lowers the max, so cross-relay skew causes no spurious decrease). Deltas cannot do this: a leave is *deduplicated* across relays, so an owner cannot attribute a decrease; the per-relay count event, being distinct per relay, is not deduplicated. -**Published on channel open, not on events.** Link data carries `(subscriberCount, subscriberCountTs)`. When an owner opens the channel it already reconciles link data (§2); if its estimate differs materially from the published count *and* its `ts` is newer than `subscriberCountTs`, it publishes `(estimate, ts)`. The timestamp is a **freshness gate** — only an owner whose data is newer than what is published writes, so a *behind* owner (stale data, older `ts`) stays quiet and cannot push a wrong value in either direction, which is exactly what lets the update go **both ways** safely. Concurrent opens CAS: one wins, the other re-reads and sees it already fresh. This needs no leading owner, no handover, no staleness detection, no debounce, and no extra query — the count tracks owner activity (fine for a cosmetic number) and writes the link at most once per owner-open, not per join. (Use the relay's *broker* timestamp so owner clock skew is irrelevant; relay-to-relay skew is absorbed by the max.) +**Published on channel open, not on events.** Link data carries `(subscriberCount, subscriberCountTs)`. When an owner opens the channel it already reconciles link data (§2); if its estimate differs from the published count (by any amount) *and* its `ts` is newer than `subscriberCountTs`, it publishes `(estimate, ts)`. No delta threshold — a small/new channel must move on every joiner (1 → 2 → 3). There is no write-amplification to guard against: publishing is gated by *channel open*, not by joins, and the `ts` gate means an owner writes only when it is genuinely fresher — if the published count is already current it writes nothing. The timestamp is thus a **freshness gate** — a *behind* owner (stale data, older `ts`) stays quiet and cannot push a wrong value in either direction, which is what lets the update go **both ways** safely. Concurrent opens CAS: one wins, the other re-reads and sees it already fresh. This needs no leading owner, no handover, no staleness detection, no debounce, and no extra query — the count tracks owner activity (fine for a cosmetic number) and writes the link at most once per owner-open, not per join. (Use the relay's *broker* timestamp so owner clock skew is irrelevant; relay-to-relay skew is absorbed by the max.) ## 6. Owner promotion (adding an owner)