Skip to content

Reconnect when the receive loop died but the socket stayed open - #88

Merged
nathanfallet merged 1 commit into
mainfrom
fix/reconnect-when-receive-loop-died
Aug 31, 2026
Merged

Reconnect when the receive loop died but the socket stayed open#88
nathanfallet merged 1 commit into
mainfrom
fix/reconnect-when-receive-loop-died

Conversation

@nathanfallet

Copy link
Copy Markdown
Member

connect() only ever checked the transport:

transport?.let { if (it.isActive && !disconnected) return it }

socketSubscription is never consulted. The receive loop runs in a scope handed in from outside, so its lifetime is not the connection's to control — cancelling that scope kills the loop without closing the socket.

From there nothing notices: onTransportGone is not reached (the CancellationException is rethrown first), disconnected stays false, isActive stays true. connect() hands back a socket nobody reads, so every later command waits out its commandTimeout for a reply that cannot be delivered — and failPendingRequests never fires, because nothing signals the loss. With commandTimeout <= 0 the wait has no end at all.

An open socket whose receive loop is dead is not a healthy connection: the loop is what completes the waiters.

Changes

isUsable() requires a live receive loop alongside an active transport, and the rebuild branch now triggers on any unusable transport rather than on the disconnected flag alone — a loop that died without reporting anything needs the same treatment as one that did.

This closes the last gap in the set already fixed by the in-flight-command failure and connectMutex double-check work.

Also drops a stale sentence in the lastMessageAt comment, which described both 0 and null as the "nothing arrived yet" sentinel.

Tests

  • a command issued after the receive loop was cancelled — reconnects and is answered, instead of timing out on a socket nobody reads
  • three consecutive commands on a healthy connection — one transport, not three, so the check cannot be satisfied by reconnecting every time

The first fails on main. Mutation-checked: dropping the liveness check, gating the rebuild on disconnected alone, testing that the loop exists rather than that it is alive, and never reusing a transport each turn one red.

205 JVM tests green. kotlinStoreYarnLock and the :cdp / :core / :opentelemetry detekt gates fail identically on main and are untouched; :core violations stay at 142.

connect() only ever checked the transport:

    transport?.let { if (it.isActive && !disconnected) return it }

socketSubscription is never consulted. The receive loop runs in a scope handed
in from outside, so its lifetime is not the connection's to control: cancelling
that scope kills the loop without closing the socket. onTransportGone is not
reached (CancellationException is rethrown first), disconnected stays false and
isActive stays true, so connect() hands back a socket nobody reads. Every later
command then waits out its commandTimeout for a reply that cannot be delivered,
and failPendingRequests never fires because nothing signals the loss.

isUsable() now requires a live receive loop as well, and the rebuild branch
triggers on any unusable transport rather than on the disconnected flag alone —
a loop that died without reporting anything needs the same treatment as one that
did.

Also drops a stale sentence left in the lastMessageAt comment, which described
both 0 and null as the 'nothing arrived yet' sentinel.
@codecov

codecov Bot commented Aug 31, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 75.00000% with 1 line in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
...n/dev/kdriver/core/connection/DefaultConnection.kt 75.00% 0 Missing and 1 partial ⚠️

📢 Thoughts on this report? Let us know!

@nathanfallet
nathanfallet merged commit 36bfb66 into main Aug 31, 2026
5 checks passed
@nathanfallet
nathanfallet deleted the fix/reconnect-when-receive-loop-died branch August 31, 2026 20:17
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