Skip to content

Native Kafka source: Flink semantic parity and remaining tails #16

Description

@jordepic

What

The remaining semantic-parity, feature, and productionization work for the native Kafka source. The source is built, default-on, and decisively faster than the shallow path on every format (divergences/19, docs/optimizations.md); per-partition watermarks/idleness and specific-offsets/topic-pattern startup have shipped.

Correctness parity comes before expanding coverage or publishing further exactly-once Kafka comparisons.

Flink checkpoint and consume semantics — highest priority

  • Bounded stopping must use the Kafka consumer position and enforce the captured stop while building each partition batch. Today progress comes from the last delivered non-null payload and a whole poll batch can overshoot the enumerator's captured latest offset. Match Flink's KafkaPartitionSplitReader: emit only records whose offsets are below the stop, finish when the actual consumer position reaches it, and finish immediately when start >= stop. Cover empty partitions, records appended after stop capture, and multiple partitions independently.
  • Make bounded read_committed completion control-record aware. Aborted transactional records and control markers advance Kafka's position without producing application records; they must still let the split reach its stop rather than hang at the tail.
  • Propagate Kafka consumer failures. The native callback currently discards all librdkafka error events. Classify benign events such as partition EOF separately and fail the Flink source for the same fatal/non-retriable authentication, authorization, unknown-topic, and offset failures that stock Flink surfaces.
  • Handle null Kafka values/tombstones without losing offset progress. Every consumed record must advance its split position. Preserve a nullable body for formats that can represent it, and otherwise invoke the format's normal failure/ignore-parse-errors behavior instead of silently dropping it in the consumer callback.
  • Match group-offset startup and auto.offset.reset exactly. In particular, absent committed offsets with no explicit reset strategy must fail (none) like Flink rather than inherit librdkafka's latest default. Test explicit earliest/latest/none plus missing and out-of-range committed offsets.
  • Commit offsets on completed checkpoints when Flink's commit.offsets.on.checkpoint is enabled. Recovery correctness continues to come exclusively from Flink checkpoint state; Kafka commits are external monitoring state and a commit failure must not fail or corrupt a completed checkpoint. Include finished splits and discard pending commits from older checkpoints exactly as KafkaSourceReader does.
  • Implement partition pause/resume parity. Delegate FLIP-27 split pause/resume to librdkafka so watermark alignment remains valid if the planner later admits aligned Kafka watermarks. Keep the current planner fallback until this is covered.

Checkpoint-performance validation

  • Measure Arrow-batch checkpoint granularity. A native source advances checkpoint state once per emitted Arrow batch rather than once per Kafka record. This is result-correct, but can enlarge the replay unit and delay a barrier behind one batch. Benchmark checkpoint duration and recovery replay at representative batch sizes; retain the columnar batch boundary unless evidence justifies a smaller native batch—not a rowwise hot path.
  • Add a real FLIP-27 failover harness with a transactional verifier. 2ebf873 completes a checkpoint, fails and restarts the task, restores native split state, and proves 10,000 records remain unique through an exactly-once Kafka sink plus read_committed verifier.
  • Add the ordinary checkpointed-sink failover variant. 3b2c22a restores the native source into Flink's checkpointed filesystem sink and proves 10,000 records remain gap-free and unique independently of Kafka transaction visibility.

Kafka observability and identity parity

  • Register Kafka source metrics. At minimum expose current/committed offsets, records lag, bytes in, and librdkafka consumer metrics with stable Flink metric-group names where practical.
  • Remove the unconditional synthetic group ID. Preserve Flink's group-ID requirements and client-ID behavior: require a group for group-offset startup/commits, but do not manufacture group membership for earliest/latest/specific/timestamp modes. Verify ACL behavior and client identity.
  • Verify cancellation and split-change latency. wakeUp() currently relies on the 100 ms bounded poll timeout. Keep it only if cancellation, checkpoint shutdown, and partition discovery/reassignment tests show Flink-equivalent responsiveness; otherwise wire librdkafka queue/consumer wakeup.

Feature gaps

  • key.format / multi-format tables — currently fall back.
  • SASL/SSL at runtime — the bundled librdkafka is built without SSL/SASL. A SASL/SSL cluster needs the ssl + gssapi-vendored rdkafka features (heavier build); the config translator already emits the config, so this is a build-features + verification item.
  • Bounded modes beyond latest-offset — still fall back.

Productionize the fast path

  • Linux mimalloc full validation — the symbol-level link-alias check passed in a container build (aliases bind library-locally, mirroring the macOS result), but a full suite + benchmark-ladder run on a Linux host is still outstanding before recommending the feature there unreservedly.
  • Multi-broker parallel fetch — unmeasured (single-broker Testcontainers only). librdkafka fetches per-broker on parallel threads vs the Java client's single network thread, so this can only widen the native win — but confirm on a 3-broker cluster before quoting it.
  • Revisit mimalloc v3 when its dlopen'd lazy thread-init stabilizes (crashes in _mi_subproc today — divergences/19). v3's cross-thread free is markedly faster than v2's on the raw-consume pattern (5.3M/s vs 3.4M/s micro).
  • Upstream librdkafka ARM hardware CRC32C (crc32c.c is x86 SSE4.2 only). Would let us re-pin check.crcs=true at ~no cost on ARM.

Acceptance gate

Before this issue's semantic-parity section is considered complete, run native-vs-stock Flink parity tests for:

  • normal checkpoint/restore and rescale;
  • completed-checkpoint Kafka offset visibility and failed asynchronous commits;
  • empty bounded partitions and start == stop;
  • appends after latest-offset capture, proving no overshoot;
  • read_committed aborted transactions/control records at the bounded tail;
  • null values at the middle and tail of a partition;
  • broker/auth/authorization/invalid-offset failures;
  • group-offset startup with each reset strategy;
  • watermark-alignment pause/resume; and
  • bounded and unbounded multi-partition consumption.

All performance measurements must use the release bench profile with the Kafka and mimalloc features, and must report checkpoint/recovery costs alongside steady-state throughput.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions