[client-v2, jdbc-v2] Makes 159 Execution TImeout not retriable & make setQueryTimeout set proper setting. - #3073
Conversation
…e fixes for tests. Added test for JDBC driver with sync=1
There was a problem hiding this comment.
Pull request overview
This PR adjusts timeout/error handling in the client-v2 and JDBC-v2 layers so that ClickHouse server error 159 (Execution Timeout) is no longer treated as retryable, and adds/updates integration tests to validate query timeout behavior (including when JDBC uses async operations).
Changes:
- client-v2: Mark
ServerExceptioncode 159 as non-retryable and add a named constant for it. - jdbc-v2: Translate execution-timeout conditions into
SQLTimeoutExceptionand add new integration tests validatingStatement.setQueryTimeout()behavior for async and server-side timeouts. - Tests/fixtures: Update SQL/YAML expectations and float boundary assertions; improve metrics pool test concurrency behavior.
Reviewed changes
Copilot reviewed 12 out of 12 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| jdbc-v2/src/test/resources/StatementSQLTests.yaml | Updates EXPLAIN query variant and expected row count. |
| jdbc-v2/src/test/java/com/clickhouse/jdbc/StatementTest.java | Adds query-timeout tests (async + server max_execution_time). |
| jdbc-v2/src/test/java/com/clickhouse/jdbc/JdbcDataTypeTests.java | Stabilizes Float32 boundary assertions. |
| jdbc-v2/src/main/java/com/clickhouse/jdbc/StatementImpl.java | Maps execution timeout to SQLTimeoutException. |
| jdbc-v2/src/main/java/com/clickhouse/jdbc/metadata/DatabaseMetaDataImpl.java | Extends engine-to-table-type mapping (new engines). |
| jdbc-v2/src/main/java/com/clickhouse/jdbc/internal/parser/javacc/ClickHouseSqlUtils.java | Adds newer keyword aliases. |
| client-v2/src/test/java/com/clickhouse/client/query/QueryTests.java | Adjusts float expectations based on server version behavior. |
| client-v2/src/test/java/com/clickhouse/client/metrics/MetricsTest.java | Makes metrics pool concurrency assertions more deterministic. |
| client-v2/src/test/java/com/clickhouse/client/ClientTests.java | Adds execution-timeout test for server setting. |
| client-v2/src/main/java/com/clickhouse/client/api/ServerException.java | Adds EXECUTION_TIMEOUT constant and removes 159 from retryable codes. |
| clickhouse-jdbc/src/test/java/com/clickhouse/jdbc/GenericJDBCTest.java | Disables one test pending follow-up. |
| CHANGELOG.md | Adds changelog entry for non-retryable 159 behavior. |
Suppressed comments (5)
jdbc-v2/src/test/java/com/clickhouse/jdbc/StatementTest.java:400
- The assertion compares runtime against
queryTimeoutMs, but the configured timeout is in whole seconds (setQueryTimeout(int seconds)). This mismatch can cause flaky assertions (e.g. 3s timeout vs 3.7squeryTimeoutMs).
long wTimeoutTime = System.currentTimeMillis() - wTimeoutStart;
assertTrue(Math.abs(wTimeoutTime - queryTimeoutMs) < 1000);
jdbc-v2/src/test/java/com/clickhouse/jdbc/StatementTest.java:425
max_execution_timeis configured in seconds, but the test computesqueryTimeoutMsand truncates it to seconds. If it truncates to 0, the server timeout is effectively disabled and the test can pass/fail depending on environment speed. Consider clamping to at least 1 second and asserting against the effective seconds-based timeout instead of millisecond target.
int queryTimeoutMs = (int) (woTimeoutTime * 0.75);
Properties config = new Properties();
config.setProperty(ClientConfigProperties.serverSetting("max_execution_time"), String.valueOf(TimeUnit.MILLISECONDS.toSeconds(queryTimeoutMs)));
try (Connection conn = getJdbcConnection(config)) {
jdbc-v2/src/test/java/com/clickhouse/jdbc/StatementTest.java:438
- This assertion compares elapsed time to
queryTimeoutMs(a millisecond value), butmax_execution_timeis set in whole seconds (truncated). As a result, the effective timeout can be significantly lower thanqueryTimeoutMs, making the assertion flaky.
long wTimeoutTime = System.currentTimeMillis() - wTimeoutStart;
assertTrue(Math.abs(wTimeoutTime - queryTimeoutMs) < 1000);
jdbc-v2/src/test/java/com/clickhouse/jdbc/StatementTest.java:446
- The async
max_execution_timeconfiguration repeats the same milliseconds-to-seconds truncation; if it truncates to 0, the server-side timeout is disabled for this part of the test.
config = new Properties();
config.setProperty(ClientConfigProperties.serverSetting("max_execution_time"), String.valueOf(TimeUnit.MILLISECONDS.toSeconds(queryTimeoutMs)));
config.setProperty(ClientConfigProperties.ASYNC_OPERATIONS.getKey(), "true");
try (Connection conn = getJdbcConnection(config)) {
jdbc-v2/src/test/java/com/clickhouse/jdbc/StatementTest.java:459
- Same as the non-async branch: the assertion uses
queryTimeoutMswhile the configuredmax_execution_timeis in whole seconds (possibly truncated). This can make the runtime check brittle.
long wTimeoutTime = System.currentTimeMillis() - wTimeoutStart;
assertTrue(Math.abs(wTimeoutTime - queryTimeoutMs) < 1000);
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
|
@cursor review |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 60bb7e1. Configure here.
…ur setQueryTimeout Ports the 0.9.9 patch (ClickHouse#3073) to main. The patch was merged into the v0.9.9 maintenance branch only, so 0.10.0 and main carry the original behaviour. - client-v2: `ServerException` no longer reports code 159 TIMEOUT_EXCEEDED as retryable. The server raises it once the query has already consumed its whole `max_execution_time` budget, so an automatic retry spends that budget again. - jdbc-v2: `Statement#setQueryTimeout` is applied as the `max_execution_time` server setting when asynchronous operations are disabled, because the query then runs in the calling thread and a future timeout cannot interrupt it. A negative value is rejected; zero clears the setting. - jdbc-v2: an execution timeout is reported as `SQLTimeoutException`. Two deliberate differences from the 0.9.9 patch: - `SQLTimeoutException` carries the ClickHouse error code as its vendor code, matching what `ExceptionUtils.toSqlState` already does for `ServerException`. The 0.9.9 patch used the `(String, Throwable)` constructor, which leaves `getErrorCode()` at 0, so callers that classify on the vendor code had to walk the cause chain. - The `ConnectionImpl` hunk and the `connectionLvlExecTimeout` field are left out. They exist to restore a connection-level `max_execution_time` on `setQueryTimeout(0)`; on main the same result comes from resetting the option and falling back to the client configuration. The part of that hunk that makes the documented `default_query_settings` property take effect is a separate bug and belongs in its own change. `HttpAPIClientHelperTest.serverExceptionRetryCases` used 159 as its example of a retryable code. The test covers `shouldRetry` reading the `ServerException` from the cause instead of throwing `ClassCastException`, so it now uses 209 SOCKET_TIMEOUT and its intent is unchanged. The unrelated backports carried by ClickHouse#3073 (ClickHouseSqlUtils keywords, DatabaseMetaDataImpl engine map, test stabilisation) are already on main. Closes ClickHouse#3136 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

Summary
159 Execution Timeoutnot retriablesetQueryTimeout()works when client uses async operationsStatement#setQueryTimeoutmodify request settings and settingmax_execution_time. This happens only if non async mode used for clientCloses: #3074
Checklist
Delete items not relevant to your PR: