Skip to content

fix(relay): read a channel's creation once, not per metadata version - #3244

Merged
bradseiler merged 5 commits into
mainfrom
elrond/creation-read-once-per-channel
Nov 11, 2026
Merged

bradseiler merged 5 commits into
mainfrom
elrond/creation-read-once-per-channel

Conversation

@TheSentinel454

@TheSentinel454 TheSentinel454 commented Nov 10, 2026 •

Copy link
Copy Markdown
Contributor

Problem

Each membership change makes the relay republish the channel's kind:39000 metadata, re-signed and so with a new event id, to every online member. Members get it because every joined channel sits in a batched live route over CHANNEL_KINDS.

On each 39000, applyDiscovery calls readCreations. That issues a background kind:9007 read for the channel's creator proof (huddle detection). The read was deduplicated by metadata version, so every republish missed the dedupe. Each single-person add made every online member send another /query.

Cost per run of adds: members online × adds. A bulk run of single-person adds to large channels turned this into a relay-wide burst of 9007 reads.

Change

readCreations now deduplicates background reads per channel instead of per version. A channel's creator (P) and creation receipt never change.

  • A read that finds the receipt is final.
  • A read that finds none, or fails, leaves a negative entry. A later metadata version retries once its backoff has passed, measured on the monotonic clock (performance.now()). The backoff starts at a minute and doubles to an hour, with jitter up to 1.5×.
  • No miss is treated as final. A replica can lag at any read, and 39000 created_at reflects replacement order, not elapsed time. A Huddle whose receipt was missing therefore still gets classified once the receipt is readable.
  • A cancelled read clears its entry, so the next version reads again right away: it learned nothing, so it doesn't back off. (Before, every error cleared the entry.) A read cut at its filter limit still retries on the next version, unchanged.
  • Strong resolution (consistency: "strong") still bypasses the dedupe. Huddle invites resolve this way.
  • A version that arrives while a read is in flight shares that read.

Background reads per client per membership change go from 1 to 0 once a channel's proof is found. Until then they are bounded by the backoff rather than by the rate of adds, which also covers a relay that is failing these reads (RPR: design every relay-facing path for the whole fleet at once).

Huddle classification

A Huddle is recognized from its creation receipt. The creator (right after creating) and invitees (on the member-added notice) resolve the room with strong consistency, which bypasses this dedupe and backoff, so the main paths are unchanged.

The difference is only on the background path, when its first read misses the receipt (replica lag or a failed read):

  • Before: the next metadata version (the next join or rename) read again, usually seconds later.
  • After: the next read waits until the backoff (60–90s) has passed and another version arrives.

In that window the room shows as an ordinary private channel instead of a Huddle under its parent. Neither version has a timer: with no further version, the room stays unclassified until the next strong resolve or restart, as before.

Rollout

Client-only, no flag: this takes effect as clients upgrade. A relay-side database pool increase is the immediate mitigation for the bursts; this change removes their source.

  • Success signal: the relay's rate of kind:9007 /query reads, and database pool wait time during bulk channel adds, before and after the release.
  • Rollback: revert.

What got simpler

CreationRead replaces its per-version key with a per-channel entry plus a miss count and retryAt. Retries are driven by events, so there are no timers or new lifecycle owners.

Tests

New tests in store.test.ts › creator proof reads drive the live 39000/39002 republish path, with a controlled clock and no jitter.

  • reads a channel's creation once…:

    • a failed read waits out its backoff
    • an in-flight read is shared
    • after a real receipt, later republishes issue no read, even a day later
  • retries a missing creation with backoff until it arrives:

    • an old channel's first sighting misses twice
    • no read at 59.999s; it reads at 60s, then again at 120s once the backoff has doubled
    • the receipt then classifies the room as a Huddle with its parent
  • lets the next version read again after a cancelled creation read: a cancelled read issues a new read on the next version without advancing the clock.

Discrimination: the first two fail on base and on the previous revision's store. The cancelled-read test fails if a cancel backs off like a miss.

🤖 Generated with Claude Code

buzz-review-completed

TheSentinel454 and others added 4 commits November 10, 2026 14:51
Every membership change republishes the channel's kind:39000 to every
online member, with a new event id. The background creator-proof read
was deduplicated by that id, so each add made every online member issue
another kind:9007 query. A run of single-person adds to a large channel
multiplied into a relay-wide query burst.

