Fix #123: every subduction call gets a real deadline, and a silent handshake closes its own wire - #124
Merged
Merged
Conversation
…ndshake closes its own wire NeverTimeout -- the Timeout impl that returned Ok(fut.await), no bound ever -- is retired. It co-conspired in the #113 hang (a sync call parked forever on a dead-but-unfailable transport; #122 removed that corpse) and still converted every reachable-but-silent peer into wedged-forever: the page's own 30s waits masked the UX, but every abandoned sync left an engine task parked for the life of the page, holding Arc<Sd> and connection Rcs. MonotonicTimeout races each call against wasi:clocks/monotonic-clock @0.3.0's wait-for (the async track; 0.2.9's pollable shape is not awaitable from this guest). The composite already imported the interface -- iroh.wit pulls it, and @polyengine/wasi serves waitFor on the 0.3 union provider in both hosts -- so the guest world import (engine.wit) is the only surface change. Every call site already passed CallTimeout::Default = the crate's 30s roundtrip bound; smoke-measured 100ms->109ms, 250ms->252ms; happy-path overhead not measurable (rebind-sync settle 0.20s, cross 0.03s, unchanged). Abort is safe by subduction's own design: the multiplexer's PendingGuard removes the pending entry when the call future drops, and ingestion/subscription happen only in the success arm (cited at the impl, verified against the pinned source). A TimedOut outcome is NOT 'gone:' -- the wire is alive, the peer is silent -- and the page keeper's leave-alone arm deliberately does not re-dial on it (#78's double-dial discipline; one-sentence notes in solo.ts and relay-partition.ts's wave-3 history). The keyhive handshake sat outside subduction's Timeout and parked the same way; the initiate side now carries its own 30s bound (the responder's 300s anti-replay window is a different fact, untouched). A timed-out dial CLOSES its connection explicitly -- this is by definition the case where the peer is alive, so nothing else ever would: the close resolves wait-closed, which wakes conn_gone_monitor, which closes the conn_inbound queues and finds the named timeout Err already in conn_results and leaves it (a named error outranks gone:). One teardown mechanism, the ordinary one. And the leak the fix would have created: with a real bound every abandoned handle INSERTS an outcome nobody will read (sync-status is one-shot; the page polls 30s then gives up). SYNCS_CAP=256 with an insertion-order deque evicts oldest -- eviction horizon ~5min at the demo's 48 settles/min against the page's 30s read horizon. demo/host/timeout-check.ts (+ just timeout-check): control settles 0.20s; a blackholed peer produces a BOUNDED named outcome at ~30s. The probe's banner is honest that it cannot discriminate the call bound from the QUIC idle timeout (both 30s; measured 30.03s fixed vs 30.20s reverted) -- true live-wire-silent-peer needs a cooperating mute peer, which would mean test-only WIT surface on a shipping world. The property it pins is bounded-not-parked, which is what #123 names. Gates: engine just check (-D warnings) clean; conn-gone + rebind-sync unchanged (x3); timeout-check x3; full e2e 34/34 (relay-partition converge 5.3s, no regression); devstore ALL REQUIRED ROWS PASS; soak seed 2/25 green. Independently reviewed; should-fix applied.
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.
Fixes #123.
NeverTimeoutis retired.MonotonicTimeoutraces every subduction call againstwasi:clocks/monotonic-clock@0.3.0's asyncwait-for— the composite already imported the interface (iroh.wit pulls it; @polyengine/wasi serves it in both hosts), so the guest world import is the only surface change. Every call site already passedCallTimeout::Default= the crate's 30s roundtrip bound. Abort is cancel-safe by subduction's own design (the multiplexer'sPendingGuard; ingestion only in the success arm — cited and verified against the pinned source). Happy-path overhead: not measurable (rebind-sync settle 0.20s / cross 0.03s, unchanged).Also bounded: the keyhive handshake's initiate side (30s; the responder's 300s anti-replay window is a different fact, untouched). A timed-out dial closes its connection explicitly — the peer being alive means nothing else ever would — and the close hands teardown to the ordinary
conn_gone_monitorchain; the named timeout error outranksgone:inconn_results. ATimedOutoutcome is deliberately NOTgone:: silent peer, live wire, no re-dial (#78 discipline; noted in the keeper and the wave-3 banner).Plus the leak the fix would have created: with a real bound, abandoned sync handles now insert outcomes nobody reads (sync-status is one-shot) — capped at 256 with insertion-order eviction (~5min horizon vs the page's 30s read horizon).
New headless gate
just timeout-check: control 0.20s; blackholed peer → bounded named outcome at ~30s. Its banner is honest that it cannot discriminate the call bound from the QUIC idle timeout (both 30s); the property it pins is bounded-not-parked, which is what #123 names.Gates: engine clippy
-D warningsclean;conn-gone/rebind-syncunchanged ×3;timeout-check×3; full e2e 34/34 (relay-partition heal 5.3s, no regression); devstore ALL REQUIRED ROWS PASS; soak seed 2/25 green. Independently reviewed; the should-fix (the connection leak on a timed-out dial) applied.Automerge armed (merge commit, org convention).