Rework connection liveness checking and connection reuse#1915
Rework connection liveness checking and connection reuse#1915SpencerTorres wants to merge 12 commits into
Conversation
🤖 Claude reviewReworks connection liveness checking and reuse: The design is careful and the reasoning holds up under tracing:
The regression suite is strong (dial-counting, One gap (should_fix): the new server-exception “keep the connection” behavior is asymmetric between the native and Blind spots: could not run the integration suite against a live server; relying on the PR's reported results for the churn/half-open assertions and the solaris/illumos Verdict: ✅ Approve General findings
Inline comments are attached to the relevant lines. This summary updates in place on re-review. |
Summary
While investigating a performance issue related to connections, several issues were identified and fixed:
connCheckis now a non-destructive peek (recvfrom(MSG_PEEK)instead of a 1-byteread). Pending bytes on an idle connection still mark it bad, which is correct for Native protocol. In these cases a debug message is logged.connLivenessWindow): a connection thatcompleted a command or passed a check within the last second skips the socket
probe. This removes the per-operation connection checks that read from the TCP connection.
Options.ConnIdlePingThreshold(DSN:conn_idle_ping_threshold, default 1m,negative disables): This is used for proper client Ping commands to validate the connection is valid. For cloud there are often a lot of proxies and other errors, so instead of depending on a client side socket check, we can now use a ping command to validate the connection before executing a command.
release()keeps the connectionand marks it unverified; the next acquire confirms liveness with a ping before handing
it out (covering the rare exception classes where the server does close). This should reduce the number of new connections formed for non-fatal connection errors.
query()releases the connection before closing the rows channels, so theconnection is guaranteed to be back in the pool by the time
rows.Close()returns.Parameter-binding failures now release the connection
healthy instead of through the error path.
prepareBatchreturns the connection to the pool on validation failure instead ofleaking it and its
MaxOpenConnsslot.contextWatchdogstop is now race-free (mutex + stopped flag): oncestop()returns, the callback has either completed or will never run. The
Sendwatchdog alsoflags the connection closed (
setClosed) so a cancelled batch's socket can never bepooled looking healthy.
Behavior changes
ConnIdlePingThresholdPinground trip on acquireContext cancellation mid-query still closes the connection by design (the response is not
drained), and this is asserted in the regression tests.
Testing
tests/conn_check_churn_test.go: counts physical dials via acustom
DialContextand probes idle sockets withMSG_PEEKbetween operations. Assertsexactly 1 dial for sequential query/exec/batch/server-logs/early-close/timeout-ctx
workloads (plain TCP and TLS), including 50 consecutive failing queries and mid-stream
exceptions (the probe also proves the socket is byte-clean at the exception boundary,
validating reuse safety). A blackhole-proxy test simulates a silently dropped flow and
verifies the idle ping discards the half-open connection and recovers within the dial
timeout instead of hanging.
tests/,tests/std, andtests/issuessuites pass against ClickHouse 26.6;root unit tests pass with
-race; cross-compiles clean for allGOOStargets coveredby the conn-check build tags (the
Recvfromcall matters on solaris/illumos).Checklist
As always, let me know if you have any questions or want to split this into multiple PRs. Thanks!