fix(mesh): frame loss when recv_validated is cancelled mid-validation (#2458)#2745
Open
brocoppler wants to merge 1 commit into
Open
fix(mesh): frame loss when recv_validated is cancelled mid-validation (#2458)#2745brocoppler wants to merge 1 commit into
brocoppler wants to merge 1 commit into
Conversation
…e silently lost (block#2458) run_demo_echo raced recv_validated against its 100ms drain tick in a select!. recv_validated reads a frame off the stream and then awaits Redis fence validation; a tick firing inside that await dropped the future and destroyed the frame it had already consumed. The stream then looked idle forever and the forwarding peer timed out — the block#2458 mesh_demo 504s, with loss probability ≈ validation latency / 100ms (worse under suite load, when Redis is slower). - reliable: split validate_received out of recv_validated so callers can extract frames cancel-safely and validate outside select!/timeout; document the cancel-safety contract. - mesh_boot: run_demo_echo selects only over frame extraction and validates outside the cancellable region. - buzz-relay-mesh: make IrohRecvHalf::recv_frame resumable (partial-read state lives in the half, not the future) so racing it is actually safe. - buzz-relay-mesh: loopback binds disable the iroh portmapper — a loopback socket can never use a WAN mapping, but the probe advertised one as a Portmapped candidate and path attempts to it fail with EADDRNOTAVAIL (regression test included). - mesh_demo: the forwarded-arm round trip runs over an in-memory stream pair by default (deterministic; reproduced the loss just like live transport) with the live-iroh variant kept as an #[ignore] evidence smoke. Verified: pre-fix the round trip failed ~40–80% locally (in-memory and live alike); post-fix 15/15 in-memory + 15/15 live + 3x full cargo test -p buzz-relay --lib green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: Broc Oppler <brocoppler@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Root-causes the #2458 mesh_demo flake to a cancel-safety bug in production code and fixes it, plus one piece of endpoint hygiene the investigation surfaced.
The bug:
run_demo_echoracesrecv_validatedagainst its 100 ms drain tick in aselect!.recv_validatedreads a frame off the stream, then awaits Redis fence validation. When the tick fires inside that await, the future is dropped with the frame it already consumed — the stream then looks idle forever, the forwarding peer waits for an echo that can no longer happen, and the probe times out (the familiar 504). Loss probability per exchange ≈ validation latency ÷ 100 ms, which is why it worsens exactly when Redis is slow — i.e. under full-suite parallelism, matching #2458's "fails undercargo test, passes standalone" signature. Reproduced at 40–80 % locally, on an in-memory stream pair and live iroh endpoints alike, which rules the transport out as the cause.Changes
tunnel/reliable.rs— splitvalidate_receivedout ofrecv_validatedso callers can extract frames cancel-safely and validate outside anyselect!/timeoutregion; the cancel-safety contract is documented on both.mesh_boot.rs—run_demo_echonow selects only over frame extraction; decoding + fence validation run outside the cancellable region. Drain semantics unchanged.buzz-relay-mesh/peer.rs—IrohRecvHalf::recv_frameis now resumable: partial-read state lives in the half rather than the future (each underlyingRecvStream::readawait is itself cancel-safe), so racingrecv_framecannot desync the stream either.buzz-relay-mesh/endpoint.rs— loopback binds disable the iroh portmapper. A loopback-bound socket can never transmit to a WAN mapping, but the gateway probe (UPnP/PCP/NAT-PMP) advertised one as aPortmappedcandidate and path attempts to it fail (sendmsg→EADDRNOTAVAIL) — observed live during the investigation on a NAT-PMP-capable network. Production binds (0.0.0.0) are unaffected. Regression test included (trivially green on hosts without a portmap-capable gateway).api/mesh_demo.rs— the forwarded-arm round trip now runs over an in-memoryMeshStreampair by default: deterministic, fast, and it reproduced the frame loss identically, making it the regression test for the fix. The live-iroh variant is kept asdemo_join_forwarded_arm_round_trips_echo_live_meshunder#[ignore]for explicit transport evidence runs.Verification
--ignored)cargo test -p buzz-relay --libcargo clippy -p buzz-relay -p buzz-relay-mesh --lib --tests: clean.buzz-relay-meshsuite: 33 passed.Relation to existing work
Fixes #2458
🤖 Generated with Claude Code