Repository navigation
Conversation
A Kafka listener source turns a cluster replica into a one-broker Kafka cluster: unmodified Kafka producers write to it, and a batch is acknowledged once it is durable in the source's log, a persist shard. Tables decode their topic's records from the log. The doc covers the SQL, the protocol server, the log and exactly-once delivery for idempotent producers, tables per topic, validation on produce, the replica's schema registry, networking through balancerd, Materialize Kafka sinks as producers, measurements, and the alternatives considered. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Some registries serve the Confluent-compatible API under a path, for example Apicurio Registry at /apis/ccompat/v7. A schema registry connection's URL may now include a path, which becomes the base of every request. Previously CREATE CONNECTION rejected any URL with a non-empty path. This is independent of the Kafka listener source and can land on its own. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A new crate, mz-kafka-server, that presents itself as a single-broker Kafka cluster to producers, built on the pure-Rust kafka-protocol crate. It knows nothing about timely or storage: a consumer registers a topic and receives a stream of produced batches to acknowledge. It serves ApiVersions, Metadata, Produce, InitProducerId, SASL/PLAIN, the transaction APIs, and CreateTopics, ListOffsets and Fetch for compacted topics, as Materialize Kafka sinks need for progress. It implements the broker's idempotence rules, validates batches through an optional per-topic validator with KIP-467 record errors, bounds each topic's queue of batches in bytes, and serves a read-only Confluent-compatible schema registry for topics with inline Avro schemas. Tested with librdkafka producers, a transactional producer, an admin client and a read_committed consumer. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Adds the Kafka listener source connection and its SQL, behind the
enable_kafka_listener_source feature flag:
CREATE SOURCE events FROM KAFKA LISTENER (
TOPIC 'events', SASL USERNAME 'u', SASL PASSWORD SECRET s,
VALIDATE ON PRODUCE)
KEY FORMAT TEXT VALUE FORMAT JSON ENVELOPE UPSERT;
CREATE SOURCE cdc FROM KAFKA LISTENER (TOPIC PREFIX 'cdc.public.');
CREATE TABLE orders FROM SOURCE cdc (REFERENCE "cdc.public.orders")
FORMAT AVRO USING CONFLUENT SCHEMA REGISTRY CONNECTION csr
ENVELOPE DEBEZIUM;
Planning reuses the Kafka source's formats, envelopes, metadata columns and
export details. A source with a TOPIC PREFIX has no relation of its own, and
each table names a topic under the prefix; a topic can feed several tables.
Purification records the defaulted TOPIC so that renaming the source does not
change the topic.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The source registers its topics with the replica's Kafka server and appends every batch it accepts to its log, a persist shard derived from the remap shard, with its records and, for idempotent producers, a row for deduplication. A batch is acknowledged once its append commits, in about 0.1 s, and its records are then emitted to every table of its topic. After a restart, or for a table created later, each table is sent the log's records from its upper. Resent batches of idempotent producers are acknowledged with their original offsets, so they get exactly-once delivery. The log commits in groups: at most one append starts per 50 ms and two are in flight, and each append is split into batches that build in parallel and commit together. Records not yet committed by every table of their topic are bounded by an eighth of the replica's memory. VALIDATE ON PRODUCE decodes each batch with its table's decoders on the producer's connection before accepting it. The storage controller finalizes the log when the source is dropped. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Behind the enable_replica_kafka_listener dyncfg, replicas get a kafka port (9092) and a kafka-registry port (8081), and clusterd runs the Kafka server and the schema registry on them. Adding ports restarts StatefulSets, so the flag applies only to replicas provisioned after it is set. Orchestrators gain endpoints: a stable address routed to one process of a service. The Kubernetes orchestrator implements one as a ClusterIP Service selecting the target pod; the process orchestrator needs none, since local clients reach each replica's TCP proxy. The controller keeps each cluster's <cluster>-kafka and <cluster>-kafka-registry endpoints pointed at process 0 of the replica that runs its listener sources. environmentd's --kafka-listener-advertised-addr-template gives the address replicas advertise in Metadata responses. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
balancerd gains optional Kafka and schema registry listeners. It terminates
TLS with the certificate it already serves, reads the first SNI label as
<environment>-<cluster ID>, resolves a backend from a template such as
cluster-{cluster}-kafka.<namespace>.svc.cluster.local:9092, and proxies the
connection without parsing Kafka. Only labels ending in a cluster ID are
routed.
orchestratord does not pass the new flags yet.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Scripts for a local environmentd, not meant for CI as they are: - demo.py: a source, a producer and a query, with the replica's endpoint. - exactly_once.py: resends identical batches over the raw Kafka protocol around replica restarts and checks offsets and rows, then runs librdkafka idempotent producers through a restart. - tables.py: a TOPIC PREFIX source with tables added mid-stream, including a second table on a topic with records, through a replica kill. - registry.py: Confluent's Avro serializer against the replica's registry. - perf.py: throughput, ack latency, and replica CPU and memory per replica size and number of producers. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Proof of concept, not for merge as is. Opened as a draft to share the design and the code.
A
KAFKA LISTENERsource turns a cluster replica into a one-broker Kafka cluster. Unmodified Kafka producers (librdkafka, the Java client, Debezium Server, Kafka Connect, a Materialize Kafka sink) write to it by pointingbootstrap.serversat the replica, with no Kafka cluster in between. The design doc,doc/developer/design/20261030_kafka_listener_source.md, is the place to start.How it works
mz-kafka-server, on the pure-Rustkafka-protocolcrate) on a newkafkaport. Ingestion stays on the cluster, scaling and isolated like every other source.SourceMessages a Kafka source would, so formats, envelopes (UPSERT,DEBEZIUM,NONE),INCLUDEmetadata and reclocking are shared.VALIDATE ON PRODUCErejects records a table could not decode; a read-only Confluent-compatible schema registry per replica for inline Avro schemas; transactions and compacted progress topics, so a Materialize Kafka sink can write into a listener exactly once; and SNI routing through balancerd for Kubernetes. orchestratord wiring is not done.Both features are behind flags that default to off:
enable_kafka_listener_sourceandenable_replica_kafka_listener.Commits
Grouped by area, for review. Lines are added plus deleted. Code includes comments; tests count integration tests, test data, and in-file
#[cfg(test)]modules.ccsr: allow a base path in schema registry URLs. Independent of the rest, and can land on its own.mz-kafka-server: the protocol server, idempotence, transactions, validation hook, schema registryCREATE SOURCE .. FROM KAFKA LISTENER,TOPIC PREFIXwithCREATE TABLE .. FROM SOURCE, the connection typeNot part of a production change:
misc/kafka-listener(commit 8), for local runs, not CICargo.lock, generatedMeasured
On an AWS m6id.16xlarge with 16 producers sending flat out:
Writing every record to persist twice costs about twice the replica CPU and memory of acknowledging at the table's commit. The extra is persist's encoding and compaction of the log. The design doc covers this and what it flags for persist.
Tests
mz-kafka-serverintegration tests with librdkafka producers: compression, idempotence, SASL, transactions, compacted topics, validation mid-sequence, the schema registry, and the byte-bounded topic queue.test/sqllogictest/kafka_listener.sltfor the SQL.misc/kafka-listenerscripts for exactly-once delivery through restarts, tables per topic, the registry, and throughput.🤖 Generated with Claude Code