Skip to content

client: a direct dial reuses a still-handshaking pinned link (mcl-warden#3) - #90

Merged
rgfaber merged 2 commits into
mainfrom
direct-dial-pinned-link-reuse
Oct 11, 2026
Merged

rgfaber merged 2 commits into
mainfrom
direct-dial-pinned-link-reuse

Conversation

@rgfaber

@rgfaber rgfaber commented Oct 10, 2026

Copy link
Copy Markdown
Contributor

What

A fresh pool's direct dial for a station it already holds a configured seed for must reuse that seed's link even before the link has completed HELLO. Today it can't: the identity fallback (find_link_by_node_id/2 over known_links/1) only sees links that completed their handshake, so the pool dials a second connection to the same station — and the station's one-connection-per-identity policy turns the two links into a permanent mutual-replacement flap.

Found live on a freshly enrolled warden box (mcl-warden#3, 2026-10-10): 761 _macula.peering.duplicate_replaced events at the station, connect → peer_closed every ~1.3 s toward both the DNS name and the literal IPv6, session-proof-rate refusal, /health never healthy. Old boxes never hit it because their links completed HELLO long ago and the boot claim that triggers the direct dial is long issued — it is a fresh-enrollment bug.

Fix

New link_for_node_id/2: a live link whose completed handshake named the node id wins; otherwise a live link whose pinned expected_node_id names it. A configured seed's pin is the identity its handshake will prove, so a still-handshaking link is recognizably THE station — and call_when_connected/2 / link_when_connected/2 already wait for that handshake, so reuse is safe. A pin that does not match the station's real identity never matches a caller's DHT-verified node id, so a stale pin still falls through to a fresh dial exactly as before.

Both the direct-dial path (reuse_by_node_id/3) and the discovery dedup (already_connected_to/2) now go through it — the discovery pass had the same fresh-pool race for a still-handshaking bootstrap seed spelled differently by the directory.

Red/green

  • 9e1e4d57 test: ... (red): a_direct_dial_for_the_pinned_station_of_a_still_handshaking_seed_reuses_that_seed_test fails on v14.8.0 — the unpatched pool dials a second link (asserted via links/1 length).
  • 25cc82d6 the fix: same test passes.

Verified locally (v14.8.0 base, Erlang 28.4.2)

  • rebar3 eunit — 5,369 tests passed
  • rebar3 xref — clean
  • rebar3 as lint lint — clean
  • rebar3 as consumer_contracts dialyzer — clean

Rollout

Patch release after merge; mcl-warden's mcl_om "~> 0.39" floats onto it, so the fleet pins the rebuilt warden and the be-bru row goes back into its manifest.

Refs mcl-warden#3, macula-fleet#19.

… link

link_for_node_id/2 asks each live link's completed handshake first, then
its pinned expected_node_id -- a configured seed's pin names the same
station identity its handshake will prove, so a link that has not
completed HELLO yet is still recognizably THE station. Both the
direct-dial reuse path (reuse_by_node_id/3) and the discovery dedup
(already_connected_to/2) now go through it.

Without the pin fallback, a fresh pool's first direct dial (e.g. a boot
claim to the realm's serving station) races the configured seed link's
first HELLO: the pool dials a second connection to the same station
under the literal address the station_endpoint advertises, the station
replaces one of the two (one connection per identity), the replaced one
respawns and replaces the other back -- a permanent flap. Seen live
2026-10-10 on a fresh warden box: 761 duplicate_replaced events at the
station, proof-rate refusal, /health never healthy (mcl-warden#3).

A pin that does not match the station's real identity never matches a
caller's verified node_id, so a stale pin still falls through to a fresh
dial exactly as before.
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.

1 participant