Repository navigation
fix(kafka): verify external dead-letter topics and check topics at consumer startup - #225
Merged
Merged
Conversation
…nsumer startup `declare` on an external() topology created, and expanded to eight partitions, a dead-letter topic infra owns. It now verifies it the way it verifies the main topic: a missing one is `ShoveError::Topology`, and the topic is never created or expanded. Owned topologies are unchanged. Only the registry path ran `declare`, so every other consumer start (`run`, the supervisor, `run_batch`, `run_fifo`, a broadcast subscription, `run_dlq`) looped silently on a missing external main topic, and lost every dead letter to a missing dead-letter topic. Each now runs one metadata probe before its reconnect loop and before any record is consumed: the external main topic, and the dead-letter topic wherever the path publishes to it. A missing topic is `Topology` naming it; an unreachable broker stays on the retry path. The probe builds no producer, and registry members skip it.
…oducer creates topics A dead-letter publish after an infra-owned DLQ disappeared would recreate it with broker defaults when the client's producer auto-creates topics. declare and every consumer start that publishes dead letters now refuse that combination with a Topology error.
…ic on a real broker
…e external topic in a dead-letter drain A publish-only declare never writes a dead letter, so the producer auto-create refusal moves to the consumer registry's register. A dead-letter drain of an external topology now probes its main topic as well as the DLQ.
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
shove-docs | 25c4974 | Commit Preview URL Branch Preview URL |
Oct 10 2026, 04:43 AM |
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
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.
Problem
A Kafka consumer accepted topologies it could not honour and failed silently at runtime:
declaretreated the DLQ as shove-owned. It created the DLQ, and expanded an existing one to 8 partitions, even when infra owns it (including adlq_namedpointing at an infra topic). An expansion cannot be undone, and it remaps keys to partitions.run, supervisor members,run_batch,run_fifo, broadcast subscriptions andrun_dlqnever declare.UnknownTopicOrPartitionto a retryableConnection, and the reconnect wrapper looped on it forever.with_producer_auto_create_topics(true), a dead-letter publish after an external DLQ disappeared would recreate the infra-owned topic with broker defaults.Change
declareon an external topology: the DLQ is verify-only. A missing one isShoveError::Topology("must be provisioned"); it is never created or expanded. Owned topologies are unchanged.Startup check on every non-registry consumer path: a metadata-only probe that runs once, before the reconnect loop, so no record is consumed first. It checks:
A missing topic is
ShoveError::Topologynaming it. An unreachable broker or a metadata timeout is not: it staysConnectionand is retried under the consumer's backoff andmax_reconnect_attempts, and shutdown ends it. Owned main topics are never probed, because another process may still declare them. Registry members skip the probe, sinceregisteralready declared the topology.Auto-create refusal: an external topology with a DLQ is refused at
registerand at every consumer start that publishes dead letters when the client's producer auto-creates topics. A publish-onlydeclareis not refused.The probe builds no producer. It is a metadata request with
allow.auto.create.topics=false, so it creates nothing and needs onlyDescribe. An external topology without a DLQ still never requests a producer id.Behaviour changes for existing users
Each of these turns a silent runtime failure into a startup error:
declareand without producer auto-create, now fails at startup if the DLQ is missing. Before, its dead letters were lost.max_reconnect_attemptsset, an unreachable broker takes up to the metadata timeout (about 10 s) per attempt to fail at startup, instead of failing immediately.No public API changes. The only new item is a
test-support-gated, doc-hidden probe counter.Tests
Every refusal test below was red on
mainand is green now.tests/kafka_startup_topic_checks_mock.rs, new, on librdkafka's mock cluster with no Docker. It covers each entry point refusing a missing external topic or DLQ, the auto-create refusal on direct run, batch and register, and the dead-letter drain checking both external topics. It also covers the cases that must keep starting:It adds an unreachable-broker guard (not
Topology), and checks that registry members make no probe.tests/kafka_integration.rs, real Kafka:auto.create.topics.enable=true.tests/kafka_consume_only_mock.rs: the startup check builds no producer.Fifteen existing tests now provision their DLQ before consuming without
declare.Ran locally:
cargo nextest runwith the mock, Kafka integration, broadcast, rebalance, batch, auto-create, offset-reset and metrics suites;--liband--no-default-features;cargo fmt -- --check;cargo clippy -D warningson all features, no default features, and the Kafka--libfeature sets.The ssl/gssapi/msk-iam feature set does not link locally; CI covers it.