Fix client-v2: detect the codec of a compressed response instead of assuming LZ4 - #3106
Fix client-v2: detect the codec of a compressed response instead of assuming LZ4#3106polyglotAI-bot wants to merge 13 commits into
Conversation
…ssuming LZ4 ClickHouse 26.9 switched the default codec of the HTTP compress=1 framing to ZSTD(3), while the response reader asserted the LZ4 method byte of every block, so every compressed read failed with "Invalid LZ4 magic byte: '-112'". The framing is self-describing, so the reader now takes the codec from the block header and decompresses LZ4, ZSTD and uncompressed blocks. A block of an unknown codec is still rejected, which keeps the fallback that reads an unframed error body. zstd-jni becomes a required dependency of client-v2 and is shaded into the all artifacts. Fixes: #3105
Client V2 CoverageCoverage Report
Class Coverage
|
JDBC V2 CoverageCoverage Report
Class Coverage
|
JDBC V1 CoverageCoverage Report
Class Coverage
|
Client V1 CoverageCoverage Report
Class Coverage
|
…ntly The new negative paths of the block decoder had no test, and writing them exposed two defects in them: - ClickHouseUtils.format delegates to String.format, so the MessageFormat style placeholders of the three new messages were printed literally and the byte counts they carry were lost. - Zstd.decompressByteArray throws ZstdException instead of returning an error code, so the Zstd.isError branch was unreachable and a corrupted ZSTD block escaped as a ZstdException while every other failure of the decoder is reported as a ClientException. The corrupted frame tests now run from one data provider that covers an unknown compression method, an impossible block size, a negative uncompressed size, a checksum mismatch, a block the ZSTD codec rejects and a ZSTD or uncompressed block whose declared size does not hold.
|
Pushed Writing the missing tests for the new failure paths showed two defects in them:
The three corrupted-frame tests are merged into one Verified: |
TriageCategory: Summary What this impacts
Concerns
Required reviewer action
|
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 0ce58b0. Configure here.
chernser
left a comment
There was a problem hiding this comment.
There should be no guessing:
- we need to introduce compression algorithms property like it was in V1 and define what algorithms are available
- by default we need to have lz4 set in headers correctly (this is needed for backward compatibility)
- need documentation in migration guide for 0.11.0 and bold message in release notes.
zstd-jni should stay provided as before to not disrupt packaging (to avoid too many changes)
The response of a query was requested with the compress=1 framing of the HTTP interface, whose codec the server chooses on its own. ClickHouse 26.9 changed that codec from LZ4 to ZSTD(3) and the framed output follows the built-in default with no setting to override it, so every compressed read failed with 'Invalid LZ4 magic byte'. The algorithm is now part of the request: the new client.compression_algorithm property names it out of a defined set, defaults to LZ4 for backward compatibility, and is sent as the content coding of the operation, so a compressed body always uses the algorithm the client asked for. Fixes: #3105
Read the algorithm through a resolver, so a per-operation option set as the name of an algorithm is accepted: a per-operation option is stored unparsed. Add the typed setters QuerySettings#compressionAlgorithm and InsertSettings#compressionAlgorithm. Warn when a request is compressed without http compression and another algorithm than LZ4 is selected: the ClickHouse framing of a request is LZ4. Pin the request contract with a mock-server test: compress=1 is not requested, a response is requested with the content coding of the algorithm, NONE requests no compression, and an operation overrides the client.
|
Thanks - reworked along your four points. Pushed 1. Compression-algorithm property, no guessing. New 2. LZ4 in the headers by default. The client now asks for a response with 3. Docs. Migration guide entry in 4. Packaging untouched. Both pom changes are reverted: Verification: against |
…eration The client now seeds one more default setting, so the canary counts of ClientTests, which the test itself asks to increment when a setting is added, move to the new size. They were the whole failure of the client-v2 legs: the failing module stopped before the JaCoCo merge of the coverage profile, which reports the coverage of new code as zero. Two paths of the new setting had no test: an insert that selects its own algorithm, and a request compressed without http compression, whose framing stays the ClickHouse one while the response still follows the algorithm. The javadoc of httpHeader(String, String) is restored - the new method of QuerySettings was inserted between the javadoc and its method.
|
CI on Root cause of both reds — one failure, two symptoms. The client now seeds one more default setting ( The SonarCloud "0.0% coverage on new code" gate has the same cause and needed no separate work: Changes
Verified in a devbox against a live server: Still red and pre-existing, not from this branch: |
…ed-response-codec-detection
|
Merged current The merge was textual only: Re-verified against the merged base in a devbox (server
|
…ed-response-codec-detection
|
Refreshed the branch against
Re-verified on the merged base:
The merge commit changes the head, so an earlier review may need to be re-submitted. |
…ed-response-codec-detection
|
Base conflict resolved (textual only). Only overlap was Re-verified against the merged base (server
@chernser the PR is still waiting on a re-review of the reworked design (explicit |
…ed-response-codec-detection
|
Rebased onto the current Resolution was textual only: Re-verified against the merged base (server
The new @chernser this head is ready for your re-review — the four points of your review are implemented in |
…ed-response-codec-detection
|
Refreshed the branch against
Re-verified against the merged base:
@chernser this head is still waiting for your re-review — the four points of your review are implemented (explicit |
…ed-response-codec-detection
|
Resolved a base-side conflict with Re-verified against the merged base on a
@chernser this head is ready for a re-review — your earlier comments are addressed by |
…ed-response-codec-detection
|
Rebased onto current Re-verified against the merged base (ClickHouse
On the red @chernser this head is ready for a re-review; the earlier review comments were addressed in |
…ed-response-codec-detection
|
chernser
left a comment
There was a problem hiding this comment.
There is still a failure related to the LZ4
[ERROR] com.clickhouse.client.HttpTransportTests.testMultiPartRequest -- Time elapsed: 0.033 s <<< FAILURE!
com.clickhouse.client.api.ClientException: Failed to get query response
at com.clickhouse.client.api.Client.queryAll(Client.java:2194)
at com.clickhouse.client.api.Client.queryAll(Client.java:2203)
at com.clickhouse.client.HttpTransportTests.testMultiPartRequest(HttpTransportTests.java:1788)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:569)
at org.testng.internal.invokers.MethodInvocationHelper.invokeMethod(MethodInvocationHelper.java:136)
at org.testng.internal.invokers.TestInvoker.invokeMethod(TestInvoker.java:658)
at org.testng.internal.invokers.TestInvoker.invokeTestMethod(TestInvoker.java:219)
at org.testng.internal.invokers.MethodRunner.runInSequence(MethodRunner.java:50)
at org.testng.internal.invokers.TestInvoker$MethodInvocationAgent.invoke(TestInvoker.java:923)
at org.testng.internal.invokers.TestInvoker.invokeTestMethods(TestInvoker.java:192)
at org.testng.internal.invokers.TestMethodWorker.invokeTestMethods(TestMethodWorker.java:146)
at org.testng.internal.invokers.TestMethodWorker.run(TestMethodWorker.java:128)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
at org.testng.TestRunner.privateRun(TestRunner.java:808)
at org.testng.TestRunner.run(TestRunner.java:603)
at org.testng.SuiteRunner.runTest(SuiteRunner.java:429)
at org.testng.SuiteRunner.runSequentially(SuiteRunner.java:423)
at org.testng.SuiteRunner.privateRun(SuiteRunner.java:383)
at org.testng.SuiteRunner.run(SuiteRunner.java:326)
at org.testng.SuiteRunnerWorker.runSuite(SuiteRunnerWorker.java:52)
at org.testng.SuiteRunnerWorker.run(SuiteRunnerWorker.java:95)
at org.testng.TestNG.runSuitesSequentially(TestNG.java:1249)
at org.testng.TestNG.runSuitesLocally(TestNG.java:1169)
at org.testng.TestNG.runSuites(TestNG.java:1092)
at org.testng.TestNG.run(TestNG.java:1060)
at org.apache.maven.surefire.testng.TestNGExecutor.run(TestNGExecutor.java:155)
at org.apache.maven.surefire.testng.TestNGDirectoryTestSuite.executeMulti(TestNGDirectoryTestSuite.java:169)
at org.apache.maven.surefire.testng.TestNGDirectoryTestSuite.execute(TestNGDirectoryTestSuite.java:88)
at org.apache.maven.surefire.testng.TestNGProvider.invoke(TestNGProvider.java:137)
at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:385)
at org.apache.maven.surefire.booter.ForkedBooter.execute(ForkedBooter.java:162)
at org.apache.maven.surefire.booter.ForkedBooter.run(ForkedBooter.java:507)
at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:495)
Caused by: com.clickhouse.client.api.ServerException: Code: 618. DB::Exception: LZ4 decompression failed. LZ4F version: 100. Error: ERROR_frameType_unknown. (LZ4_DECODER_FAILED) (version 26.8.2.7 (official build)) (queryId= )
|
@chernser Thank you for looking again. I checked the 1. The failure is present on
The PR's own run 34433980294 (job 2. Why it is a different bug — request path, not response path. Error 618 is raised by the server while it decompresses the request body we uploaded. This PR changes how a response is requested and read. The two directions do not meet. The cause is filed as #3075: on the multipart path ( The other four reds ( 3. How would you like #3075 handled? The fix is small and lives in the same file (drop
4. Your earlier review is addressed on this head (
Ready for another look when you have a moment. |




Description
Fixes #3105.
client-v2requested a compressed response with thecompress=1framing of the HTTP interface, whose codec theserver chooses on its own, and decoded every block as ClickHouse-framed LZ4. ClickHouse
26.9(#108786) changed that codec from
LZ4toZSTD(3), soevery compressed read failed with
Invalid LZ4 magic byte: '-112'(0x90is the ZSTD method byte).The framed output follows the built-in default codec, so no client-side knob restores
LZ4- verified against26.9.1.954:compress=1answers with method byte0x90andnetwork_compression_method=LZ4does not change it(the server PR states the path has "no runtime rollback").
The algorithm is therefore now part of the request instead of a property of the server: the client asks for a
response with the HTTP content coding of the algorithm it will decode, so a compressed body always uses the
algorithm the client selected, on every server version.
Changes
CompressionAlgorithm(LZ4,ZSTD,GZIP,NONE) defines the available algorithms, each with its contentcoding.
client.compression_algorithm, defaultLZ4, withClient.Builder#compressionAlgorithmand theper-operation
QuerySettings#compressionAlgorithm/InsertSettings#compressionAlgorithm. The name and thecontent-coding token are both accepted, in any case.
Accept-Encoding: <coding>andenable_http_compression=1;compress=1is not requested any more. A response without a content coding is read as a plain body.
useHttpCompression, as before; the ClickHouse framing ofa request compressed without it stays LZ4, and the client warns when the two contradict.
NONEdisables compression of both directions.zstd-jnistays aprovideddependency ofclickhouse-jdbcand is not added toclient-v2. An application that selectsZSTDdeclares the dependency itself.CHANGELOG.md(breaking-changes entry and bug fix), the 0.11.0 migration guide(
docs/releases/0_11_0.md), anddocs/features.md.Test
QueryServerContentCompressionTests#testQueryWithCompressionAlgorithmreads 1000 rows over the default(non-http-compression) path for each algorithm. Against
26.9.1.954all four rows of the data provider fail withInvalid LZ4 magic byte: '-112'on the unpatched transport and pass with the fix; the suite is100/100on26.7.3.19and99/100on26.9.1.954, where the only failure is the pre-existingtestSettingsNotChanged(theX-ClickHouse-Formatprecedence issue [client-v2, jdbc-v2] A caller-supplied FORMAT clause is silently ignored on ClickHouse 26.8+ (X-ClickHouse-Format wins) #3070 / client-v2 0.10.0: getTableSchema fails on ClickHouse 26.8 with "Failed to parse columnnulldefined by type 'null'" (works on 26.7) #3094, also red onmain).CompressionRequestUnitTestpins the request contract with a mock server:compress=1is not sent, the responseis requested with the content coding of the algorithm,
NONErequests no compression, an operation overrides theclient, an option set as a name is accepted, and
useHttpCompressionsetsContent-Encoding.ClientConfigPropertiesTestcovers parsing of every algorithm in both spellings, the default, and the rejectionof an unknown algorithm.
mvn -pl client-v2 test: 676/676.mvn -pl jdbc-v2,packages/clickhouse-jdbc-all,clickhouse-jdbc -ambuilds.Pre-PR validation gate
26.9, passes with the fix)AGENTS.mdanddocs/changes_checklist.md(new config property: uniquekey, value type matches parsing, default parses, focused tests; enum constant appended, not inserted)
CHANGELOG.md, the 0.11.0 migration guide anddocs/features.mdupdated