pub struct Consumer { /* private fields */ }Expand description
Manual-assignment fetch client.
Implementations§
Source§impl Consumer
impl Consumer
Sourcepub async fn connect(bootstrap: impl Into<String>) -> Result<Self>
pub async fn connect(bootstrap: impl Into<String>) -> Result<Self>
Connect with default config to one bootstrap server.
Examples found in repository?
6async fn main() -> partitionline::Result<()> {
7 let bootstrap = std::env::var("KAFKA_BOOTSTRAP").unwrap_or_else(|_| "127.0.0.1:9092".into());
8 let topic = std::env::var("KAFKA_TOPIC").unwrap_or_else(|_| "partitionline".into());
9 let mut consumer = Consumer::connect(bootstrap).await?;
10 consumer.assign_topic(topic, 0).await?;
11 loop {
12 for rec in consumer.fetch().await? {
13 println!(
14 "{}-{}@{} bytes={}",
15 rec.topic,
16 rec.partition,
17 rec.offset,
18 rec.value.as_ref().map(|v| v.len()).unwrap_or(0)
19 );
20 }
21 }
22}More examples
8async fn main() -> partitionline::Result<()> {
9 let bootstrap = std::env::var("KAFKA_BOOTSTRAP").unwrap_or_else(|_| "127.0.0.1:9092".into());
10 let topic = std::env::var("KAFKA_TOPIC").unwrap_or_else(|_| "partitionline".into());
11 let mut consumer = Consumer::connect(bootstrap).await?;
12 consumer.assign_topic(topic, 0).await?;
13 let assigned = consumer.assignment();
14 println!("assigned {assigned:?}");
15 if let Some(tp) = assigned.first().cloned() {
16 consumer.pause([tp.clone()]);
17 println!("paused {:?}", consumer.paused());
18 let recs = consumer.fetch().await?;
19 println!("while paused: {} records", recs.len());
20 consumer.resume([tp]);
21 }
22 let recs = consumer.fetch().await?;
23 println!("after resume: {} records", recs.len());
24 consumer.close().await?;
25 Ok(())
26}10async fn main() -> partitionline::Result<()> {
11 let bootstrap = std::env::var("KAFKA_BOOTSTRAP").unwrap_or_else(|_| "127.0.0.1:9092".into());
12 let topic = std::env::var("KAFKA_TOPIC").unwrap_or_else(|_| "partitionline".into());
13 let mut consumer = Consumer::connect(bootstrap).await?;
14 consumer.assign_topic(topic, 0).await?;
15 let wakeup = consumer.wakeup_handle();
16 drop(tokio::spawn(async move {
17 tokio::signal::ctrl_c().await.unwrap_or(());
18 wakeup.wakeup();
19 }));
20 loop {
21 match consumer.fetch().await {
22 Ok(recs) => {
23 for rec in recs {
24 println!("{}-{}@{}", rec.topic, rec.partition, rec.offset);
25 }
26 }
27 Err(partitionline::Error::Wakeup) => break,
28 Err(e) => return Err(e),
29 }
30 }
31 consumer.close().await?;
32 Ok(())
33}11async fn main() -> partitionline::Result<()> {
12 let bootstrap = std::env::var("KAFKA_BOOTSTRAP").unwrap_or_else(|_| "127.0.0.1:9092".into());
13 let topic = std::env::var("KAFKA_TOPIC").unwrap_or_else(|_| "partitionline".into());
14
15 let producer = Producer::new(
16 ProducerConfig::bootstrap([bootstrap.clone()]).linger(std::time::Duration::ZERO),
17 )
18 .await?;
19 let md = producer
20 .send(ProduceRecord::to(topic.clone()).value(&b"hello"[..]))
21 .await?;
22 println!("produced {}-{}@{}", md.topic, md.partition, md.offset);
23 producer.close().await?;
24
25 let mut consumer = Consumer::connect(bootstrap.clone()).await?;
26 consumer.assign(&topic, 0, 0).await?;
27 let begin = consumer.beginning_offsets([(topic.as_str(), 0)]).await?;
28 let end = consumer.end_offsets([(topic.as_str(), 0)]).await?;
29 let lag = consumer.current_lag((topic.as_str(), 0)).await?;
30 println!("begin={begin:?} end={end:?} lag={lag:?}");
31 consumer.close().await?;
32
33 let mut group = ConsumerGroup::join(
34 ConsumerConfig::bootstrap([bootstrap]).max_wait_ms(500),
35 "partitionline-offsets",
36 topic.clone(),
37 )
38 .await?;
39 let recs = group.poll().await?;
40 if let Some(rec) = recs.first() {
41 let tp = TopicPartition::new(&rec.topic, rec.partition);
42 group
43 .commit_with_metadata([(
44 tp,
45 OffsetAndMetadata::with_metadata(rec.offset + 1, "example"),
46 )])
47 .await?;
48 }
49 for (tp, md) in group.committed().await? {
50 println!("{tp} committed={} meta={}", md.offset, md.metadata);
51 }
52 group.leave().await?;
53 Ok(())
54}9async fn main() -> partitionline::Result<()> {
10 let bootstrap = std::env::var("KAFKA_BOOTSTRAP").unwrap_or_else(|_| "127.0.0.1:9092".into());
11 let topic = std::env::var("KAFKA_TOPIC").unwrap_or_else(|_| "partitionline".into());
12 let format = std::env::var("FORMAT").unwrap_or_else(|_| "log".into());
13
14 let producer = Producer::new(
15 ProducerConfig::bootstrap([bootstrap.clone()]).linger(std::time::Duration::ZERO),
16 )
17 .await?;
18 let md = producer
19 .send(ProduceRecord::to(topic.clone()).value(&b"hello"[..]))
20 .await?;
21 let pm = producer.metrics();
22
23 if format == "prom" {
24 println!("# HELP partitionline_produce_records_acked Produce records acked");
25 println!("# TYPE partitionline_produce_records_acked counter");
26 println!("partitionline_produce_records_acked {}", pm.records_acked);
27 println!("# HELP partitionline_produce_ack_p99_seconds Produce ack p99 latency");
28 println!("# TYPE partitionline_produce_ack_p99_seconds gauge");
29 println!(
30 "partitionline_produce_ack_p99_seconds {}",
31 (pm.ack_latency.p99_nanos as f64) / 1_000_000_000.0
32 );
33 } else {
34 println!(
35 "produced {}-{}@{} queued={} acked={} bytes={} ack_us={} p50_us={} p99_us={} topics={}",
36 md.topic,
37 md.partition,
38 md.offset,
39 pm.records_queued,
40 pm.records_acked,
41 pm.bytes_queued,
42 pm.ack_latency.mean_nanos().unwrap_or(0) / 1000,
43 pm.ack_latency.p50_nanos / 1000,
44 pm.ack_latency.p99_nanos / 1000,
45 pm.topics.len()
46 );
47 }
48 producer.close().await?;
49
50 let mut consumer = Consumer::connect(bootstrap).await?;
51 consumer.assign(&topic, 0, 0).await?;
52 let recs = consumer.fetch().await?;
53 let cm = consumer.metrics();
54 if format == "prom" {
55 println!("# HELP partitionline_fetch_rounds Fetch rounds completed");
56 println!("# TYPE partitionline_fetch_rounds counter");
57 println!("partitionline_fetch_rounds {}", cm.fetch_rounds);
58 println!("# HELP partitionline_fetch_p99_seconds Fetch round p99 latency");
59 println!("# TYPE partitionline_fetch_p99_seconds gauge");
60 println!(
61 "partitionline_fetch_p99_seconds {}",
62 (cm.fetch_latency.p99_nanos as f64) / 1_000_000_000.0
63 );
64 } else {
65 println!(
66 "fetched {} records rounds={} bytes={} errors={} fetch_us={} p50_us={} p99_us={} topics={}",
67 recs.len(),
68 cm.fetch_rounds,
69 cm.bytes_fetched,
70 cm.fetch_errors,
71 cm.fetch_latency.mean_nanos().unwrap_or(0) / 1000,
72 cm.fetch_latency.p50_nanos / 1000,
73 cm.fetch_latency.p99_nanos / 1000,
74 cm.topics.len()
75 );
76 }
77 consumer.close().await?;
78 Ok(())
79}Sourcepub async fn new(cfg: ConsumerConfig) -> Result<Self>
pub async fn new(cfg: ConsumerConfig) -> Result<Self>
Connect using cfg. Negotiates ApiVersions and optional SASL/TLS.
Examples found in repository?
20async fn main() -> partitionline::Result<()> {
21 let bootstrap = std::env::var("KAFKA_BOOTSTRAP").unwrap_or_else(|_| "127.0.0.1:9092".into());
22 let topic = std::env::var("KAFKA_TOPIC").unwrap_or_else(|_| "partitionline".into());
23 let mut consumer =
24 Consumer::new(ConsumerConfig::bootstrap([bootstrap]).interceptor(SkipHeartbeats)).await?;
25 consumer.assign_topic(topic, 0).await?;
26 loop {
27 for rec in consumer.fetch().await? {
28 println!(
29 "{}-{}@{} bytes={}",
30 rec.topic,
31 rec.partition,
32 rec.offset,
33 rec.value.as_ref().map(|v| v.len()).unwrap_or(0)
34 );
35 }
36 }
37}More examples
8async fn main() -> partitionline::Result<()> {
9 let bootstrap = std::env::var("KAFKA_BOOTSTRAP").unwrap_or_else(|_| "127.0.0.1:9092".into());
10 let topic = std::env::var("KAFKA_TOPIC").unwrap_or_else(|_| "partitionline".into());
11 let payload = b"live-roundtrip";
12
13 let mut pcfg = ProducerConfig::bootstrap([bootstrap.clone()]);
14 pcfg.linger = Duration::ZERO;
15 let producer = Producer::new(pcfg).await?;
16 let md = producer
17 .send(ProduceRecord::to(topic.clone()).value(&payload[..]))
18 .await?;
19 producer.close().await?;
20
21 let mut ccfg = ConsumerConfig::bootstrap([bootstrap]);
22 ccfg.max_wait_ms = 1000;
23 let mut consumer = Consumer::new(ccfg).await?;
24 consumer.assign(topic, md.partition, md.offset).await?;
25 let recs = consumer.fetch().await?;
26 let rec = recs
27 .iter()
28 .find(|r| r.offset == md.offset)
29 .ok_or_else(|| partitionline::Error::protocol("record not fetched"))?;
30 if rec.value.as_deref() != Some(&payload[..]) {
31 return Err(partitionline::Error::protocol("payload mismatch"));
32 }
33 println!(
34 "ok {}-{}@{} bytes={}",
35 rec.topic,
36 rec.partition,
37 rec.offset,
38 rec.value.as_ref().map(|v| v.len()).unwrap_or(0)
39 );
40 Ok(())
41}100async fn main() -> partitionline::Result<()> {
101 let bootstrap = env_or("KAFKA_BOOTSTRAP", "127.0.0.1:9092");
102 let topic = env_or("KAFKA_TOPIC", "pllat");
103 let payload = env_parse("PAYLOAD_BYTES", 100usize);
104 let warmup = env_parse("WARMUP", 1_000u64);
105 let count = env_parse("COUNT", 10_000u64);
106 let linger_ms = env_parse("LINGER_MS", 0u64);
107 let acks = env_parse("ACKS", 1i16);
108 let max_wait_ms = env_parse("MAX_WAIT_MS", 100i32);
109 let max_bytes = env_parse("MAX_BYTES", 4_096i32);
110 let min_bytes = env_parse("MIN_BYTES", 1i32);
111 let mode = env_or("MODE", "both");
112
113 let mut pcfg = ProducerConfig::bootstrap([bootstrap.clone()]);
114 pcfg.linger = Duration::from_millis(linger_ms);
115 pcfg.acks = acks;
116 pcfg.batch_records = 1;
117 pcfg.batch_bytes = payload.saturating_add(256).max(1);
118 pcfg.connections = 1;
119 pcfg.max_in_flight = 1;
120 let producer = Producer::new(pcfg).await?;
121 let value = Bytes::from(vec![b'x'; payload]);
122
123 if warmup > 0 {
124 let _ = produce_ack(&producer, &topic, &value, warmup).await?;
125 }
126
127 if mode == "produce" || mode == "both" {
128 let samples = produce_ack(&producer, &topic, &value, count).await?;
129 print_latency(
130 "produce_ack",
131 samples,
132 &format!(
133 ",\"payload_bytes\":{payload},\"acks\":{acks},\"linger_ms\":{linger_ms},\"client\":\"partitionline\""
134 ),
135 )?;
136 }
137 producer.close().await?;
138
139 if mode == "fetch" || mode == "both" {
140 if mode == "fetch" {
141 let producer = Producer::new({
142 let mut cfg = ProducerConfig::bootstrap([bootstrap.clone()]);
143 cfg.linger = Duration::ZERO;
144 cfg.acks = acks;
145 cfg.batch_records = 1;
146 cfg.connections = 1;
147 cfg.max_in_flight = 1;
148 cfg
149 })
150 .await?;
151 let _ = produce_ack(&producer, &topic, &value, count).await?;
152 producer.close().await?;
153 }
154 let mut ccfg = ConsumerConfig::bootstrap([bootstrap]);
155 ccfg.max_wait_ms = max_wait_ms;
156 ccfg.max_bytes = max_bytes;
157 ccfg.min_bytes = min_bytes;
158 let mut consumer = Consumer::new(ccfg).await?;
159 consumer.assign(&topic, 0, 0).await?;
160 let (samples, got) = fetch_rpc(&mut consumer, count).await?;
161 print_latency(
162 "fetch_rpc",
163 samples,
164 &format!(
165 ",\"consumed\":{got},\"max_wait_ms\":{max_wait_ms},\"max_bytes\":{max_bytes},\"min_bytes\":{min_bytes},\"client\":\"partitionline\""
166 ),
167 )?;
168 }
169 Ok(())
170}8async fn main() -> partitionline::Result<()> {
9 let bootstrap = std::env::var("KAFKA_BOOTSTRAP").unwrap_or_else(|_| "127.0.0.1:9092".into());
10 let topic = std::env::var("KAFKA_TOPIC").unwrap_or_else(|_| "plbench".into());
11 let count: u64 = std::env::var("COUNT")
12 .ok()
13 .and_then(|s| s.parse().ok())
14 .unwrap_or(8_000_000);
15 let max_wait_ms = std::env::var("MAX_WAIT_MS")
16 .ok()
17 .and_then(|s| s.parse().ok())
18 .unwrap_or(100i32);
19 let max_bytes = std::env::var("MAX_BYTES")
20 .ok()
21 .and_then(|s| s.parse().ok())
22 .unwrap_or(16_777_216i32);
23 let min_bytes = std::env::var("MIN_BYTES")
24 .ok()
25 .and_then(|s| s.parse().ok())
26 .unwrap_or(1i32);
27
28 let mut cfg = ConsumerConfig::bootstrap([bootstrap]);
29 cfg.max_wait_ms = max_wait_ms;
30 cfg.max_bytes = max_bytes;
31 cfg.min_bytes = min_bytes;
32 if let Ok(ca_path) = std::env::var("TLS_CA_PEM") {
33 let mut tls = TlsConfig {
34 ca_pem: Some(tokio::fs::read(&ca_path).await.map_err(|e| {
35 partitionline::Error::protocol(format!("read TLS_CA_PEM {ca_path}: {e}"))
36 })?),
37 ..TlsConfig::default()
38 };
39 if let Ok(name) = std::env::var("TLS_SERVER_NAME") {
40 if !name.is_empty() {
41 tls.server_name = Some(name);
42 }
43 }
44 cfg.tls = Some(tls);
45 }
46 let mech = std::env::var("SASL_MECHANISM").unwrap_or_else(|_| "PLAIN".into());
47 if mech == "OAUTHBEARER" {
48 let principal = std::env::var("SASL_OAUTH_PRINCIPAL").unwrap_or_else(|_| "alice".into());
49 cfg.sasl_oauthbearer = Some(principal);
50 } else if let (Ok(user), Ok(pass)) = (
51 std::env::var("SASL_USERNAME"),
52 std::env::var("SASL_PASSWORD"),
53 ) {
54 match mech.as_str() {
55 "SCRAM-SHA-256" => cfg.sasl_scram = Some((user, pass)),
56 "SCRAM-SHA-512" => cfg.sasl_scram_sha512 = Some((user, pass)),
57 "PLAIN" => cfg.sasl_plain = Some((user, pass)),
58 other => {
59 return Err(partitionline::Error::protocol(format!(
60 "unknown SASL_MECHANISM {other}"
61 )));
62 }
63 }
64 }
65
66 let mut consumer = Consumer::new(cfg).await?;
67 consumer.assign_topic(&topic, 0).await?;
68 let assigned = consumer.assignment().len();
69 let start = Instant::now();
70 let mut got = 0u64;
71 let mut empty = 0u32;
72 while got < count {
73 let recs = consumer.fetch().await?;
74 if recs.is_empty() {
75 empty += 1;
76 if empty > 600 {
77 return Err(partitionline::Error::Timeout);
78 }
79 continue;
80 }
81 empty = 0;
82 got += recs.len() as u64;
83 }
84 let elapsed = start.elapsed().as_secs_f64();
85 let rec_s = got as f64 / elapsed.max(1e-9);
86 println!(
87 "{{\"consumed\":{got},\"elapsed_s\":{elapsed:.6},\"consumed_rec_s\":{rec_s:.3},\"partitions\":{assigned},\"max_wait_ms\":{max_wait_ms},\"max_bytes\":{max_bytes}}}"
88 );
89 Ok(())
90}Sourcepub async fn assign(
&mut self,
topic: impl Into<String>,
partition: i32,
offset: i64,
) -> Result<()>
pub async fn assign( &mut self, topic: impl Into<String>, partition: i32, offset: i64, ) -> Result<()>
Assign one partition at offset. Replaces a previous offset for the same pair.
Java assign calls crate::protocol::group::Topic::validate on the topic name.
Examples found in repository?
8async fn main() -> partitionline::Result<()> {
9 let bootstrap = std::env::var("KAFKA_BOOTSTRAP").unwrap_or_else(|_| "127.0.0.1:9092".into());
10 let topic = std::env::var("KAFKA_TOPIC").unwrap_or_else(|_| "partitionline".into());
11 let payload = b"live-roundtrip";
12
13 let mut pcfg = ProducerConfig::bootstrap([bootstrap.clone()]);
14 pcfg.linger = Duration::ZERO;
15 let producer = Producer::new(pcfg).await?;
16 let md = producer
17 .send(ProduceRecord::to(topic.clone()).value(&payload[..]))
18 .await?;
19 producer.close().await?;
20
21 let mut ccfg = ConsumerConfig::bootstrap([bootstrap]);
22 ccfg.max_wait_ms = 1000;
23 let mut consumer = Consumer::new(ccfg).await?;
24 consumer.assign(topic, md.partition, md.offset).await?;
25 let recs = consumer.fetch().await?;
26 let rec = recs
27 .iter()
28 .find(|r| r.offset == md.offset)
29 .ok_or_else(|| partitionline::Error::protocol("record not fetched"))?;
30 if rec.value.as_deref() != Some(&payload[..]) {
31 return Err(partitionline::Error::protocol("payload mismatch"));
32 }
33 println!(
34 "ok {}-{}@{} bytes={}",
35 rec.topic,
36 rec.partition,
37 rec.offset,
38 rec.value.as_ref().map(|v| v.len()).unwrap_or(0)
39 );
40 Ok(())
41}More examples
11async fn main() -> partitionline::Result<()> {
12 let bootstrap = std::env::var("KAFKA_BOOTSTRAP").unwrap_or_else(|_| "127.0.0.1:9092".into());
13 let topic = std::env::var("KAFKA_TOPIC").unwrap_or_else(|_| "partitionline".into());
14
15 let producer = Producer::new(
16 ProducerConfig::bootstrap([bootstrap.clone()]).linger(std::time::Duration::ZERO),
17 )
18 .await?;
19 let md = producer
20 .send(ProduceRecord::to(topic.clone()).value(&b"hello"[..]))
21 .await?;
22 println!("produced {}-{}@{}", md.topic, md.partition, md.offset);
23 producer.close().await?;
24
25 let mut consumer = Consumer::connect(bootstrap.clone()).await?;
26 consumer.assign(&topic, 0, 0).await?;
27 let begin = consumer.beginning_offsets([(topic.as_str(), 0)]).await?;
28 let end = consumer.end_offsets([(topic.as_str(), 0)]).await?;
29 let lag = consumer.current_lag((topic.as_str(), 0)).await?;
30 println!("begin={begin:?} end={end:?} lag={lag:?}");
31 consumer.close().await?;
32
33 let mut group = ConsumerGroup::join(
34 ConsumerConfig::bootstrap([bootstrap]).max_wait_ms(500),
35 "partitionline-offsets",
36 topic.clone(),
37 )
38 .await?;
39 let recs = group.poll().await?;
40 if let Some(rec) = recs.first() {
41 let tp = TopicPartition::new(&rec.topic, rec.partition);
42 group
43 .commit_with_metadata([(
44 tp,
45 OffsetAndMetadata::with_metadata(rec.offset + 1, "example"),
46 )])
47 .await?;
48 }
49 for (tp, md) in group.committed().await? {
50 println!("{tp} committed={} meta={}", md.offset, md.metadata);
51 }
52 group.leave().await?;
53 Ok(())
54}100async fn main() -> partitionline::Result<()> {
101 let bootstrap = env_or("KAFKA_BOOTSTRAP", "127.0.0.1:9092");
102 let topic = env_or("KAFKA_TOPIC", "pllat");
103 let payload = env_parse("PAYLOAD_BYTES", 100usize);
104 let warmup = env_parse("WARMUP", 1_000u64);
105 let count = env_parse("COUNT", 10_000u64);
106 let linger_ms = env_parse("LINGER_MS", 0u64);
107 let acks = env_parse("ACKS", 1i16);
108 let max_wait_ms = env_parse("MAX_WAIT_MS", 100i32);
109 let max_bytes = env_parse("MAX_BYTES", 4_096i32);
110 let min_bytes = env_parse("MIN_BYTES", 1i32);
111 let mode = env_or("MODE", "both");
112
113 let mut pcfg = ProducerConfig::bootstrap([bootstrap.clone()]);
114 pcfg.linger = Duration::from_millis(linger_ms);
115 pcfg.acks = acks;
116 pcfg.batch_records = 1;
117 pcfg.batch_bytes = payload.saturating_add(256).max(1);
118 pcfg.connections = 1;
119 pcfg.max_in_flight = 1;
120 let producer = Producer::new(pcfg).await?;
121 let value = Bytes::from(vec![b'x'; payload]);
122
123 if warmup > 0 {
124 let _ = produce_ack(&producer, &topic, &value, warmup).await?;
125 }
126
127 if mode == "produce" || mode == "both" {
128 let samples = produce_ack(&producer, &topic, &value, count).await?;
129 print_latency(
130 "produce_ack",
131 samples,
132 &format!(
133 ",\"payload_bytes\":{payload},\"acks\":{acks},\"linger_ms\":{linger_ms},\"client\":\"partitionline\""
134 ),
135 )?;
136 }
137 producer.close().await?;
138
139 if mode == "fetch" || mode == "both" {
140 if mode == "fetch" {
141 let producer = Producer::new({
142 let mut cfg = ProducerConfig::bootstrap([bootstrap.clone()]);
143 cfg.linger = Duration::ZERO;
144 cfg.acks = acks;
145 cfg.batch_records = 1;
146 cfg.connections = 1;
147 cfg.max_in_flight = 1;
148 cfg
149 })
150 .await?;
151 let _ = produce_ack(&producer, &topic, &value, count).await?;
152 producer.close().await?;
153 }
154 let mut ccfg = ConsumerConfig::bootstrap([bootstrap]);
155 ccfg.max_wait_ms = max_wait_ms;
156 ccfg.max_bytes = max_bytes;
157 ccfg.min_bytes = min_bytes;
158 let mut consumer = Consumer::new(ccfg).await?;
159 consumer.assign(&topic, 0, 0).await?;
160 let (samples, got) = fetch_rpc(&mut consumer, count).await?;
161 print_latency(
162 "fetch_rpc",
163 samples,
164 &format!(
165 ",\"consumed\":{got},\"max_wait_ms\":{max_wait_ms},\"max_bytes\":{max_bytes},\"min_bytes\":{min_bytes},\"client\":\"partitionline\""
166 ),
167 )?;
168 }
169 Ok(())
170}9async fn main() -> partitionline::Result<()> {
10 let bootstrap = std::env::var("KAFKA_BOOTSTRAP").unwrap_or_else(|_| "127.0.0.1:9092".into());
11 let topic = std::env::var("KAFKA_TOPIC").unwrap_or_else(|_| "partitionline".into());
12 let format = std::env::var("FORMAT").unwrap_or_else(|_| "log".into());
13
14 let producer = Producer::new(
15 ProducerConfig::bootstrap([bootstrap.clone()]).linger(std::time::Duration::ZERO),
16 )
17 .await?;
18 let md = producer
19 .send(ProduceRecord::to(topic.clone()).value(&b"hello"[..]))
20 .await?;
21 let pm = producer.metrics();
22
23 if format == "prom" {
24 println!("# HELP partitionline_produce_records_acked Produce records acked");
25 println!("# TYPE partitionline_produce_records_acked counter");
26 println!("partitionline_produce_records_acked {}", pm.records_acked);
27 println!("# HELP partitionline_produce_ack_p99_seconds Produce ack p99 latency");
28 println!("# TYPE partitionline_produce_ack_p99_seconds gauge");
29 println!(
30 "partitionline_produce_ack_p99_seconds {}",
31 (pm.ack_latency.p99_nanos as f64) / 1_000_000_000.0
32 );
33 } else {
34 println!(
35 "produced {}-{}@{} queued={} acked={} bytes={} ack_us={} p50_us={} p99_us={} topics={}",
36 md.topic,
37 md.partition,
38 md.offset,
39 pm.records_queued,
40 pm.records_acked,
41 pm.bytes_queued,
42 pm.ack_latency.mean_nanos().unwrap_or(0) / 1000,
43 pm.ack_latency.p50_nanos / 1000,
44 pm.ack_latency.p99_nanos / 1000,
45 pm.topics.len()
46 );
47 }
48 producer.close().await?;
49
50 let mut consumer = Consumer::connect(bootstrap).await?;
51 consumer.assign(&topic, 0, 0).await?;
52 let recs = consumer.fetch().await?;
53 let cm = consumer.metrics();
54 if format == "prom" {
55 println!("# HELP partitionline_fetch_rounds Fetch rounds completed");
56 println!("# TYPE partitionline_fetch_rounds counter");
57 println!("partitionline_fetch_rounds {}", cm.fetch_rounds);
58 println!("# HELP partitionline_fetch_p99_seconds Fetch round p99 latency");
59 println!("# TYPE partitionline_fetch_p99_seconds gauge");
60 println!(
61 "partitionline_fetch_p99_seconds {}",
62 (cm.fetch_latency.p99_nanos as f64) / 1_000_000_000.0
63 );
64 } else {
65 println!(
66 "fetched {} records rounds={} bytes={} errors={} fetch_us={} p50_us={} p99_us={} topics={}",
67 recs.len(),
68 cm.fetch_rounds,
69 cm.bytes_fetched,
70 cm.fetch_errors,
71 cm.fetch_latency.mean_nanos().unwrap_or(0) / 1000,
72 cm.fetch_latency.p50_nanos / 1000,
73 cm.fetch_latency.p99_nanos / 1000,
74 cm.topics.len()
75 );
76 }
77 consumer.close().await?;
78 Ok(())
79}Sourcepub async fn assign_many(
&mut self,
starts: impl IntoIterator<Item = (impl Into<TopicPartition>, i64)>,
) -> Result<()>
pub async fn assign_many( &mut self, starts: impl IntoIterator<Item = (impl Into<TopicPartition>, i64)>, ) -> Result<()>
Replace the assignment with these (partition, offset) pairs.
Sourcepub async fn assign_partitions(
&mut self,
partitions: impl IntoIterator<Item = impl Into<TopicPartition>>,
) -> Result<()>
pub async fn assign_partitions( &mut self, partitions: impl IntoIterator<Item = impl Into<TopicPartition>>, ) -> Result<()>
Replace the assignment (Java assign(Collection)).
Offsets come from ConsumerConfig::auto_offset_reset via ListOffsets
(earliest or latest). crate::AutoOffsetReset::None is an error
(a manual consumer has no committed offsets). An empty list drops the
assignment (Self::unassign). Each topic name is checked with
crate::protocol::group::Topic::validate.
Waits up to ConsumerConfig::request_timeout. For a one-shot
timeout, use Self::assign_partitions_timeout.
Sourcepub async fn assign_partitions_timeout(
&mut self,
partitions: impl IntoIterator<Item = impl Into<TopicPartition>>,
timeout: Duration,
) -> Result<()>
pub async fn assign_partitions_timeout( &mut self, partitions: impl IntoIterator<Item = impl Into<TopicPartition>>, timeout: Duration, ) -> Result<()>
Self::assign_partitions with a one-shot timeout for ListOffsets.
Sourcepub fn unassign(&mut self)
pub fn unassign(&mut self)
Drop every assigned partition (Java unsubscribe for a manual consumer).
Sourcepub async fn assign_topic(
&mut self,
topic: impl Into<String>,
offset: i64,
) -> Result<()>
pub async fn assign_topic( &mut self, topic: impl Into<String>, offset: i64, ) -> Result<()>
Assign every partition of topic at offset (from metadata).
Java assign calls crate::protocol::group::Topic::validate on the topic name.
Examples found in repository?
6async fn main() -> partitionline::Result<()> {
7 let bootstrap = std::env::var("KAFKA_BOOTSTRAP").unwrap_or_else(|_| "127.0.0.1:9092".into());
8 let topic = std::env::var("KAFKA_TOPIC").unwrap_or_else(|_| "partitionline".into());
9 let mut consumer = Consumer::connect(bootstrap).await?;
10 consumer.assign_topic(topic, 0).await?;
11 loop {
12 for rec in consumer.fetch().await? {
13 println!(
14 "{}-{}@{} bytes={}",
15 rec.topic,
16 rec.partition,
17 rec.offset,
18 rec.value.as_ref().map(|v| v.len()).unwrap_or(0)
19 );
20 }
21 }
22}More examples
20async fn main() -> partitionline::Result<()> {
21 let bootstrap = std::env::var("KAFKA_BOOTSTRAP").unwrap_or_else(|_| "127.0.0.1:9092".into());
22 let topic = std::env::var("KAFKA_TOPIC").unwrap_or_else(|_| "partitionline".into());
23 let mut consumer =
24 Consumer::new(ConsumerConfig::bootstrap([bootstrap]).interceptor(SkipHeartbeats)).await?;
25 consumer.assign_topic(topic, 0).await?;
26 loop {
27 for rec in consumer.fetch().await? {
28 println!(
29 "{}-{}@{} bytes={}",
30 rec.topic,
31 rec.partition,
32 rec.offset,
33 rec.value.as_ref().map(|v| v.len()).unwrap_or(0)
34 );
35 }
36 }
37}8async fn main() -> partitionline::Result<()> {
9 let bootstrap = std::env::var("KAFKA_BOOTSTRAP").unwrap_or_else(|_| "127.0.0.1:9092".into());
10 let topic = std::env::var("KAFKA_TOPIC").unwrap_or_else(|_| "partitionline".into());
11 let mut consumer = Consumer::connect(bootstrap).await?;
12 consumer.assign_topic(topic, 0).await?;
13 let assigned = consumer.assignment();
14 println!("assigned {assigned:?}");
15 if let Some(tp) = assigned.first().cloned() {
16 consumer.pause([tp.clone()]);
17 println!("paused {:?}", consumer.paused());
18 let recs = consumer.fetch().await?;
19 println!("while paused: {} records", recs.len());
20 consumer.resume([tp]);
21 }
22 let recs = consumer.fetch().await?;
23 println!("after resume: {} records", recs.len());
24 consumer.close().await?;
25 Ok(())
26}10async fn main() -> partitionline::Result<()> {
11 let bootstrap = std::env::var("KAFKA_BOOTSTRAP").unwrap_or_else(|_| "127.0.0.1:9092".into());
12 let topic = std::env::var("KAFKA_TOPIC").unwrap_or_else(|_| "partitionline".into());
13 let mut consumer = Consumer::connect(bootstrap).await?;
14 consumer.assign_topic(topic, 0).await?;
15 let wakeup = consumer.wakeup_handle();
16 drop(tokio::spawn(async move {
17 tokio::signal::ctrl_c().await.unwrap_or(());
18 wakeup.wakeup();
19 }));
20 loop {
21 match consumer.fetch().await {
22 Ok(recs) => {
23 for rec in recs {
24 println!("{}-{}@{}", rec.topic, rec.partition, rec.offset);
25 }
26 }
27 Err(partitionline::Error::Wakeup) => break,
28 Err(e) => return Err(e),
29 }
30 }
31 consumer.close().await?;
32 Ok(())
33}8async fn main() -> partitionline::Result<()> {
9 let bootstrap = std::env::var("KAFKA_BOOTSTRAP").unwrap_or_else(|_| "127.0.0.1:9092".into());
10 let topic = std::env::var("KAFKA_TOPIC").unwrap_or_else(|_| "plbench".into());
11 let count: u64 = std::env::var("COUNT")
12 .ok()
13 .and_then(|s| s.parse().ok())
14 .unwrap_or(8_000_000);
15 let max_wait_ms = std::env::var("MAX_WAIT_MS")
16 .ok()
17 .and_then(|s| s.parse().ok())
18 .unwrap_or(100i32);
19 let max_bytes = std::env::var("MAX_BYTES")
20 .ok()
21 .and_then(|s| s.parse().ok())
22 .unwrap_or(16_777_216i32);
23 let min_bytes = std::env::var("MIN_BYTES")
24 .ok()
25 .and_then(|s| s.parse().ok())
26 .unwrap_or(1i32);
27
28 let mut cfg = ConsumerConfig::bootstrap([bootstrap]);
29 cfg.max_wait_ms = max_wait_ms;
30 cfg.max_bytes = max_bytes;
31 cfg.min_bytes = min_bytes;
32 if let Ok(ca_path) = std::env::var("TLS_CA_PEM") {
33 let mut tls = TlsConfig {
34 ca_pem: Some(tokio::fs::read(&ca_path).await.map_err(|e| {
35 partitionline::Error::protocol(format!("read TLS_CA_PEM {ca_path}: {e}"))
36 })?),
37 ..TlsConfig::default()
38 };
39 if let Ok(name) = std::env::var("TLS_SERVER_NAME") {
40 if !name.is_empty() {
41 tls.server_name = Some(name);
42 }
43 }
44 cfg.tls = Some(tls);
45 }
46 let mech = std::env::var("SASL_MECHANISM").unwrap_or_else(|_| "PLAIN".into());
47 if mech == "OAUTHBEARER" {
48 let principal = std::env::var("SASL_OAUTH_PRINCIPAL").unwrap_or_else(|_| "alice".into());
49 cfg.sasl_oauthbearer = Some(principal);
50 } else if let (Ok(user), Ok(pass)) = (
51 std::env::var("SASL_USERNAME"),
52 std::env::var("SASL_PASSWORD"),
53 ) {
54 match mech.as_str() {
55 "SCRAM-SHA-256" => cfg.sasl_scram = Some((user, pass)),
56 "SCRAM-SHA-512" => cfg.sasl_scram_sha512 = Some((user, pass)),
57 "PLAIN" => cfg.sasl_plain = Some((user, pass)),
58 other => {
59 return Err(partitionline::Error::protocol(format!(
60 "unknown SASL_MECHANISM {other}"
61 )));
62 }
63 }
64 }
65
66 let mut consumer = Consumer::new(cfg).await?;
67 consumer.assign_topic(&topic, 0).await?;
68 let assigned = consumer.assignment().len();
69 let start = Instant::now();
70 let mut got = 0u64;
71 let mut empty = 0u32;
72 while got < count {
73 let recs = consumer.fetch().await?;
74 if recs.is_empty() {
75 empty += 1;
76 if empty > 600 {
77 return Err(partitionline::Error::Timeout);
78 }
79 continue;
80 }
81 empty = 0;
82 got += recs.len() as u64;
83 }
84 let elapsed = start.elapsed().as_secs_f64();
85 let rec_s = got as f64 / elapsed.max(1e-9);
86 println!(
87 "{{\"consumed\":{got},\"elapsed_s\":{elapsed:.6},\"consumed_rec_s\":{rec_s:.3},\"partitions\":{assigned},\"max_wait_ms\":{max_wait_ms},\"max_bytes\":{max_bytes}}}"
88 );
89 Ok(())
90}Sourcepub fn assignment(&self) -> Vec<TopicPartition>
pub fn assignment(&self) -> Vec<TopicPartition>
Assigned partitions (Java assignment). Offsets are Self::positions.
Examples found in repository?
8async fn main() -> partitionline::Result<()> {
9 let bootstrap = std::env::var("KAFKA_BOOTSTRAP").unwrap_or_else(|_| "127.0.0.1:9092".into());
10 let topic = std::env::var("KAFKA_TOPIC").unwrap_or_else(|_| "partitionline".into());
11 let mut consumer = Consumer::connect(bootstrap).await?;
12 consumer.assign_topic(topic, 0).await?;
13 let assigned = consumer.assignment();
14 println!("assigned {assigned:?}");
15 if let Some(tp) = assigned.first().cloned() {
16 consumer.pause([tp.clone()]);
17 println!("paused {:?}", consumer.paused());
18 let recs = consumer.fetch().await?;
19 println!("while paused: {} records", recs.len());
20 consumer.resume([tp]);
21 }
22 let recs = consumer.fetch().await?;
23 println!("after resume: {} records", recs.len());
24 consumer.close().await?;
25 Ok(())
26}More examples
8async fn main() -> partitionline::Result<()> {
9 let bootstrap = std::env::var("KAFKA_BOOTSTRAP").unwrap_or_else(|_| "127.0.0.1:9092".into());
10 let topic = std::env::var("KAFKA_TOPIC").unwrap_or_else(|_| "plbench".into());
11 let count: u64 = std::env::var("COUNT")
12 .ok()
13 .and_then(|s| s.parse().ok())
14 .unwrap_or(8_000_000);
15 let max_wait_ms = std::env::var("MAX_WAIT_MS")
16 .ok()
17 .and_then(|s| s.parse().ok())
18 .unwrap_or(100i32);
19 let max_bytes = std::env::var("MAX_BYTES")
20 .ok()
21 .and_then(|s| s.parse().ok())
22 .unwrap_or(16_777_216i32);
23 let min_bytes = std::env::var("MIN_BYTES")
24 .ok()
25 .and_then(|s| s.parse().ok())
26 .unwrap_or(1i32);
27
28 let mut cfg = ConsumerConfig::bootstrap([bootstrap]);
29 cfg.max_wait_ms = max_wait_ms;
30 cfg.max_bytes = max_bytes;
31 cfg.min_bytes = min_bytes;
32 if let Ok(ca_path) = std::env::var("TLS_CA_PEM") {
33 let mut tls = TlsConfig {
34 ca_pem: Some(tokio::fs::read(&ca_path).await.map_err(|e| {
35 partitionline::Error::protocol(format!("read TLS_CA_PEM {ca_path}: {e}"))
36 })?),
37 ..TlsConfig::default()
38 };
39 if let Ok(name) = std::env::var("TLS_SERVER_NAME") {
40 if !name.is_empty() {
41 tls.server_name = Some(name);
42 }
43 }
44 cfg.tls = Some(tls);
45 }
46 let mech = std::env::var("SASL_MECHANISM").unwrap_or_else(|_| "PLAIN".into());
47 if mech == "OAUTHBEARER" {
48 let principal = std::env::var("SASL_OAUTH_PRINCIPAL").unwrap_or_else(|_| "alice".into());
49 cfg.sasl_oauthbearer = Some(principal);
50 } else if let (Ok(user), Ok(pass)) = (
51 std::env::var("SASL_USERNAME"),
52 std::env::var("SASL_PASSWORD"),
53 ) {
54 match mech.as_str() {
55 "SCRAM-SHA-256" => cfg.sasl_scram = Some((user, pass)),
56 "SCRAM-SHA-512" => cfg.sasl_scram_sha512 = Some((user, pass)),
57 "PLAIN" => cfg.sasl_plain = Some((user, pass)),
58 other => {
59 return Err(partitionline::Error::protocol(format!(
60 "unknown SASL_MECHANISM {other}"
61 )));
62 }
63 }
64 }
65
66 let mut consumer = Consumer::new(cfg).await?;
67 consumer.assign_topic(&topic, 0).await?;
68 let assigned = consumer.assignment().len();
69 let start = Instant::now();
70 let mut got = 0u64;
71 let mut empty = 0u32;
72 while got < count {
73 let recs = consumer.fetch().await?;
74 if recs.is_empty() {
75 empty += 1;
76 if empty > 600 {
77 return Err(partitionline::Error::Timeout);
78 }
79 continue;
80 }
81 empty = 0;
82 got += recs.len() as u64;
83 }
84 let elapsed = start.elapsed().as_secs_f64();
85 let rec_s = got as f64 / elapsed.max(1e-9);
86 println!(
87 "{{\"consumed\":{got},\"elapsed_s\":{elapsed:.6},\"consumed_rec_s\":{rec_s:.3},\"partitions\":{assigned},\"max_wait_ms\":{max_wait_ms},\"max_bytes\":{max_bytes}}}"
88 );
89 Ok(())
90}Sourcepub fn assigned_partitions(&self) -> Vec<TopicPartition>
pub fn assigned_partitions(&self) -> Vec<TopicPartition>
Same as Self::assignment.
Sourcepub fn positions(&self) -> Vec<(TopicPartition, i64)>
pub fn positions(&self) -> Vec<(TopicPartition, i64)>
Assigned partitions with their next fetch offsets.
Sourcepub fn position(&self, topic: &str, partition: i32) -> Result<i64>
pub fn position(&self, topic: &str, partition: i32) -> Result<i64>
Next fetch offset for an assigned partition.
An unassigned partition is Java IllegalStateException
(You can only check the position for partitions assigned to this consumer.).
Sourcepub fn position_of(&self, partition: impl Into<TopicPartition>) -> Result<i64>
pub fn position_of(&self, partition: impl Into<TopicPartition>) -> Result<i64>
Self::position for a TopicPartition.
Sourcepub fn pause(
&mut self,
partitions: impl IntoIterator<Item = impl Into<TopicPartition>>,
)
pub fn pause( &mut self, partitions: impl IntoIterator<Item = impl Into<TopicPartition>>, )
Stop fetching these assigned partitions until resume.
Pause is stored on the consumer, so it survives group rebalance. Fetch skips a partition only while it is both assigned and paused. Records already buffered for a paused partition are held until resume.
Examples found in repository?
8async fn main() -> partitionline::Result<()> {
9 let bootstrap = std::env::var("KAFKA_BOOTSTRAP").unwrap_or_else(|_| "127.0.0.1:9092".into());
10 let topic = std::env::var("KAFKA_TOPIC").unwrap_or_else(|_| "partitionline".into());
11 let mut consumer = Consumer::connect(bootstrap).await?;
12 consumer.assign_topic(topic, 0).await?;
13 let assigned = consumer.assignment();
14 println!("assigned {assigned:?}");
15 if let Some(tp) = assigned.first().cloned() {
16 consumer.pause([tp.clone()]);
17 println!("paused {:?}", consumer.paused());
18 let recs = consumer.fetch().await?;
19 println!("while paused: {} records", recs.len());
20 consumer.resume([tp]);
21 }
22 let recs = consumer.fetch().await?;
23 println!("after resume: {} records", recs.len());
24 consumer.close().await?;
25 Ok(())
26}Sourcepub fn resume(
&mut self,
partitions: impl IntoIterator<Item = impl Into<TopicPartition>>,
)
pub fn resume( &mut self, partitions: impl IntoIterator<Item = impl Into<TopicPartition>>, )
Undo pause for these partitions.
Examples found in repository?
8async fn main() -> partitionline::Result<()> {
9 let bootstrap = std::env::var("KAFKA_BOOTSTRAP").unwrap_or_else(|_| "127.0.0.1:9092".into());
10 let topic = std::env::var("KAFKA_TOPIC").unwrap_or_else(|_| "partitionline".into());
11 let mut consumer = Consumer::connect(bootstrap).await?;
12 consumer.assign_topic(topic, 0).await?;
13 let assigned = consumer.assignment();
14 println!("assigned {assigned:?}");
15 if let Some(tp) = assigned.first().cloned() {
16 consumer.pause([tp.clone()]);
17 println!("paused {:?}", consumer.paused());
18 let recs = consumer.fetch().await?;
19 println!("while paused: {} records", recs.len());
20 consumer.resume([tp]);
21 }
22 let recs = consumer.fetch().await?;
23 println!("after resume: {} records", recs.len());
24 consumer.close().await?;
25 Ok(())
26}Sourcepub fn paused(&self) -> Vec<TopicPartition>
pub fn paused(&self) -> Vec<TopicPartition>
Assigned partitions that fetch currently skips.
Examples found in repository?
8async fn main() -> partitionline::Result<()> {
9 let bootstrap = std::env::var("KAFKA_BOOTSTRAP").unwrap_or_else(|_| "127.0.0.1:9092".into());
10 let topic = std::env::var("KAFKA_TOPIC").unwrap_or_else(|_| "partitionline".into());
11 let mut consumer = Consumer::connect(bootstrap).await?;
12 consumer.assign_topic(topic, 0).await?;
13 let assigned = consumer.assignment();
14 println!("assigned {assigned:?}");
15 if let Some(tp) = assigned.first().cloned() {
16 consumer.pause([tp.clone()]);
17 println!("paused {:?}", consumer.paused());
18 let recs = consumer.fetch().await?;
19 println!("while paused: {} records", recs.len());
20 consumer.resume([tp]);
21 }
22 let recs = consumer.fetch().await?;
23 println!("after resume: {} records", recs.len());
24 consumer.close().await?;
25 Ok(())
26}Sourcepub async fn fetch(&mut self) -> Result<ConsumerRecords>
pub async fn fetch(&mut self) -> Result<ConsumerRecords>
Fetch one round from every assigned partition that is not paused.
Returns ConsumerRecords, which indexes like a slice of
FetchedRecord. Empty when every assigned partition is paused.
Nothing assigned is Java IllegalStateException (Consumer is not subscribed to any topics or assigned any partitions).
Partitions that share a leader go in one Fetch. Distinct leaders are
fetched at the same time.
When ConsumerConfig::max_poll_records is set, extra records from
the Fetch stay buffered and are returned on the next call.
Examples found in repository?
6async fn main() -> partitionline::Result<()> {
7 let bootstrap = std::env::var("KAFKA_BOOTSTRAP").unwrap_or_else(|_| "127.0.0.1:9092".into());
8 let topic = std::env::var("KAFKA_TOPIC").unwrap_or_else(|_| "partitionline".into());
9 let mut consumer = Consumer::connect(bootstrap).await?;
10 consumer.assign_topic(topic, 0).await?;
11 loop {
12 for rec in consumer.fetch().await? {
13 println!(
14 "{}-{}@{} bytes={}",
15 rec.topic,
16 rec.partition,
17 rec.offset,
18 rec.value.as_ref().map(|v| v.len()).unwrap_or(0)
19 );
20 }
21 }
22}More examples
77async fn fetch_rpc(consumer: &mut Consumer, count: u64) -> partitionline::Result<(Vec<u64>, u64)> {
78 let mut samples = Vec::new();
79 let mut got = 0u64;
80 let mut empty = 0u32;
81 while got < count {
82 let start = Instant::now();
83 let recs = consumer.fetch().await?;
84 let elapsed = u64::try_from(start.elapsed().as_micros()).unwrap_or(u64::MAX);
85 if recs.is_empty() {
86 empty += 1;
87 if empty > 600 {
88 return Err(partitionline::Error::Timeout);
89 }
90 continue;
91 }
92 empty = 0;
93 samples.push(elapsed);
94 got += recs.len() as u64;
95 }
96 Ok((samples, got))
97}20async fn main() -> partitionline::Result<()> {
21 let bootstrap = std::env::var("KAFKA_BOOTSTRAP").unwrap_or_else(|_| "127.0.0.1:9092".into());
22 let topic = std::env::var("KAFKA_TOPIC").unwrap_or_else(|_| "partitionline".into());
23 let mut consumer =
24 Consumer::new(ConsumerConfig::bootstrap([bootstrap]).interceptor(SkipHeartbeats)).await?;
25 consumer.assign_topic(topic, 0).await?;
26 loop {
27 for rec in consumer.fetch().await? {
28 println!(
29 "{}-{}@{} bytes={}",
30 rec.topic,
31 rec.partition,
32 rec.offset,
33 rec.value.as_ref().map(|v| v.len()).unwrap_or(0)
34 );
35 }
36 }
37}8async fn main() -> partitionline::Result<()> {
9 let bootstrap = std::env::var("KAFKA_BOOTSTRAP").unwrap_or_else(|_| "127.0.0.1:9092".into());
10 let topic = std::env::var("KAFKA_TOPIC").unwrap_or_else(|_| "partitionline".into());
11 let mut consumer = Consumer::connect(bootstrap).await?;
12 consumer.assign_topic(topic, 0).await?;
13 let assigned = consumer.assignment();
14 println!("assigned {assigned:?}");
15 if let Some(tp) = assigned.first().cloned() {
16 consumer.pause([tp.clone()]);
17 println!("paused {:?}", consumer.paused());
18 let recs = consumer.fetch().await?;
19 println!("while paused: {} records", recs.len());
20 consumer.resume([tp]);
21 }
22 let recs = consumer.fetch().await?;
23 println!("after resume: {} records", recs.len());
24 consumer.close().await?;
25 Ok(())
26}10async fn main() -> partitionline::Result<()> {
11 let bootstrap = std::env::var("KAFKA_BOOTSTRAP").unwrap_or_else(|_| "127.0.0.1:9092".into());
12 let topic = std::env::var("KAFKA_TOPIC").unwrap_or_else(|_| "partitionline".into());
13 let mut consumer = Consumer::connect(bootstrap).await?;
14 consumer.assign_topic(topic, 0).await?;
15 let wakeup = consumer.wakeup_handle();
16 drop(tokio::spawn(async move {
17 tokio::signal::ctrl_c().await.unwrap_or(());
18 wakeup.wakeup();
19 }));
20 loop {
21 match consumer.fetch().await {
22 Ok(recs) => {
23 for rec in recs {
24 println!("{}-{}@{}", rec.topic, rec.partition, rec.offset);
25 }
26 }
27 Err(partitionline::Error::Wakeup) => break,
28 Err(e) => return Err(e),
29 }
30 }
31 consumer.close().await?;
32 Ok(())
33}8async fn main() -> partitionline::Result<()> {
9 let bootstrap = std::env::var("KAFKA_BOOTSTRAP").unwrap_or_else(|_| "127.0.0.1:9092".into());
10 let topic = std::env::var("KAFKA_TOPIC").unwrap_or_else(|_| "partitionline".into());
11 let payload = b"live-roundtrip";
12
13 let mut pcfg = ProducerConfig::bootstrap([bootstrap.clone()]);
14 pcfg.linger = Duration::ZERO;
15 let producer = Producer::new(pcfg).await?;
16 let md = producer
17 .send(ProduceRecord::to(topic.clone()).value(&payload[..]))
18 .await?;
19 producer.close().await?;
20
21 let mut ccfg = ConsumerConfig::bootstrap([bootstrap]);
22 ccfg.max_wait_ms = 1000;
23 let mut consumer = Consumer::new(ccfg).await?;
24 consumer.assign(topic, md.partition, md.offset).await?;
25 let recs = consumer.fetch().await?;
26 let rec = recs
27 .iter()
28 .find(|r| r.offset == md.offset)
29 .ok_or_else(|| partitionline::Error::protocol("record not fetched"))?;
30 if rec.value.as_deref() != Some(&payload[..]) {
31 return Err(partitionline::Error::protocol("payload mismatch"));
32 }
33 println!(
34 "ok {}-{}@{} bytes={}",
35 rec.topic,
36 rec.partition,
37 rec.offset,
38 rec.value.as_ref().map(|v| v.len()).unwrap_or(0)
39 );
40 Ok(())
41}Sourcepub async fn fetch_timeout(
&mut self,
timeout: Duration,
) -> Result<ConsumerRecords>
pub async fn fetch_timeout( &mut self, timeout: Duration, ) -> Result<ConsumerRecords>
Fetch with a one-shot fetch.max.wait.ms (Java poll(Duration)).
ConsumerConfig::max_wait_ms is restored afterwards. Nothing
assigned is the same Java IllegalStateException as Self::fetch.
Sourcepub fn metrics(&self) -> ConsumerMetrics
pub fn metrics(&self) -> ConsumerMetrics
Fetch counters and round latency since connect (min/mean/max and p50/p99).
crate::ConsumerMetrics::topics is one row per topic that returned at least
one record.
Examples found in repository?
9async fn main() -> partitionline::Result<()> {
10 let bootstrap = std::env::var("KAFKA_BOOTSTRAP").unwrap_or_else(|_| "127.0.0.1:9092".into());
11 let topic = std::env::var("KAFKA_TOPIC").unwrap_or_else(|_| "partitionline".into());
12 let format = std::env::var("FORMAT").unwrap_or_else(|_| "log".into());
13
14 let producer = Producer::new(
15 ProducerConfig::bootstrap([bootstrap.clone()]).linger(std::time::Duration::ZERO),
16 )
17 .await?;
18 let md = producer
19 .send(ProduceRecord::to(topic.clone()).value(&b"hello"[..]))
20 .await?;
21 let pm = producer.metrics();
22
23 if format == "prom" {
24 println!("# HELP partitionline_produce_records_acked Produce records acked");
25 println!("# TYPE partitionline_produce_records_acked counter");
26 println!("partitionline_produce_records_acked {}", pm.records_acked);
27 println!("# HELP partitionline_produce_ack_p99_seconds Produce ack p99 latency");
28 println!("# TYPE partitionline_produce_ack_p99_seconds gauge");
29 println!(
30 "partitionline_produce_ack_p99_seconds {}",
31 (pm.ack_latency.p99_nanos as f64) / 1_000_000_000.0
32 );
33 } else {
34 println!(
35 "produced {}-{}@{} queued={} acked={} bytes={} ack_us={} p50_us={} p99_us={} topics={}",
36 md.topic,
37 md.partition,
38 md.offset,
39 pm.records_queued,
40 pm.records_acked,
41 pm.bytes_queued,
42 pm.ack_latency.mean_nanos().unwrap_or(0) / 1000,
43 pm.ack_latency.p50_nanos / 1000,
44 pm.ack_latency.p99_nanos / 1000,
45 pm.topics.len()
46 );
47 }
48 producer.close().await?;
49
50 let mut consumer = Consumer::connect(bootstrap).await?;
51 consumer.assign(&topic, 0, 0).await?;
52 let recs = consumer.fetch().await?;
53 let cm = consumer.metrics();
54 if format == "prom" {
55 println!("# HELP partitionline_fetch_rounds Fetch rounds completed");
56 println!("# TYPE partitionline_fetch_rounds counter");
57 println!("partitionline_fetch_rounds {}", cm.fetch_rounds);
58 println!("# HELP partitionline_fetch_p99_seconds Fetch round p99 latency");
59 println!("# TYPE partitionline_fetch_p99_seconds gauge");
60 println!(
61 "partitionline_fetch_p99_seconds {}",
62 (cm.fetch_latency.p99_nanos as f64) / 1_000_000_000.0
63 );
64 } else {
65 println!(
66 "fetched {} records rounds={} bytes={} errors={} fetch_us={} p50_us={} p99_us={} topics={}",
67 recs.len(),
68 cm.fetch_rounds,
69 cm.bytes_fetched,
70 cm.fetch_errors,
71 cm.fetch_latency.mean_nanos().unwrap_or(0) / 1000,
72 cm.fetch_latency.p50_nanos / 1000,
73 cm.fetch_latency.p99_nanos / 1000,
74 cm.topics.len()
75 );
76 }
77 consumer.close().await?;
78 Ok(())
79}Sourcepub async fn client_instance_id(&mut self) -> Result<Uuid>
pub async fn client_instance_id(&mut self) -> Result<Uuid>
Java clientInstanceId (KIP-714 GetTelemetrySubscriptions).
Returns crate::Uuid (Java Uuid). The first call sends a zero
UUID; the broker assigns one. Later calls return the cached id
without another round-trip. Waits up to
ConsumerConfig::request_timeout. For a one-shot timeout, use
Self::client_instance_id_timeout.
Sourcepub async fn client_instance_id_timeout(
&mut self,
timeout: Duration,
) -> Result<Uuid>
pub async fn client_instance_id_timeout( &mut self, timeout: Duration, ) -> Result<Uuid>
Self::client_instance_id with a one-shot timeout (Java
clientInstanceId(Duration)).
timeout is the GetTelemetrySubscriptions RPC deadline. Cached after
the first successful call; later calls ignore timeout.
Sourcepub fn wakeup(&self)
pub fn wakeup(&self)
Interrupt Self::fetch (and group poll that calls it).
Safe to call while fetch is running on this task. From another task,
use Self::wakeup_handle.
Sourcepub fn wakeup_handle(&self) -> WakeupHandle
pub fn wakeup_handle(&self) -> WakeupHandle
Cloneable handle for Self::wakeup from another task.
Examples found in repository?
10async fn main() -> partitionline::Result<()> {
11 let bootstrap = std::env::var("KAFKA_BOOTSTRAP").unwrap_or_else(|_| "127.0.0.1:9092".into());
12 let topic = std::env::var("KAFKA_TOPIC").unwrap_or_else(|_| "partitionline".into());
13 let mut consumer = Consumer::connect(bootstrap).await?;
14 consumer.assign_topic(topic, 0).await?;
15 let wakeup = consumer.wakeup_handle();
16 drop(tokio::spawn(async move {
17 tokio::signal::ctrl_c().await.unwrap_or(());
18 wakeup.wakeup();
19 }));
20 loop {
21 match consumer.fetch().await {
22 Ok(recs) => {
23 for rec in recs {
24 println!("{}-{}@{}", rec.topic, rec.partition, rec.offset);
25 }
26 }
27 Err(partitionline::Error::Wakeup) => break,
28 Err(e) => return Err(e),
29 }
30 }
31 consumer.close().await?;
32 Ok(())
33}Sourcepub async fn close(self) -> Result<()>
pub async fn close(self) -> Result<()>
Drop fetch connections. The consumer is then gone (same as Producer::close).
Examples found in repository?
8async fn main() -> partitionline::Result<()> {
9 let bootstrap = std::env::var("KAFKA_BOOTSTRAP").unwrap_or_else(|_| "127.0.0.1:9092".into());
10 let topic = std::env::var("KAFKA_TOPIC").unwrap_or_else(|_| "partitionline".into());
11 let mut consumer = Consumer::connect(bootstrap).await?;
12 consumer.assign_topic(topic, 0).await?;
13 let assigned = consumer.assignment();
14 println!("assigned {assigned:?}");
15 if let Some(tp) = assigned.first().cloned() {
16 consumer.pause([tp.clone()]);
17 println!("paused {:?}", consumer.paused());
18 let recs = consumer.fetch().await?;
19 println!("while paused: {} records", recs.len());
20 consumer.resume([tp]);
21 }
22 let recs = consumer.fetch().await?;
23 println!("after resume: {} records", recs.len());
24 consumer.close().await?;
25 Ok(())
26}More examples
10async fn main() -> partitionline::Result<()> {
11 let bootstrap = std::env::var("KAFKA_BOOTSTRAP").unwrap_or_else(|_| "127.0.0.1:9092".into());
12 let topic = std::env::var("KAFKA_TOPIC").unwrap_or_else(|_| "partitionline".into());
13 let mut consumer = Consumer::connect(bootstrap).await?;
14 consumer.assign_topic(topic, 0).await?;
15 let wakeup = consumer.wakeup_handle();
16 drop(tokio::spawn(async move {
17 tokio::signal::ctrl_c().await.unwrap_or(());
18 wakeup.wakeup();
19 }));
20 loop {
21 match consumer.fetch().await {
22 Ok(recs) => {
23 for rec in recs {
24 println!("{}-{}@{}", rec.topic, rec.partition, rec.offset);
25 }
26 }
27 Err(partitionline::Error::Wakeup) => break,
28 Err(e) => return Err(e),
29 }
30 }
31 consumer.close().await?;
32 Ok(())
33}11async fn main() -> partitionline::Result<()> {
12 let bootstrap = std::env::var("KAFKA_BOOTSTRAP").unwrap_or_else(|_| "127.0.0.1:9092".into());
13 let topic = std::env::var("KAFKA_TOPIC").unwrap_or_else(|_| "partitionline".into());
14
15 let producer = Producer::new(
16 ProducerConfig::bootstrap([bootstrap.clone()]).linger(std::time::Duration::ZERO),
17 )
18 .await?;
19 let md = producer
20 .send(ProduceRecord::to(topic.clone()).value(&b"hello"[..]))
21 .await?;
22 println!("produced {}-{}@{}", md.topic, md.partition, md.offset);
23 producer.close().await?;
24
25 let mut consumer = Consumer::connect(bootstrap.clone()).await?;
26 consumer.assign(&topic, 0, 0).await?;
27 let begin = consumer.beginning_offsets([(topic.as_str(), 0)]).await?;
28 let end = consumer.end_offsets([(topic.as_str(), 0)]).await?;
29 let lag = consumer.current_lag((topic.as_str(), 0)).await?;
30 println!("begin={begin:?} end={end:?} lag={lag:?}");
31 consumer.close().await?;
32
33 let mut group = ConsumerGroup::join(
34 ConsumerConfig::bootstrap([bootstrap]).max_wait_ms(500),
35 "partitionline-offsets",
36 topic.clone(),
37 )
38 .await?;
39 let recs = group.poll().await?;
40 if let Some(rec) = recs.first() {
41 let tp = TopicPartition::new(&rec.topic, rec.partition);
42 group
43 .commit_with_metadata([(
44 tp,
45 OffsetAndMetadata::with_metadata(rec.offset + 1, "example"),
46 )])
47 .await?;
48 }
49 for (tp, md) in group.committed().await? {
50 println!("{tp} committed={} meta={}", md.offset, md.metadata);
51 }
52 group.leave().await?;
53 Ok(())
54}9async fn main() -> partitionline::Result<()> {
10 let bootstrap = std::env::var("KAFKA_BOOTSTRAP").unwrap_or_else(|_| "127.0.0.1:9092".into());
11 let topic = std::env::var("KAFKA_TOPIC").unwrap_or_else(|_| "partitionline".into());
12 let format = std::env::var("FORMAT").unwrap_or_else(|_| "log".into());
13
14 let producer = Producer::new(
15 ProducerConfig::bootstrap([bootstrap.clone()]).linger(std::time::Duration::ZERO),
16 )
17 .await?;
18 let md = producer
19 .send(ProduceRecord::to(topic.clone()).value(&b"hello"[..]))
20 .await?;
21 let pm = producer.metrics();
22
23 if format == "prom" {
24 println!("# HELP partitionline_produce_records_acked Produce records acked");
25 println!("# TYPE partitionline_produce_records_acked counter");
26 println!("partitionline_produce_records_acked {}", pm.records_acked);
27 println!("# HELP partitionline_produce_ack_p99_seconds Produce ack p99 latency");
28 println!("# TYPE partitionline_produce_ack_p99_seconds gauge");
29 println!(
30 "partitionline_produce_ack_p99_seconds {}",
31 (pm.ack_latency.p99_nanos as f64) / 1_000_000_000.0
32 );
33 } else {
34 println!(
35 "produced {}-{}@{} queued={} acked={} bytes={} ack_us={} p50_us={} p99_us={} topics={}",
36 md.topic,
37 md.partition,
38 md.offset,
39 pm.records_queued,
40 pm.records_acked,
41 pm.bytes_queued,
42 pm.ack_latency.mean_nanos().unwrap_or(0) / 1000,
43 pm.ack_latency.p50_nanos / 1000,
44 pm.ack_latency.p99_nanos / 1000,
45 pm.topics.len()
46 );
47 }
48 producer.close().await?;
49
50 let mut consumer = Consumer::connect(bootstrap).await?;
51 consumer.assign(&topic, 0, 0).await?;
52 let recs = consumer.fetch().await?;
53 let cm = consumer.metrics();
54 if format == "prom" {
55 println!("# HELP partitionline_fetch_rounds Fetch rounds completed");
56 println!("# TYPE partitionline_fetch_rounds counter");
57 println!("partitionline_fetch_rounds {}", cm.fetch_rounds);
58 println!("# HELP partitionline_fetch_p99_seconds Fetch round p99 latency");
59 println!("# TYPE partitionline_fetch_p99_seconds gauge");
60 println!(
61 "partitionline_fetch_p99_seconds {}",
62 (cm.fetch_latency.p99_nanos as f64) / 1_000_000_000.0
63 );
64 } else {
65 println!(
66 "fetched {} records rounds={} bytes={} errors={} fetch_us={} p50_us={} p99_us={} topics={}",
67 recs.len(),
68 cm.fetch_rounds,
69 cm.bytes_fetched,
70 cm.fetch_errors,
71 cm.fetch_latency.mean_nanos().unwrap_or(0) / 1000,
72 cm.fetch_latency.p50_nanos / 1000,
73 cm.fetch_latency.p99_nanos / 1000,
74 cm.topics.len()
75 );
76 }
77 consumer.close().await?;
78 Ok(())
79}Sourcepub async fn close_timeout(self, _timeout: Duration) -> Result<()>
pub async fn close_timeout(self, _timeout: Duration) -> Result<()>
Drop fetch connections (Java close(Duration)).
A manual consumer has no LeaveGroup RPC; this is the same as
Self::close. Group and share members use
crate::ConsumerGroup::close_timeout /
crate::ShareGroup::close_timeout.
Sourcepub fn versions(&self) -> &HashMap<i16, ApiVersion>
pub fn versions(&self) -> &HashMap<i16, ApiVersion>
Negotiated ApiVersions for this connection.
Sourcepub async fn list_offsets(
&mut self,
topic: impl Into<String>,
partition: i32,
timestamp: i64,
) -> Result<i64>
pub async fn list_offsets( &mut self, topic: impl Into<String>, partition: i32, timestamp: i64, ) -> Result<i64>
ListOffsets timestamp: crate::EARLIEST_TIMESTAMP (-2),
crate::LATEST_TIMESTAMP (-1), crate::MAX_TIMESTAMP (-3),
crate::EARLIEST_LOCAL_TIMESTAMP (-4),
crate::LATEST_TIERED_TIMESTAMP (-5), or milliseconds.
Negotiates ListOffsets v1–v10 (v6–v10 flexible; v10 TimeoutMs). Waits up to
ConsumerConfig::request_timeout. For a one-shot timeout, use
Self::list_offsets_timeout.
Sourcepub async fn list_offsets_timeout(
&mut self,
topic: impl Into<String>,
partition: i32,
timestamp: i64,
timeout: Duration,
) -> Result<i64>
pub async fn list_offsets_timeout( &mut self, topic: impl Into<String>, partition: i32, timestamp: i64, timeout: Duration, ) -> Result<i64>
Self::list_offsets with a one-shot timeout.
Sourcepub async fn list_offset(
&mut self,
partition: impl Into<TopicPartition>,
timestamp: i64,
) -> Result<i64>
pub async fn list_offset( &mut self, partition: impl Into<TopicPartition>, timestamp: i64, ) -> Result<i64>
Self::list_offsets for a TopicPartition.
Sourcepub async fn list_offset_timeout(
&mut self,
partition: impl Into<TopicPartition>,
timestamp: i64,
timeout: Duration,
) -> Result<i64>
pub async fn list_offset_timeout( &mut self, partition: impl Into<TopicPartition>, timestamp: i64, timeout: Duration, ) -> Result<i64>
Self::list_offset with a one-shot timeout.
Sourcepub fn seek(&mut self, topic: &str, partition: i32, offset: i64) -> Result<()>
pub fn seek(&mut self, topic: &str, partition: i32, offset: i64) -> Result<()>
Set the next fetch offset for an assigned partition (Java
seek(TopicPartition, long)).
A negative offset is Java IllegalArgumentException (seek offset must not be a negative number). An unassigned partition is Java
IllegalStateException (No current assignment for partition).
Clears Fetch LastFetchedEpoch (KIP-320). To keep a leader epoch,
use Self::seek_with_metadata.
Sourcepub fn seek_to(
&mut self,
partition: impl Into<TopicPartition>,
offset: i64,
) -> Result<()>
pub fn seek_to( &mut self, partition: impl Into<TopicPartition>, offset: i64, ) -> Result<()>
Self::seek for a TopicPartition.
Sourcepub fn seek_with_metadata(
&mut self,
partition: impl Into<TopicPartition>,
offset: impl Into<OffsetAndMetadata>,
) -> Result<()>
pub fn seek_with_metadata( &mut self, partition: impl Into<TopicPartition>, offset: impl Into<OffsetAndMetadata>, ) -> Result<()>
Seek using OffsetAndMetadata (Java seek(TopicPartition, OffsetAndMetadata)).
The offset is the next fetch position. A negative offset and an
unassigned partition use the same Java messages as Self::seek.
The leader epoch is sent as Fetch LastFetchedEpoch (KIP-320).
Unknown epoch (None) clears it, matching Java Optional.empty().
The metadata string is ignored (Java does the same).
Sourcepub async fn seek_to_beginning(&mut self) -> Result<()>
pub async fn seek_to_beginning(&mut self) -> Result<()>
Seek every assigned partition to the log start (ListOffsets earliest).
Sourcepub async fn seek_to_beginning_of(
&mut self,
partitions: impl IntoIterator<Item = impl Into<TopicPartition>>,
) -> Result<()>
pub async fn seek_to_beginning_of( &mut self, partitions: impl IntoIterator<Item = impl Into<TopicPartition>>, ) -> Result<()>
Seek these assigned partitions to the log start (Java seekToBeginning).
Sourcepub async fn seek_to_end(&mut self) -> Result<()>
pub async fn seek_to_end(&mut self) -> Result<()>
Seek every assigned partition to the high watermark (ListOffsets latest).
Sourcepub async fn seek_to_end_of(
&mut self,
partitions: impl IntoIterator<Item = impl Into<TopicPartition>>,
) -> Result<()>
pub async fn seek_to_end_of( &mut self, partitions: impl IntoIterator<Item = impl Into<TopicPartition>>, ) -> Result<()>
Seek these assigned partitions to the high watermark (Java seekToEnd).
Sourcepub async fn partitions_for(
&mut self,
topic: impl Into<String>,
) -> Result<Vec<PartitionInfo>>
pub async fn partitions_for( &mut self, topic: impl Into<String>, ) -> Result<Vec<PartitionInfo>>
Partition metadata for topic (Java partitionsFor: leader, replicas, ISR, offline replicas, leader epoch).
Waits up to ConsumerConfig::request_timeout. For a one-shot
timeout, use Self::partitions_for_timeout.
Sourcepub async fn partitions_for_timeout(
&mut self,
topic: impl Into<String>,
timeout: Duration,
) -> Result<Vec<PartitionInfo>>
pub async fn partitions_for_timeout( &mut self, topic: impl Into<String>, timeout: Duration, ) -> Result<Vec<PartitionInfo>>
Self::partitions_for with a one-shot timeout (Java partitionsFor(String, Duration)).
Sourcepub async fn list_topics(&mut self) -> Result<Vec<PartitionInfo>>
pub async fn list_topics(&mut self) -> Result<Vec<PartitionInfo>>
Cluster Metadata for every topic (Java listTopics).
Waits up to ConsumerConfig::request_timeout. For a one-shot
timeout, use Self::list_topics_timeout.
Sourcepub async fn list_topics_timeout(
&mut self,
timeout: Duration,
) -> Result<Vec<PartitionInfo>>
pub async fn list_topics_timeout( &mut self, timeout: Duration, ) -> Result<Vec<PartitionInfo>>
Self::list_topics with a one-shot timeout (Java listTopics(Duration)).
Sourcepub async fn beginning_offsets(
&mut self,
partitions: impl IntoIterator<Item = impl Into<TopicPartition>>,
) -> Result<Vec<(TopicPartition, i64)>>
pub async fn beginning_offsets( &mut self, partitions: impl IntoIterator<Item = impl Into<TopicPartition>>, ) -> Result<Vec<(TopicPartition, i64)>>
Log-start offset for each partition (ListOffsets earliest).
Waits up to ConsumerConfig::request_timeout. For a one-shot
timeout, use Self::beginning_offsets_timeout.
Examples found in repository?
11async fn main() -> partitionline::Result<()> {
12 let bootstrap = std::env::var("KAFKA_BOOTSTRAP").unwrap_or_else(|_| "127.0.0.1:9092".into());
13 let topic = std::env::var("KAFKA_TOPIC").unwrap_or_else(|_| "partitionline".into());
14
15 let producer = Producer::new(
16 ProducerConfig::bootstrap([bootstrap.clone()]).linger(std::time::Duration::ZERO),
17 )
18 .await?;
19 let md = producer
20 .send(ProduceRecord::to(topic.clone()).value(&b"hello"[..]))
21 .await?;
22 println!("produced {}-{}@{}", md.topic, md.partition, md.offset);
23 producer.close().await?;
24
25 let mut consumer = Consumer::connect(bootstrap.clone()).await?;
26 consumer.assign(&topic, 0, 0).await?;
27 let begin = consumer.beginning_offsets([(topic.as_str(), 0)]).await?;
28 let end = consumer.end_offsets([(topic.as_str(), 0)]).await?;
29 let lag = consumer.current_lag((topic.as_str(), 0)).await?;
30 println!("begin={begin:?} end={end:?} lag={lag:?}");
31 consumer.close().await?;
32
33 let mut group = ConsumerGroup::join(
34 ConsumerConfig::bootstrap([bootstrap]).max_wait_ms(500),
35 "partitionline-offsets",
36 topic.clone(),
37 )
38 .await?;
39 let recs = group.poll().await?;
40 if let Some(rec) = recs.first() {
41 let tp = TopicPartition::new(&rec.topic, rec.partition);
42 group
43 .commit_with_metadata([(
44 tp,
45 OffsetAndMetadata::with_metadata(rec.offset + 1, "example"),
46 )])
47 .await?;
48 }
49 for (tp, md) in group.committed().await? {
50 println!("{tp} committed={} meta={}", md.offset, md.metadata);
51 }
52 group.leave().await?;
53 Ok(())
54}Sourcepub async fn beginning_offsets_timeout(
&mut self,
partitions: impl IntoIterator<Item = impl Into<TopicPartition>>,
timeout: Duration,
) -> Result<Vec<(TopicPartition, i64)>>
pub async fn beginning_offsets_timeout( &mut self, partitions: impl IntoIterator<Item = impl Into<TopicPartition>>, timeout: Duration, ) -> Result<Vec<(TopicPartition, i64)>>
Self::beginning_offsets with a one-shot timeout
(Java beginningOffsets(Collection, Duration)).
Sourcepub async fn end_offsets(
&mut self,
partitions: impl IntoIterator<Item = impl Into<TopicPartition>>,
) -> Result<Vec<(TopicPartition, i64)>>
pub async fn end_offsets( &mut self, partitions: impl IntoIterator<Item = impl Into<TopicPartition>>, ) -> Result<Vec<(TopicPartition, i64)>>
High-watermark offset for each partition (ListOffsets latest).
Waits up to ConsumerConfig::request_timeout. For a one-shot
timeout, use Self::end_offsets_timeout.
Examples found in repository?
11async fn main() -> partitionline::Result<()> {
12 let bootstrap = std::env::var("KAFKA_BOOTSTRAP").unwrap_or_else(|_| "127.0.0.1:9092".into());
13 let topic = std::env::var("KAFKA_TOPIC").unwrap_or_else(|_| "partitionline".into());
14
15 let producer = Producer::new(
16 ProducerConfig::bootstrap([bootstrap.clone()]).linger(std::time::Duration::ZERO),
17 )
18 .await?;
19 let md = producer
20 .send(ProduceRecord::to(topic.clone()).value(&b"hello"[..]))
21 .await?;
22 println!("produced {}-{}@{}", md.topic, md.partition, md.offset);
23 producer.close().await?;
24
25 let mut consumer = Consumer::connect(bootstrap.clone()).await?;
26 consumer.assign(&topic, 0, 0).await?;
27 let begin = consumer.beginning_offsets([(topic.as_str(), 0)]).await?;
28 let end = consumer.end_offsets([(topic.as_str(), 0)]).await?;
29 let lag = consumer.current_lag((topic.as_str(), 0)).await?;
30 println!("begin={begin:?} end={end:?} lag={lag:?}");
31 consumer.close().await?;
32
33 let mut group = ConsumerGroup::join(
34 ConsumerConfig::bootstrap([bootstrap]).max_wait_ms(500),
35 "partitionline-offsets",
36 topic.clone(),
37 )
38 .await?;
39 let recs = group.poll().await?;
40 if let Some(rec) = recs.first() {
41 let tp = TopicPartition::new(&rec.topic, rec.partition);
42 group
43 .commit_with_metadata([(
44 tp,
45 OffsetAndMetadata::with_metadata(rec.offset + 1, "example"),
46 )])
47 .await?;
48 }
49 for (tp, md) in group.committed().await? {
50 println!("{tp} committed={} meta={}", md.offset, md.metadata);
51 }
52 group.leave().await?;
53 Ok(())
54}Sourcepub async fn end_offsets_timeout(
&mut self,
partitions: impl IntoIterator<Item = impl Into<TopicPartition>>,
timeout: Duration,
) -> Result<Vec<(TopicPartition, i64)>>
pub async fn end_offsets_timeout( &mut self, partitions: impl IntoIterator<Item = impl Into<TopicPartition>>, timeout: Duration, ) -> Result<Vec<(TopicPartition, i64)>>
Self::end_offsets with a one-shot timeout
(Java endOffsets(Collection, Duration)).
Sourcepub async fn current_lag(
&mut self,
partition: impl Into<TopicPartition>,
) -> Result<Option<i64>>
pub async fn current_lag( &mut self, partition: impl Into<TopicPartition>, ) -> Result<Option<i64>>
High watermark minus position (Java currentLag).
None when the high watermark is unknown (-1). An unassigned
partition is Java IllegalStateException (No current assignment for partition).
Examples found in repository?
11async fn main() -> partitionline::Result<()> {
12 let bootstrap = std::env::var("KAFKA_BOOTSTRAP").unwrap_or_else(|_| "127.0.0.1:9092".into());
13 let topic = std::env::var("KAFKA_TOPIC").unwrap_or_else(|_| "partitionline".into());
14
15 let producer = Producer::new(
16 ProducerConfig::bootstrap([bootstrap.clone()]).linger(std::time::Duration::ZERO),
17 )
18 .await?;
19 let md = producer
20 .send(ProduceRecord::to(topic.clone()).value(&b"hello"[..]))
21 .await?;
22 println!("produced {}-{}@{}", md.topic, md.partition, md.offset);
23 producer.close().await?;
24
25 let mut consumer = Consumer::connect(bootstrap.clone()).await?;
26 consumer.assign(&topic, 0, 0).await?;
27 let begin = consumer.beginning_offsets([(topic.as_str(), 0)]).await?;
28 let end = consumer.end_offsets([(topic.as_str(), 0)]).await?;
29 let lag = consumer.current_lag((topic.as_str(), 0)).await?;
30 println!("begin={begin:?} end={end:?} lag={lag:?}");
31 consumer.close().await?;
32
33 let mut group = ConsumerGroup::join(
34 ConsumerConfig::bootstrap([bootstrap]).max_wait_ms(500),
35 "partitionline-offsets",
36 topic.clone(),
37 )
38 .await?;
39 let recs = group.poll().await?;
40 if let Some(rec) = recs.first() {
41 let tp = TopicPartition::new(&rec.topic, rec.partition);
42 group
43 .commit_with_metadata([(
44 tp,
45 OffsetAndMetadata::with_metadata(rec.offset + 1, "example"),
46 )])
47 .await?;
48 }
49 for (tp, md) in group.committed().await? {
50 println!("{tp} committed={} meta={}", md.offset, md.metadata);
51 }
52 group.leave().await?;
53 Ok(())
54}Sourcepub async fn current_lag_timeout(
&mut self,
partition: impl Into<TopicPartition>,
timeout: Duration,
) -> Result<Option<i64>>
pub async fn current_lag_timeout( &mut self, partition: impl Into<TopicPartition>, timeout: Duration, ) -> Result<Option<i64>>
Self::current_lag with a one-shot timeout for the ListOffsets RPC.
An unassigned partition is the same Java IllegalStateException as
Self::current_lag.
Sourcepub async fn offsets_for_times(
&mut self,
queries: impl IntoIterator<Item = (impl Into<TopicPartition>, i64)>,
) -> Result<Vec<(TopicPartition, Option<OffsetAndTimestamp>)>>
pub async fn offsets_for_times( &mut self, queries: impl IntoIterator<Item = (impl Into<TopicPartition>, i64)>, ) -> Result<Vec<(TopicPartition, Option<OffsetAndTimestamp>)>>
First offset at or after each timestamp (Java offsetsForTimes).
A negative timestamp is Java IllegalArgumentException
(The target time cannot be negative). Use Self::beginning_offsets
/ Self::end_offsets (or Self::list_offsets with
crate::EARLIEST_TIMESTAMP / crate::LATEST_TIMESTAMP) for
those sentinels. Partitions with no matching record return None.
OffsetAndTimestamp::leader_epoch is Java getLeaderEpoch.
Waits up to ConsumerConfig::request_timeout. For a one-shot
timeout, use Self::offsets_for_times_timeout.
Sourcepub async fn offsets_for_times_timeout(
&mut self,
queries: impl IntoIterator<Item = (impl Into<TopicPartition>, i64)>,
timeout: Duration,
) -> Result<Vec<(TopicPartition, Option<OffsetAndTimestamp>)>>
pub async fn offsets_for_times_timeout( &mut self, queries: impl IntoIterator<Item = (impl Into<TopicPartition>, i64)>, timeout: Duration, ) -> Result<Vec<(TopicPartition, Option<OffsetAndTimestamp>)>>
Self::offsets_for_times with a one-shot timeout
(Java offsetsForTimes(Map, Duration)).