Skip to content

fix: skip drain dispatch for clientTtl-evicted clients in PoolBase - #5677

Open
vwong wants to merge 1 commit into
nodejs:mainfrom
vwong:fix/client-closed-error-on-client-ttl
Open

fix: skip drain dispatch for clientTtl-evicted clients in PoolBase#5677
vwong wants to merge 1 commit into
nodejs:mainfrom
vwong:fix/client-closed-error-on-client-ttl

Conversation

@vwong

@vwong vwong commented Aug 10, 2026

Copy link
Copy Markdown

Problem

When a Pool is configured with clientTtl, evicted clients can still emit drain events after being closed — typically when an in-flight request completes after the client has been removed from the pool. kOnDrain has no guard against this and will attempt to dispatch queued pool items to the closed client, throwing ClientClosedError.

The race window is narrow in low-latency environments (same-region) but widens significantly with cross-region TLS connection times (hundreds of milliseconds), making the error reliably reproducible under load.

Sequence that triggers the bug:

  1. Pool has connections: 1. Client C1 is active, its clientTtl expires.
  2. A new request arrives → kGetDispatcher evicts C1 (client.close(), kClosed = true), creates C2.
  3. C2 starts a TLS handshake. While it's connecting, further requests fill the pool queue (kQueue), because C2 has kNeedDrain = true and the connections limit is reached.
  4. C1 finishes an in-flight response → resume() → emitDrain() → C1.emit('drain').
  5. kOnDrain(C1) dequeues an item from kQueue and calls C1.dispatch() → ClientClosedError.

Fix

Return early in kOnDrain when the client is already closed or destroyed. The queued items remain in kQueue and are correctly drained when C2 connects and emits its own drain event.

Why connections: 2 doesn't prevent this alone

The drain listener is added in kAddClient and bound to the specific client instance. kRemoveClient removes the client from kClients but never removes the listener. So even with a second connection available, the stale listener on C1 still fires and attempts to dispatch to C1.

@codecov-commenter

codecov-commenter commented Aug 13, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 93.45%. Comparing base (dd85997) to head (0c26a12).
⚠️ Report is 6 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #5677      +/-   ##
==========================================
+ Coverage   93.43%   93.45%   +0.02%     
==========================================
  Files         110      110              
  Lines       38733    38780      +47     
==========================================
+ Hits        36190    36242      +52     
+ Misses       2543     2538       -5     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

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.

3 participants