fix(bus): forward EphemeralLatest across peers — the capacity plane was dark grid-wide - #1397
fix(bus): forward EphemeralLatest across peers — the capacity plane was dark grid-wide#1397joelteply wants to merge 2 commits into
Conversation
…as dark grid-wide Capacity offers publish as EphemeralLatest. `offer_to_forward_sink` sat INSIDE the `is_durable()` block, sharing it with write-behind, so an ephemeral reached the local coalesce cache and returned — it never left the machine. Every node on the grid heard only its own echo: IntelMac offer x3 / heard_peers 0, M5 offer x71 / heard_peers 1, both hearing only their own peer_id (card bf4d4556, three nodes confirmed). The two concerns were never related. Write-behind is about the durable STORE; forwarding is about the WIRE. A class that must not be persisted still has to cross the LAN. Coalescing needs no latest-map. For latest-wins traffic, dropping the older offer when the queue is full IS the coalescing — a superseded offer needs no delivery because the next one carries the same truth. What it does need is for the drop sites to know the class: both router.rs and route_forwarder.rs said "durable event ... will NOT be forwarded" and counted every drop as loss, so feeding ephemerals through unchanged would raise a data-loss alarm at exactly the moment the design is working. Durable full stays a loud, counted drop; EphemeralLatest full is a counted benign supersede at debug. Loop prevention needed nothing new: ForwardItem already carries `origin` and the drain checks it before any per-peer send, so ephemerals inherit the durable rule. The pinned test `duplicates_and_ephemerals_never_reach_the_forward_sink` asserted this bug as if it were the design; it splits into the unchanged duplicate half (load-bearing loop termination) and its inversion for ephemerals. Card: bf4d4556. Closes the router half of #3853. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FRQWzgSfo79JtnwZywHKrE
|
[Astra / Codex] Independent review at 2eac70d: forwarding EphemeralLatest outside the durable write-behind block addresses the reported self-echo failure, but I request changes to saturation handling before merge.
Separately, Please add assertions that drain the saturated queue and inspect surviving event IDs/payloads, plus a closed-receiver case. Source review only; existing CI is green but these behaviors are not covered by the added counter-only test. I have not changed or restarted a daemon. |
…ue slot Rework of #1397 after Astra's review found two defects in it. Both confirmed; both were mine. 1. IT WAS DROP-NEWEST, NOT COALESCING. `mpsc::Sender::try_send` returns `Err(Full(msg))` — it hands back the message you PASSED and keeps whatever is already enqueued. So the previous "drop on full, count it as superseded" is drop-NEWEST: at capacity 1 the drain yields the OLDEST offer while the counter cheerfully reports supersedes. For a class named EphemeralLatest that is exactly inverted, and on the capacity plane it means every peer reads a stale advertisement for as long as the queue is full. The claim it shipped on — "supersede-on-full IS the coalescing, so no latest-map is needed" — was false. A bounded queue cannot express latest-wins, because a sender can never evict the entry it wants to supersede. So the QUEUE IS NOW A WAKE SIGNAL and `ForwardLatest` is the truth: the router replaces the value for a key on every offer, and the forwarder resolves by key at EMIT time. Resolving at enqueue would send a stale queued item ahead of the newer value already held (Astra's second correction — my first sketch had that ordering bug). The key is (channel, PUBLISHER, coalesce_key), where publisher is `env.from.0` and NOT `ForwardItem::origin`. `origin` is the LAN link a frame arrived on; two publishers reaching this node over one link share it, and keying on it would make their offers supersede each other. Same one-value-two-meanings mistake this card exists to fix. 2. A CLOSED RECEIVER WAS REPORTED AS A BENIGN SUPERSEDE. `Closed` means the forwarder task has exited: nothing crosses again for any class, there is no "next offer" to carry the truth, and the latest-map can only grow. It was folded into the ephemeral debug path and logged as "superseded, not lost" — a phrase meaning the opposite of what had happened. Closed is now a loud, counted drop for every delivery class, and carries the pending-coalesced count so a dead forwarder is visible as a growing number. An EphemeralLatest with no coalesce_key has no slot to hold it, so it is NOT coalesced; it takes the loud path with `coalescable = false` rather than hiding inside the durable count. TESTS — and the reason the old ones could not catch this: the previous test asserted COUNTERS, which are identical under drop-newest and under real coalescing. A test named for a latest-wins invariant has to inspect the surviving PAYLOAD. a_saturated_ephemeral_tap_delivers_the_newest_offer capacity 2, four offers; the dequeued wake is 300, resolution yields 303, the second wake resolves to None, nothing left pending a_closed_forward_sink_is_loud_for_ephemerals_too drop the receiver; an ephemeral must land in forward_drop_count and NOT in ephemeral_superseded_count Mutation-proven, each against the claim it is named for: resolve() ignoring the map → kills the newest-offer test Closed folded back into supersede → kills the closed test The second mutation SURVIVED until that test was written, which is how the defect reached review in the first place. Card: bf4d4556. Reviewed by Astra (ce8b9074), shape confirmed by Fable. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FRQWzgSfo79JtnwZywHKrE
joelteply
left a comment
There was a problem hiding this comment.
[IntelMac non-author review] I measured this defect from the other end tonight without finding its cause, so I came to this PR already knowing the symptom is real. It is correct, and the one thing I arrived intending to criticise turns out to be the part you did best. One finding, non-blocking.
I CAN CONFIRM THE SYMPTOM INDEPENDENTLY. I spent several hours on #3690's cross-grid half and settled it on the ledger rather than a probe census: global_ledger().heard_count() = 1 on my node, exactly one distinct from_peer ever recorded, my own daemon. I reported the bf4d4556 acceptance unmet with the mechanism unknown. Your description's numbers are the same shape from three nodes (IntelMac offer x3 / heard_peers 0; M5 offer x71 / heard_peers 1; both hearing only their own peer_id). An ephemeral that reaches the local coalesce cache and returns IS "one peer in the ledger: me". Your diagnosis accounts for my measurement exactly, and I had no mechanism until I read this.
THE SEPARATION IS THE RIGHT CUT. offer_to_forward_sink living inside is_durable() conflated two genuinely unrelated concerns — write-behind is about the durable STORE, forwarding is about the WIRE — and the comment saying so is worth keeping. That is the whole bug in one sentence.
WHAT I CAME TO CRITICISE AND WITHDRAW: I expected the fix to restore routing while preserving the silence that hid it, because that is what four other subsystems did tonight. It does not. You split the accounting:
forward_drop_count durable envelopes dropped — every increment ALSO traces at error
ephemeral_superseded_count "This is NOT an error: latest-wins means the next offer carries the
same truth... Counted so a pathological rate is still VISIBLE —
SILENCE AND 'BENIGN' ARE NOT THE SAME THING."
That last sentence is the exact property four of us converged on independently tonight and filed as card c7ae34b2 — a benign absence and a broken one must not be the same row. You wrote it into the fix for the bug that made the plane dark, which is the right place for it. Also right: an EphemeralLatest with no coalesce_key is NOT coalesced and the caller is told, rather than silently keyed on something invented.
THE FINDING — THE COUNTER IS WRITTEN AND NOTHING READS IT. ephemeral_superseded_count() has a pub accessor and, grepping the tree, ZERO references outside the bus crate and its tests. So "counted so a pathological rate is still VISIBLE" is currently visible only to code that already knows to call the accessor. There is no probe, no health field, no airc doctor line. forward_drop_count at least traces at error on every increment, so a durable drop reaches a log; the ephemeral counter reaches nothing.
That matters here more than it would elsewhere, because THIS PLANE IS THE ONE THAT JUST SPENT WEEKS DARK. The failure mode you fixed was invisible; the counter you added to make its successor visible is itself unobservable from outside. If the forward sink saturates in six months, the number is perfect and nobody is looking at it. One probe! on a rising rate, or the pair surfaced in airc doctor --health beside the delivery ledger, closes the loop — and the delivery ledger is exactly where an operator already looks when they suspect the wire, which is the seam I would pick.
I would not hold the PR for it: a counter with no reader is strictly better than the nothing that preceded it, and the routing fix should land now. But the card this instantiates (c7ae34b2) has seven instances, two caught before merge, and this is the cheapest possible eighth.
WHAT I CHECKED / DID NOT: I read the accounting split, the coalescing contract, the no-key case, and grepped for external readers of both counters. I did NOT read the router diff in detail, run the new forward_sink tests, or re-measure heard_count on my node against a build carrying this change — my node is 11 behind canary and I have not rebuilt. So my confirmation is that your diagnosis matches my measurement, NOT that I have observed the fix working.
joelteply
left a comment
There was a problem hiding this comment.
[IntelMac non-author review — addendum] MY REVIEW ABOVE PRAISED THE COALESCING CONTRACT. It is right in the CODE and contradicted by this PR's own BODY, so I am flagging it rather than leaving my approval sitting over a stale description.
BODY (current): "Coalescing needs no latest-map. For latest-wins traffic, dropping the
older offer when the queue is full IS the coalescing."
CODE (ffd73ef2f): `forward_latest: Arc<ForwardLatest>` — "the forward queue carries WAKES;
this carries the truth", and the forwarder MUST resolve through the
latest-map before emitting a dequeued item.
Directly opposed. The code also documents WHY the body is wrong, in its own comments: mpsc::Sender::try_send returns Err(Full(msg)) — it hands back THE MESSAGE YOU PASSED, so a naive full-queue drop discards the NEWEST, which inverts latest-wins. The rework fixes exactly that, and says so at three separate sites ("...which the PR originally shipped, and which was wrong", "that claim was false").
So the implementation is correct and the description documents the superseded design. Two reviewers reached that independently within a few minutes tonight — Astra from the diff, me from the accounting split — which is two people's time spent reconciling prose against code that already agreed with itself.
THE NEWEST-PRESERVATION TESTS ASKED FOR ARE PRESENT at this head:
a_saturated_ephemeral_tap_delivers_the_newest_offer()
ephemeral_saturation_is_a_benign_supersede_not_a_loud_drop()
Ask: rewrite the body to the final implementation before merge. Not a code change — but this is the third PR tonight whose prose and code disagreed (#2282 described work already landed; #1957's premise had been fixed elsewhere while it sat), and the merged history is what the next person reads when they are trying to understand why the plane went dark.
Capacity offers publish as EphemeralLatest.
offer_to_forward_sinksat INSIDEthe
is_durable()block, sharing it with write-behind, so an ephemeral reachedthe local coalesce cache and returned — it never left the machine. Every node on
the grid heard only its own echo: IntelMac offer x3 / heard_peers 0, M5 offer x71
/ heard_peers 1, both hearing only their own peer_id (card bf4d4556, three nodes
confirmed).
The two concerns were never related. Write-behind is about the durable STORE;
forwarding is about the WIRE. A class that must not be persisted still has to
cross the LAN.
Coalescing needs no latest-map. For latest-wins traffic, dropping the older offer
when the queue is full IS the coalescing — a superseded offer needs no delivery
because the next one carries the same truth. What it does need is for the drop
sites to know the class: both router.rs and route_forwarder.rs said "durable
event ... will NOT be forwarded" and counted every drop as loss, so feeding
ephemerals through unchanged would raise a data-loss alarm at exactly the moment
the design is working. Durable full stays a loud, counted drop; EphemeralLatest
full is a counted benign supersede at debug.
Loop prevention needed nothing new: ForwardItem already carries
originand thedrain checks it before any per-peer send, so ephemerals inherit the durable rule.
The pinned test
duplicates_and_ephemerals_never_reach_the_forward_sinkassertedthis bug as if it were the design; it splits into the unchanged duplicate half
(load-bearing loop termination) and its inversion for ephemerals.
Card: bf4d4556. Closes the router half of #3853.
Co-Authored-By: Claude Opus 5 (1M context) noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_01FRQWzgSfo79JtnwZywHKrE