A channel's creator and creation receipt never change, so a completed
background read is now final for the channel. A failed or limit-cut read
still retries on the next version, and strong resolution still revisits
proof.

Co-authored-by: Elrond <28d6302a099e5225b02c4155ac4236e4912603df2ab08dbfc2f4fef08ce598c8@buzz.block.builderlab.xyz>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Signed-off-by: Luke Tornquist <[email protected]>
…ttle

An empty background answer can be replica lag right after a channel is
created. Keep it negative until a metadata version signed
CREATION_SETTLE_SECONDS after the channel's creation, which reads once
more; a found receipt, or a miss on a settled channel, stays final.

Co-authored-by: Elrond <28d6302a099e5225b02c4155ac4236e4912603df2ab08dbfc2f4fef08ce598c8@buzz.block.builderlab.xyz>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Signed-off-by: Luke Tornquist <[email protected]>
No miss proves a creation absent: a replica can lag at any read, and
39000 created_at is replacement order, not elapsed time. Give every
unproved read a negative entry that a later metadata version retries
after a jittered backoff on the monotonic clock, doubling from a minute
to an hour. A found receipt stays final.

Co-authored-by: Elrond <28d6302a099e5225b02c4155ac4236e4912603df2ab08dbfc2f4fef08ce598c8@buzz.block.builderlab.xyz>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Signed-off-by: Luke Tornquist <[email protected]>
The missing-discovery arm expected a newer live version to re-read the
creation at once; reads now back off after a miss. Advance the monotonic
clock past the longest jittered backoff first. Classify a cancelled read
with the store's isAbort.

Co-authored-by: Elrond <28d6302a099e5225b02c4155ac4236e4912603df2ab08dbfc2f4fef08ce598c8@buzz.block.builderlab.xyz>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Signed-off-by: Luke Tornquist <[email protected]>
@TheSentinel454
TheSentinel454 marked this pull request as ready for review November 11, 2026 20:44
@TheSentinel454
TheSentinel454 requested a review from a team as a code owner November 11, 2026 20:44

@kazemek21 kazemek21 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Kyle reviewed this PR and the Nexus review-tracker notes and approved with no comments.

Posted by Codex (gpt-6.1-sol) on behalf of kylez.

A cancelled read learned nothing, so the next metadata version reads
again right away instead of waiting out the miss backoff.

Co-authored-by: Elrond <28d6302a099e5225b02c4155ac4236e4912603df2ab08dbfc2f4fef08ce598c8@buzz.block.builderlab.xyz>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Signed-off-by: Luke Tornquist <[email protected]>
@bradseiler
bradseiler merged commit 72ed0fe into main Nov 11, 2026
70 checks passed
@bradseiler
bradseiler deleted the elrond/creation-read-once-per-channel branch November 11, 2026 23:41
joahg added a commit to joahg/buzz-app that referenced this pull request Nov 12, 2026
* origin/main: (38 commits)
  Fix reminder counts with compact dock corner tabs (block#3126)
  Add conditional undo for explicit reads (block#3291)
  fix(composer): edit channel links as atomic chips (block#3312)
  fix(agents): show remote instruction limit (block#3325)
  NEXUS-4033: Offer session skills in the core task composer (block#2790)
  feat(github): name pasted repository and commit links (block#3268)
  Let plugins give standing instructions to every agent turn (block#3285)
  fix(relay): read a channel's creation once, not per metadata version (block#3244)
  fix(messages): remove unconfirmed sends from this device (block#3317)
  fix: expose proven non-delivery for artifacts (block#3300)
  feat(plugins): let plugins mark a person's name in @mention suggestions (block#3260)
  fix(mcp): strip metadata from images agents attach (block#3311)
  fix(identity): name a linked key that isn't here, and keep Restore after a failed backup check (block#3238)
  feat(changesets): show the files a local agent changed on its reply (block#3214)
  fix(identity): show a backed-up key as backed up, and drop dead retries (block#3223)
  fix(browser): open Builderlab account connections in the system browser (block#3287)
  Expose exact captured-message reads (block#3264)
  docs(agents): keep internal discussion out of public repo content (block#3258)
  fix(identity): clear a copied private key, name restore failures, fix storage copy (block#3236)
  fix(channels): list workspaces that load after `#` opens (block#3277)
  ...
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants