Skip to content

GO-7556: recover from stale connections after sleep/wake - #801

Closed
requilence wants to merge 3 commits into
mainfrom
go-7556-stale-conn-recovery
Closed

requilence wants to merge 3 commits into
mainfrom
go-7556-stale-conn-recovery

Conversation

@requilence

Copy link
Copy Markdown
Contributor

After sleep/wake, clients keep reusing dead pre-sleep connections. Handshakes and RPCs hang for 10–30s+, and pool.Flush can miss peers that ocache GC is closing or that a dial publishes after the flush.

Changes

  • pool: Flush bumps a pool generation. Peers are stamped at dial/AddPeer, and every lookup rejects older-generation peers, so a peer restored by GC or published late is never handed out. Cleanup uses RemoveSame everywhere, and stale peers are closed asynchronously. No ocache changes.
  • peer: a lazy per-peer cleanup owner moves the synchronous sub-conn closes (release, failed handshake, gc) off the caller path. In-flight closes still count toward the open limiter. The MultiConn is closed only on stall evidence (threshold derived from the transport write timeout). Conns doomed by gc are never reused.
  • yamux: Open honours ctx.
  • quic: stream Read/Write wrap connection-death errors as ErrConnClosed.
  • handshake: new OutgoingProtoHandshakeWithCloser. The legacy function keeps its contract.
  • New API (additive): transport.WriteTimeouter, handshake.OutgoingProtoHandshakeWithCloser.

Test plan

  • go test -race ./net/... ./app/ocache/...; new tests at -count=20..200
  • Mutation checks on the pool and peer tests
  • anytype-heart builds and its device/peerstatus/pubsub/rpcstore/spacecore tests pass against this branch
  • Windows sleep/wake integration (with heart GO-7556 changes)

Linear: GO-7556

net/pool:
- Flush bumps a pool generation; peers are stamped at dial start /
  AddPeer and every lookup rejects older-generation peers, so a peer
  restored by ocache GC or published by a dial that spanned the flush
  can never be handed out again (no ocache changes).
- Instance-safe cleanup (RemoveSame) everywhere; stale peers are closed
  asynchronously, including ones not visible to ForEach.

net/peer:
- Per-peer lazy cleanup owner: synchronous sub-conn closes
  (ReleaseDrpcConn, failed handshake, peer gc) move off the caller path;
  in-flight closes count toward the open limiter; the MultiConn is closed
  only on stall evidence (threshold derived from the transport write
  timeout).
- Conns handed to the owner by gc are never reused.

net/transport:
- yamux Open honours ctx (bounded abandoned helpers).
- QUIC stream Read/Write wrap connection-death errors as ErrConnClosed.
- Optional WriteTimeouter for yamux/quic/iroh/webtransport.

net/secureservice/handshake:
- OutgoingProtoHandshakeWithCloser: cancellation hands the conn to a
  closer exactly once; legacy OutgoingProtoHandshake keeps its contract.
@requilence
requilence requested a review from cheggaaa October 1, 2026 15:40
@github-actions

github-actions Bot commented Oct 1, 2026 •

Copy link
Copy Markdown

New Coverage 62.7% of statements
Patch Coverage 92.2% of changed statements (318/345)

Coverage provided by https://github.com/seriousben/go-patch-cover-action

…Write

On the yamux (TCP fallback) transport a session shutdown made stream
Read return io.EOF / stream reset, which drpc v1.0.0 turns into a bare
context.Canceled, so callers couldn't tell connection loss from their own
cancellation. Mirror the QUIC normalization: ErrSessionShutdown, and
EOF/reset/stream-closed while the session is closed, wrap
transport.ErrConnClosed (original error still reachable via errors.As).
A normal remote stream close stays a plain io.EOF.
Callers can now classify TLS handshake I/O failures (EOF, reset,
timeout) with errors.Is/As. Type assertions and sentinel comparisons
are unaffected.
@requilence

Copy link
Copy Markdown
Contributor Author

Superseded by #802: same scope, but pool.Flush is now a cache swap instead of per-peer generation stamps. It's simpler, closes a case where a Get after Flush could wait out a pre-flush dial, and cached lookups are faster than main.

@requilence requilence closed this Oct 2, 2026
@github-actions github-actions Bot locked and limited conversation to collaborators Oct 2, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant