Repository navigation
Conversation
|
Closing without merging. A multi-reviewer audit found that the spec's documentation misdescribes the code in places, and that the committed configurations cannot reproduce the table in the spec header — TLC halts at the first violation, so the naive and guarded configs never evaluate the invariant the header reports for them, and that invariant is vacuous under the current config. The Reworking it is possible — a combined connection-failure action, corrected configurations, corrected prose — but the result would still be a single-node, single-peer, safety-only model, and the questions #140 actually turns on are cross-node and liveness-shaped: contagion between two nodes' maps, oscillation without backoff, and telling a converged mesh from an emptied one. A model that answers the easy question more rigorously while staying silent on the hard one is not worth the authority a spec in this repo carries. The one design conclusion that survives — removing by key when a receiver task ends is wrong — was reached independently from the code by several reviewers and does not need a model to stand. It is recorded on #140, along with a correction retracting the prescription this PR's header proposed. The investigation was not wasted: it produced #143, a live framing-corruption bug in the QUIC transport, which is the more valuable finding. |
Summary
A TLA+ model of the QUIC peer-connection map, written as the design step for #140. No production code changes.
peers: HashMap<NodeId, PeerConnection>: two insert sites under the same key, so an insert replaces and drops the displaced connection's send stream, while each detached receiver task outlives the replacement_currentreproduces today's behaviour: a dead peer stays in the map forever, sodirect_peers()reports it as linked and the mesh warning goes quiet exactly when it should fire_naiverefutes the obvious fix: removing by key when a receiver task ends drops a link whose send side still works, and on a reconnect deletes the newer entry_guardedrefutes the compare-and-remove fix too: the guard asks whether the slot is still mine, not whether the link is dead, so it also removes a healthy connectionThe header also records what the model cannot settle: no async implementation can satisfy
InvNoDeadLink, because a break and its cleanup are separate events; and guarded removal without a re-dial turns a stale entry into a permanently absent one, which the safety invariants cannot distinguish from a fix.Test plan
validate_specparses the spec and detects all three invariants_current:InvNoDeadLinkviolated, full state space_naiveand_naive_livedrop:InvNoLiveDropviolated_guardedand_guarded_livedrop:InvNoLiveDropviolated, refuting the guarded fixquic_transport.rsand the quinn source by two independent reviewers;RecvStream::dropcallingstop()andSendStream::dropcallingfinish()verified directlycargo make test: 24 suites, 1200 passed, 0 failed