fix: abort connection establishment when connection is closed#1737
fix: abort connection establishment when connection is closed#1737arthurschreiber wants to merge 2 commits into
Conversation
Calling `close` while a connection was being established did not abort the connection process. Nothing connected `close` to the abort controller inside `initialiseConnection`, and the connection process never re-checked the connection state after resuming from `await` points. If `close` was called before the socket was created, the connection process would continue unaffected, log in, reset the `closed` flag, and emit a successful `connect` event after `end` was already emitted, leaving behind a fully established "zombie" connection. If `close` was called after the socket was created, destroying the socket only emitted a `close` event, which the login logic ignores, so the connection process would dangle until the connect timer fired, emitting a `connect` event with an `ETIMEOUT` error and a duplicate `end` event. Introduce a `closeController` that is created in `connect` and aborted by `close` with an `ECLOSE` connection error. Its signal is combined with the per-attempt timeout signal, so every abort checkpoint in the connection process now also observes connection closure: - Check for early abort after the socket is connected, as abort listeners added to an already aborted signal are never called. - No longer reset the `closed` flag during connection establishment, as it allowed a closed connection to be resurrected. - Route the connection failure path through `cleanupConnection` so that the `end` event is emitted exactly once. - Make the transient failure retry delay abortable so that closing during a retry wait does not emit a spurious `retry` event.
Code ReviewOverviewThis PR fixes a real and subtle race condition: calling The fix is conceptually clean: introduce a CorrectnessEvent ordering is correct. When
Since both are nextTick-scheduled, and the
Removal of The IssuesMissing test for retry delay abort. The PR description specifically calls out "Make the transient failure retry delay abortable so that closing during a retry wait does not emit a spurious
Unused Minor Observations
SummaryThe fix is well-reasoned and correctly addresses all the root causes described in the PR. The implementation is idiomatic and the two tests cover the primary scenarios with a realistic mock server (full PRELOGIN → LOGIN7 → Initial SQL sequence). The main gap is a test for the transient retry abort path. Everything else is minor. |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #1737 +/- ##
==========================================
+ Coverage 79.82% 79.84% +0.02%
==========================================
Files 90 90
Lines 4901 4908 +7
Branches 923 923
==========================================
+ Hits 3912 3919 +7
Misses 687 687
Partials 302 302 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
…nnect # Conflicts: # src/connection.ts
Code Review: fix: abort connection establishment when connection is closedOverviewThis PR fixes a real and tricky race condition: calling The design is sound. A few observations below. CorrectnessEvent ordering when When
This means
Missing test coverageThe retry wait abort path is new code: // src/connection.ts:3441-3455
const closeSignal = this.closeController!.signal;
closeSignal.throwIfAborted();
const onAbort = () => { reject(closeSignal.reason); };
closeSignal.addEventListener('abort', onAbort, { once: true });
...There's no test that calls Minor observationsNon-null assertions on
SummaryThe fix is correct, well-targeted, and handles the described zombie-connection and duplicate-end-event bugs. The |
Calling
closewhile a connection was being established did not abort the connection process. Nothing connectedcloseto the abort controller insideinitialiseConnection, and the connection process never re-checked the connection state after resuming fromawaitpoints.If
closewas called before the socket was created, the connection process would continue unaffected, log in, reset theclosedflag, and emit a successfulconnectevent afterendwas already emitted, leaving behind a fully established "zombie" connection. Ifclosewas called after the socket was created, destroying the socket only emitted acloseevent, which the login logic ignores, so the connection process would dangle until the connect timer fired, emitting aconnectevent with anETIMEOUTerror and a duplicateendevent.Introduce a
closeControllerthat is created inconnectand aborted byclosewith anECLOSEconnection error. Its signal is combined with the per-attempt timeout signal, so every abort checkpoint in the connection process now also observes connection closure:closedflag during connection establishment, as it allowed a closed connection to be resurrected.cleanupConnectionso that theendevent is emitted exactly once.retryevent.