Kafka¶
Status: Native on both the source and sink sides — including the exactly-once transactional commit hand-off — across the full value-format family Flink itself supports for Kafka (JSON, CSV, Avro, Avro Confluent, Protobuf, raw, and the CDC JSON envelope dialects). A specific, enumerated set of table shapes, key configurations, properties, and security schemes falls back to stock Flink; those conditions are listed precisely below rather than summarized as "partial" support. Per-format decode/encode detail (byte-level option handling, type coverage, parity corner cases) lives on each format's own page, linked throughout.
Unlike the Parquet connector — where the native side only produces Parquet bytes and Flink's own filesystem plugins, credentials, and exactly-once commit own everything else — Kafka is native end to end on both source and sink, including the exactly-once producer identity that Flink's committer resumes.
Source¶
The source requires both the streamfusion-kafka connector extension and the matching
streamfusion-* format JAR to be installed; either missing is a planner fallback to Flink's own
connector, never a linkage failure. See Deployment for the JAR list.
Value formats¶
A kafka table's value format decodes natively for insert-only streams in
JSON, CSV, raw, bare Avro,
Avro Confluent, and Protobuf, and for full
changelog streams in the four CDC JSON envelope dialects
(debezium-json, canal-json, maxwell-json, ogg-json) and debezium-avro-confluent. Any other
value format falls back. Format-specific decode wrinkles (protobuf presence/enum handling,
ignore-parse-errors granularity, and so on) are covered on each format's page, not here.
Keyed tables¶
Exactly one keyed shape decodes on the fused native source path: key.format = 'raw' with a
single key field, over an insert-only JSON/CSV/raw value format. It reproduces Flink's merge
semantics exactly — a raw key produces one key row per record, a NULL Kafka key keeps the record
with a NULL key column, EXCEPT_KEY/ALL projections resolve as Flink's factory does (value
fields win the ALL overlap), and a JSON value fanning a top-level array into N rows shares the
record's key across all of them. Projection pushdown is off for keyed tables.
Falling back: any key.format other than raw; multiple key.fields (raw is single-column); an
ALL projection combined with key.fields-prefix (Flink's own rejection); a non-UTF-8
key.raw.charset; a keyed CDC value format; and keyed Avro or Protobuf value formats (their schema
providers derive from the gated row type).
Metadata columns¶
A metadata column (ts TIMESTAMP_LTZ(3) METADATA FROM 'timestamp' and its kin) falls back on
every decoded path, insert-only and CDC alike — the connector, not the value decode, fills it, so a
value-only decode would silently emit NULL. A metadata column that's declared but never
referenced also declines, since Flink keeps it in the scan's output regardless of projection.
Computed columns are unaffected on insert-only paths — the planner projects them above the native
scan.
Bounded scanning¶
Only scan.bounded.mode of unbounded or latest-offset runs natively; any other bounded mode
falls back.
Consumer properties and security¶
Consumer property translation is fail-closed: every supplied properties.* key is classified
against kafka-clients 4.2's ConsumerConfig, and anything unclassified is a fallback — vanilla
Flink instead forwards arbitrary keys and lets kafka-clients merely warn. Java-owned coordination
keys (client.id.prefix, discovery and commit-on-checkpoint options, deserializers,
group-membership/assignment machinery that never engages under manual assignment, reader-call
tuning like max.poll.records) are honored on the JVM side and deliberately not forwarded to the
native layer.
Security running natively: PLAINTEXT, SSL, SASL_PLAINTEXT, and SASL_SSL with the PLAIN, SCRAM-SHA-256, or SCRAM-SHA-512 mechanisms — credentials from a Plain/Scram JAAS module, PEM trust and key material, and (when SSL is on with no configured truststore) a probe of the platform CA bundle to match the JVM's default trust. The native DSO links OpenSSL statically, so no system OpenSSL install is required.
Falling back: client plugins (interceptor.classes, metric reporters, config.providers,
security.providers); sasl.login.*/sasl.oauthbearer.* (OAUTHBEARER needs the Java client);
SASL/GSSAPI (Kerberos) entirely — every sasl.kerberos.* key, a Krb5LoginModule, an explicit
GSSAPI mechanism, or a SASL protocol with no mechanism set (the Java default is GSSAPI) — because
cyrus-sasl is deliberately excluded from the portable native build; JVM-specific SSL machinery
(protocol/algorithm selection, engine factories, inline PEM strings, JKS/PKCS#12 stores needing
conversion); SASL credentials missing from sasl.jaas.config; metadata.recovery.*; an
unrecognized JAAS login module; an unmappable auto.offset.reset (e.g. by_duration:...); and any
unknown key. See Configuration for the streamfusion.kafka.prefetch-mb
off-heap budget this source draws on, and Native Kafka source
for why the source is built this way.
Watermarks¶
A pushed WATERMARK clause regenerates inside the native source for the reproducible shapes:
bounded out-of-orderness (rt or rt - INTERVAL const) over a physical rowtime column, or
TO_TIMESTAMP_LTZ(bigintCol, 3) (the epoch-millis computed-rowtime idiom), with periodic emit and
scan.watermark.idle-timeout/table.exec.source.idle-timeout honored. Decode happens in-poll, so
the split reader stamps each per-partition batch's max rowtime as its record timestamp and Flink's
own per-split machinery (one generator per split, min combination, idleness) reproduces the pushed
strategy exactly — the same shared path the Fluss source uses.
Falling back: an on-event emit strategy, watermark alignment, SOURCE_WATERMARK(), or any other
rowtime expression; a watermarked CDC table (CDC decodes in an operator downstream of Flink's
source, which can't regenerate watermarks); and a watermarked table the native source can't consume
at all (the decode-operator path regenerates no watermarks either).
Sink¶
Delivery modes¶
A fixed-topic kafka/upsert-kafka table runs its data plane natively in one of two shapes,
selected by the sink's own delivery guarantee:
- Exactly-once, incremental transaction naming — the whole data plane is native. Rust
serializes and produces each Arrow batch inside a librdkafka transaction, flushes it at the
checkpoint barrier, and surfaces the transaction's
(producer id, epoch)as a real FlinkKafkaCommittable; Flink's own committer, checkpoint-completion commit, recovery re-commit, and probing abort on restore remain the host's contract unchanged. The producer identity is read authoritatively from the transaction coordinator (DescribeTransactions, KIP-664), so this path requires brokers ≥ 3.0 — on an older cluster the writer fails at startup with the broker's admin error, and the table must use the Java sink instead. none/at-least-once delivery — serialization is native; the resulting key/value bytes feed Flink's unmodifiedKafkaSink, whose producer, parallelism, and metrics contracts apply verbatim.
Table shapes and formats¶
| Table shape | Supported formats | Changelog admitted |
|---|---|---|
plain kafka, insert-only value |
JSON, CSV, Protobuf, raw, Avro, Avro Confluent | insert-only only |
plain kafka, CDC value format |
debezium-avro-confluent, the four CDC JSON envelopes (debezium-json, canal-json, maxwell-json, ogg-json) |
full changelog — the CDC format itself requests UPDATE_BEFORE from the planner |
upsert-kafka |
JSON, CSV, Avro, Avro Confluent, Protobuf, raw for key and value (resolved independently, so key and value formats can differ) | upsert changelog: INSERT/UPDATE_AFTER carry a serialized value, UPDATE_BEFORE/DELETE carry a Kafka tombstone |
A plain kafka table may carry a PRIMARY KEY alongside a CDC value format (Flink permits a PK
there) and, as in Flink, produces no key output without an explicit key.format. upsert-kafka
keeps Flink's own rejection of CDC value formats — that's parity with Flink, not a fallback.
Producer properties¶
Producer properties are normalized against kafka-clients' own defaults and translated to librdkafka one classified key at a time; a property outside the classification is a planner fallback, never silently dropped or ignored.
Fallback causes¶
Shape and ability fallbacks, independent of wire format:
- an upsert-materialized sink — when Flink decides the upsert changelog can arrive out of
order it bakes a stateful
SinkUpsertMaterializerinto its own sink translation, which a substituted native sink would silently drop; - a keyed ordinary
kafkatable (onlyupsert-kafkacarries a key at the sink); sink.parallelismon a changelog input — stock Flink keys that edge by primary key (or rejects the plan without one) so a key's changes stay ordered across the parallelism change, and the native sink would only rebalance whole batches; insert-only inputs keepsink.parallelismnative, since there's no per-key ordering to preserve; a non-default partitioner, sink-side buffer flushing, writable metadata, or any other sink ability beyond plain production;- an explicit key/value projection, key prefix, or
EXCEPT_KEYvalue projection; - a connector library built without a given format's encode arm — probed at plan time, so a missing optional format module is a fallback rather than a runtime dispatch failure.
Connectivity and transactional fallbacks:
- missing
properties.bootstrap.servers, or exactly-once configured without a transactional ID prefix; - exactly-once with a transaction naming strategy other than
INCREMENTING(POOLINGis a planned follow-up); - exactly-once with a producer property the native translator can't guarantee parity for: an
unclassified/unknown key,
transactional.id(owned by Flink's own naming strategy),enable.idempotence=false, custom serializers/partitioner/interceptors, non-default adaptive partitioning orpartitioner.ignore.keys=true,batch.size=0, JKS or other non-PEM security material, SASL/GSSAPI (Kerberos), or a SASL mechanism outside PLAIN/SCRAM-SHA-256/SCRAM-SHA-512.
Per-format value fallbacks (an unsupported value/key format, option values Flink's own factory rejects, and format-specific type-family limits) are documented on each format's page linked above.