From 7a6d6dd86f18d0432835a639165644d778051177 Mon Sep 17 00:00:00 2001 From: Rahul Malik Date: Wed, 7 Oct 2026 11:04:13 +0000 Subject: [PATCH] Flush the handshake addendum before returning from Handshake() Since ClickHouse added the server setting handshake_timeout_milliseconds (default 30000, 2026-04-21) the server keeps a timer running from the first Hello byte until it has read the handshake addendum that follows the server Hello (quota key, plus the chunked-protocol and parallel-replicas fields on newer revisions). Client::Impl::Handshake() wrote the addendum into the output buffer but did not flush it, so it only reached the server together with the first query. A connection that was opened and then not used for 30 seconds was closed by the server: Code: 209. DB::NetException: Timeout exceeded while reading from socket (peer: ..., 30000 ms). (SOCKET_TIMEOUT) which the client saw as a ServerException on its first Execute(), or as an SSL/socket error once the FIN had been processed. The server log for such a connection shows "Authenticating user ..." followed by nothing for 30 seconds and then the SOCKET_TIMEOUT from TCPHandler. Flush right after writing the addendum so the handshake completes on the wire when Handshake() returns. Verified against ClickHouse 26.10: a client that connects, sleeps 35 seconds and then runs SELECT 1 failed before this change and succeeds after it. Co-Authored-By: Claude Fable 5.1 --- clickhouse/client.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/clickhouse/client.cpp b/clickhouse/client.cpp index dae5cb7f..531498c0 100644 --- a/clickhouse/client.cpp +++ b/clickhouse/client.cpp @@ -676,7 +676,13 @@ bool Client::Impl::Handshake() { } if (server_info_.revision >= DBMS_MIN_PROTOCOL_VERSION_WITH_ADDENDUM) { + // The server reads the addendum as part of the handshake and, since + // handshake_timeout_milliseconds (default 30s), drops the connection + // if it does not arrive in time. Without this flush the addendum only + // left the buffer with the first query, so a connection that was idle + // for 30s after connecting was closed with SOCKET_TIMEOUT. WireFormat::WriteString(*output_, std::string()); + output_->Flush(); } return true;