Kumar Mallikarjuna created FLINK-40348:
------------------------------------------

             Summary: ZSTD fails at runtime with the SQL Kafka Connector
                 Key: FLINK-40348
                 URL: https://issues.apache.org/jira/browse/FLINK-40348
             Project: Flink
          Issue Type: Bug
          Components: Connectors / Kafka
            Reporter: Kumar Mallikarjuna


The {{flink-sql-connector-kafka}} uber jar bundles the Kafka classes that 
_invoke_
the compression codecs, but none of the codec libraries themselves. Any table 
using
{{'properties.compression.type' = 'zstd'}} fails at runtime.

The same connector consumed as a plain Maven dependency works: {{kafka-clients}}
declares {{zstd-jni}} as a mandatory ({{{}optional=false{}}}) runtime 
dependency, so a
DataStream user gets it transitively. Only the SQL uber jar drops it. Since 
that jar
exists to be the self-contained equivalent of the Maven dependency, the two 
should not
diverge on which codecs work.
h2. Cause

The uber jar's shade configuration uses an include whitelist:
{code:xml}
<artifactSet>
    <includes>
        <include>org.apache.flink:flink-connector-kafka</include>
        <include>org.apache.kafka:*</include>
    </includes>
</artifactSet>
{code}
{{kafka-clients}} declares its three compression providers at {{runtime}} scope:
 * {{com.github.luben:zstd-jni}}
 * {{org.lz4:lz4-java}}
 * {{org.xerial.snappy:snappy-java}}

An include whitelist is exhaustive, so none of the three are bundled, while
{{org.apache.kafka.common.compress.ZstdCompression}} (and {{ZstdFactory}} on 
older lines), which reference them, are.
h2. Symptom
{code:java}
java.lang.NoClassDefFoundError: com/github/luben/zstd/BufferPool
    at 
org.apache.kafka.common.compress.ZstdCompression$Builder.build(ZstdCompression.java:138)
    at 
org.apache.kafka.clients.producer.KafkaProducer.configureCompression(KafkaProducer.java:559)
    at 
org.apache.kafka.clients.producer.KafkaProducer.<init>(KafkaProducer.java:410)
Caused by: java.lang.ClassNotFoundException: com.github.luben.zstd.BufferPool
{code}
On Kafka clients >= 3.8 this throws during producer construction.
h2. Why this only appears for zstd
 * gzip: JDK provides
 * snappy and lz4: {{flink-dist}} bundles them
 * zstd: Flink doesn't need zstd, so zstd is the only codec with no provider 

h2. Are codecs expected to be provided at runtime?

No such contract in the docs and Kafka expects this to be handled differently:

*1. Kafka declares them mandatory.* In {{kafka-clients}} 3.4.0/3.9.1/4.2.0, all 
three (minus gzip) codecs are {{scope=runtime}} with {{{}optional=false{}}}. An 
optional-false runtime dependency is delivered transitively to every Maven 
consumer. {_}Had Kafka intended them to be user-supplied it would mark them 
{{optional=true}}{_}.

*2. The same connector behaves differently depending only on packaging.* The
non-shaded {{org.apache.flink:flink-connector-kafka}} declares 
{{kafka-clients}} at {{{}compile{}}}/{{{}optional=false{}}}, so a DataStream 
user who depends on it via Maven gets {{zstd-jni}} transitively and ZSTD works. 
_A SQL user who drops the uber jar into {{lib/}} gets the same connector code 
with the codec silently removed._ The uber jar
exists precisely to be the self-contained equivalent of that Maven dependency, 
so the
two should not diverge on which compression codecs function.

*3. Nothing in the build or the docs expresses such a contract.* The codecs are 
lost
by omission from an include whitelist, not by an explicit {{{}<exclude>{}}}. 
There is no
comment, and no reference to zstd/luben in any pom in the repository. _The 
connector_
_documentation tells users to add the connector jar; it does not mention codec 
jars._
h2. Steps to reproduce
 # Start a Flink cluster and SQL client with {{flink-sql-connector-kafka}} in 
{{{}lib/{}}}.
 # Create a table against any Kafka topic with
{{'properties.compression.type' = 'zstd'}} in the WITH clause.
 # {{INSERT INTO}} it. The job fails with the {{NoClassDefFoundError}} above.

h2. Proposed fix

Add the codec provider(s) to the shade {{{}<includes>{}}}, {*}unrelocated{*}:
{code:xml}
<include>com.github.luben:zstd-jni</include>
{code}
Two constraints:
 * It must not be relocated. The shaded Kafka bytecode references the original
{{com.github.luben.zstd}} package, and zstd-jni's JNI loader resolves its native
library off that package name, relocating breaks it twice over.
 * Flink's {{NoticeFileChecker}} requires bundled dependencies to be listed, so
{{META-INF/NOTICE}} needs the corresponding entry.

h3. Trade-off

{{zstd-jni}} is ~7 MB because it ships prebuilt natives for ~10 platforms. That 
more
than doubles the 5.3 MB connector jar. Options:
 # Bundle {{zstd-jni}} whole.
 # Document that users must supply codec jars themselves. However, this 
contradicts having an "uber" jar for SQL (users do not assemble a classpath).

h3. On lz4 and snappy

Bundle zstd only, the others are currently being shipped already with the Flink 
build.
h2. Context

The whitelist pattern dates to FLINK-11026, which reworked SQL connector jar 
creation.
It is correct for the compile-scope graph; the {{{}runtime{}}}-scope codec 
providers were
simply never in scope for that change.



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to