ruststream_rdkafka/lib.rs
1//! Apache Kafka broker for the [RustStream](https://github.com/powersemmi/ruststream) messaging
2//! framework, backed by [`rdkafka`] / librdkafka.
3//!
4//! # Transport model
5//!
6//! A subscription is one consumer joining one consumer group on one topic; [`KafkaTopic`]
7//! describes it, and the bare-string `#[subscriber("orders")]` form consumes the topic named
8//! `orders` through [`KafkaBroker::default_group`]. On the publish side
9//! [`OutgoingMessage::name`](ruststream::OutgoingMessage) is the destination topic, and a
10//! [`PARTITION_KEY_HEADER`] header becomes the record's native key, so Kafka itself keeps
11//! per-key ordering.
12//!
13//! Settlement follows Kafka's committed-position model instead of per-message frames; the
14//! [`Commit`] mode picks how:
15//!
16//! - [`Commit::Auto`] (the default): librdkafka auto-commit; `ack` and both `nack` forms are
17//! advisory no-ops (the position is stored when a message is handed to the application, so
18//! `nack(true)` does not cause a redelivery).
19//! - [`Commit::Tracked`]: precise at-least-once. `ack` settles its delivery and the stored
20//! position advances across everything settled below it, staying correct under concurrent
21//! handler lanes and across offset gaps (transaction markers, compacted topics).
22//! `nack(false)` settles the offset (drop); `nack(true)` leaves it unsettled, so Kafka
23//! redelivers from the committed position on the next fetch of the partition.
24//!
25//! Configuration delegates to librdkafka: unset options mean librdkafka defaults, and the raw
26//! `config(key, value)` passthroughs on the broker, the producer, and the descriptor reach
27//! every property this crate does not surface as a typed option.
28//!
29//! # The lifecycle ladder
30//!
31//! Each state of the connection is its own type, so out-of-order use does not compile:
32//!
33//! ```text
34//! KafkaBroker::new(servers) configuration only, synchronous, no I/O
35//! .connect() -> ConnectedKafkaBroker the live connection: subscriptions and publishers
36//! .shutdown() -> ClosedKafkaBroker the terminal witness, carrying the flush result
37//! ```
38//!
39//! [`KafkaBroker::new`] being synchronous is what lets a service compose with the synchronous
40//! `#[ruststream::app]` builder; the runtime calls `connect` once at startup.
41//!
42//! Publishers follow the same split: [`KafkaPublish`] (and its transactional and per-partition
43//! transitions) is pure policy, constructible anywhere, with no publish surface at all; pairing
44//! it with the connected broker produces the live [`KafkaPublisher`]. Handles paired before a
45//! shutdown keep aliasing the closed connection, so their operations report
46//! [`KafkaError::Closed`] rather than succeeding against a dead connection.
47//!
48//! One publisher stands outside that split, on purpose: [`KafkaRetryPublisher`], minted from
49//! the unconnected broker for builder-time wiring that takes a live publisher rather than a
50//! policy - `retry_via`, the deferred republish behind `retry_after` that Kafka needs because
51//! it has no native delayed redelivery. See its documentation.
52//!
53//! [`rdkafka`]: https://docs.rs/rdkafka
54
55#![forbid(unsafe_code)]
56
57mod broker;
58mod convert;
59mod distribution;
60mod eos;
61mod error;
62mod message;
63mod publisher;
64mod retry;
65mod seek;
66mod subscriber;
67mod topic;
68mod tracker;
69
70#[cfg(feature = "avro")]
71pub mod avro;
72pub mod context;
73#[cfg(feature = "protobuf")]
74pub mod protobuf;
75#[cfg(feature = "schema-registry")]
76pub mod schema_registry;
77#[cfg(feature = "testing")]
78pub mod testing;
79
80pub use broker::{ClosedKafkaBroker, ConnectedKafkaBroker, KafkaBroker};
81pub use distribution::RoundRobin;
82pub use eos::{EOS_SOURCE_HEADER, EosPipeline, EosReplies, KafkaEosPublish, SourceOffset};
83pub use error::KafkaError;
84pub use message::{KafkaMessage, PARTITION_HEADER, PARTITION_KEY_HEADER};
85pub use publisher::{
86 KafkaPartitionedPublish, KafkaPublish, KafkaPublisher, KafkaRetryPublisher,
87 KafkaTransactionalPublish, KafkaTransactionalPublisher, TransactionalPartitions,
88};
89#[cfg(feature = "schema-registry")]
90pub use schema_registry::{
91 RegisteredSchema, SchemaFrame, SchemaRegistry, SchemaType, SubjectStrategy,
92};
93pub use seek::{KafkaPosition, KafkaSeeker};
94
95pub use retry::{
96 DLQ_SOURCE_OFFSET_HEADER, DLQ_SOURCE_PARTITION_HEADER, DLQ_SOURCE_TOPIC_HEADER,
97 RETRY_COUNT_HEADER, Retry,
98};
99pub use subscriber::KafkaSubscriber;
100pub use topic::{Assignment, Commit, KafkaTopic, LaneKey, StartOffset};