Skip to content

Don't block server shutdown on idle keep-alive connections - #108581

Open
george-larionov wants to merge 1 commit into
masterfrom
fix-http-keepalive-shutdown-hang
Open

Don't block server shutdown on idle keep-alive connections#108581
george-larionov wants to merge 1 commit into
masterfrom
fix-http-keepalive-shutdown-hang

Conversation

@george-larionov

Copy link
Copy Markdown
Member

On shutdown, server.stop only closes the listening socket. A handler thread already parked in HTTPServerSession::hasMoreRequests — a socket read bounded by keep_alive_timeout — is not woken, and HTTPServerConnection::run only re-checks tcp_server.isOpen() after that read returns. The interserver ("servers for tables") shutdown phase then blocks in server_pool.joinAll for the full keep_alive_timeout, since (unlike the client-connection phase, which calls safeExit) it has no force-close fallback. Any idle keep-alive connection on such a server stalls shutdown for the whole timeout.

Fix: make the keep-alive wait interruptible. hasMoreRequests polls in 1-second slices and re-checks a stop callback (!tcp_server.isOpen()) between slices, so an idle connection observes shutdown within ~1s. The wait is only shortened while idle — buffered and in-flight requests are handled before it — so no traffic is truncated.

Added integration test test_keep_alive_shutdown that holds an idle keep-alive connection on the interserver port with keep_alive_timeout=50 and asserts shutdown still completes quickly. Verified: it fails (Server did not stop within timeout) without the fix and passes with it.

Tradeoffs:

  • Touches the vendored Poco fork (HTTPServerSession), extending its existing keep-alive machinery.
  • Idle keep-alive connections now wake ~once per second to check the stop flag (a single-fd poll that returns immediately) — negligible per connection, a minor change in a hot path at very high idle-connection counts.

Changelog category (leave one):

  • Bug Fix (user-visible misbehavior in an official stable release)

Changelog entry (a user-readable short description of the changes that goes into CHANGELOG.md):

Fixed server shutdown blocking for up to keep_alive_timeout seconds when an idle keep-alive HTTP connection (for example, an interserver connection) is open.

Documentation entry for user-facing changes

  • Documentation is written (mandatory for new features)

On shutdown `server.stop` only closes the listening socket; a handler
thread already parked in `HTTPServerSession::hasMoreRequests` (a socket
read bounded by `keep_alive_timeout`) is not woken, so `server_pool.joinAll`
blocks for the full `keep_alive_timeout`. The interserver ("servers for
tables") phase has no force-close fallback, unlike the client phase's
`safeExit`, so an idle keep-alive connection there stalls shutdown for the
whole timeout.

Make the keep-alive wait interruptible: `hasMoreRequests` polls in 1s
slices and re-checks a stop callback (`!tcp_server.isOpen()`) between them,
so an idle connection observes shutdown within ~1s. Buffered and in-flight
requests are untouched.

Add an integration test that holds an idle keep-alive connection on the
interserver port and asserts shutdown still completes quickly.
@clickhouse-gh

clickhouse-gh Bot commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

Workflow [PR], commit [f1c63c8]

Summary:

job_name test_name status info comment
Stress test (amd_tsan) FAIL
Cannot start clickhouse-server FAIL cidb
Logical error: 'Unexpected exception in refresh scheduling' (STID: 2508-3e7b) FAIL cidb, issue
Check failed FAIL cidb

AI Review

Summary
  • This PR makes idle HTTP keep-alive waits interruptible during shutdown by adding a stop callback to HTTPServerSession and wiring it from HTTPServerConnection, with an integration test covering an idle interserver keep-alive connection and a long keep_alive_timeout. I did not find a correctness, safety, or performance issue that warrants an inline finding.
Missing context / blind spots
  • ⚠️ The PR CI report had no failed checks at review time, but most build/test jobs were still pending and Bugfix validation (integration tests) was still running. Completion of those jobs, especially the new test_keep_alive_shutdown coverage, would close the remaining validation gap.
Final Verdict
  • Status: ✅ Approve

@clickhouse-gh clickhouse-gh Bot added the pr-bugfix Pull request with bugfix, not backported by default label Jun 26, 2026
@clickhouse-gh

clickhouse-gh Bot commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

LLVM Coverage Report

Metric Baseline Current Δ
Lines 85.40% 85.40% +0.00%
Functions 92.60% 92.60% +0.00%
Branches 77.60% 77.60% +0.00%

Changed lines: Changed C/C++ lines covered by tests: 4/4 (100.00%) | Lost baseline coverage: none · Uncovered code

Full report · Diff report

@george-larionov

Copy link
Copy Markdown
Member Author

@vdimir would you like to review?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pr-bugfix Pull request with bugfix, not backported by default

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant