krabka_client_producer/
error.rs1use std::time::Duration;
2
3use thiserror::Error;
4
5#[derive(Debug, Error)]
6#[non_exhaustive]
7pub enum ProducerError {
8 #[error("client: {0}")]
9 Client(#[from] krabka_client_core::ClientError),
10
11 #[error("protocol: {0}")]
12 Protocol(#[from] krabka_protocol::ProtocolError),
13
14 #[error("broker error_code {0}")]
15 Server(i16),
16
17 #[error("fenced by newer producer instance")]
18 FencedProducer,
19
20 #[error("invalid config: {0}")]
21 InvalidConfig(String),
22
23 #[error("Invalid partition: {0}. Partition number should always be non-negative or null.")]
26 InvalidPartition(i32),
27
28 #[error("Invalid timestamp: {0}. Timestamp should always be non-negative or null.")]
31 InvalidTimestamp(i64),
32
33 #[error(
37 "The partitioner generated an invalid partition number: {0}. Partition number should always be non-negative."
38 )]
39 InvalidPartitionerResult(i32),
40
41 #[error("batch too large: {batch_size} > max")]
42 BatchTooLarge { batch_size: usize },
43
44 #[error("{}", record_too_large_message(*record_size, *limit))]
48 RecordTooLarge {
49 record_size: usize,
51 limit: RecordSizeLimit,
53 },
54
55 #[error(
60 "Failed to allocate {size} bytes within the configured max blocking time {} ms. Total memory: {total} bytes. Available memory: {available} bytes. Poolable size: {poolable} bytes",
61 max_block.as_millis()
62 )]
63 BufferExhausted {
64 size: usize,
65 max_block: Duration,
66 total: usize,
67 available: usize,
68 poolable: usize,
69 },
70
71 #[error("producer closed")]
72 Closed,
73
74 #[error("flush timed out")]
75 FlushTimeout,
76
77 #[error("{}", metadata_timeout_message(topic, *partition, *partition_count, *waited))]
82 MetadataTimeout {
83 topic: String,
84 partition: Option<i32>,
85 partition_count: Option<i32>,
86 waited: Duration,
87 },
88
89 #[error("the batch was not acknowledged before its retries ran out")]
93 SendTimeout,
94
95 #[error("compression: {0}")]
96 Compression(#[from] krabka_compression::CompressionError),
97
98 #[error("producer is not transactional (no transactional_id configured)")]
99 NotTransactional,
100
101 #[error("invalid transaction state: {0}")]
102 InvalidTransactionState(&'static str),
103
104 #[error("the transaction was aborted; the record was never sent")]
110 TransactionAborted,
111
112 #[error("concurrent transactions on the same transactional_id")]
113 ConcurrentTransactions,
114
115 #[error(
116 "transaction outcome is unknown; call init_transactions before sending or beginning another transaction"
117 )]
118 RecoveryRequired,
119
120 #[error(
127 "the transactional producer is in a fatal error state after broker error_code {0}; close the producer"
128 )]
129 FatalTransactionError(i16),
130}
131
132#[derive(Debug, Clone, Copy, PartialEq, Eq)]
134pub enum RecordSizeLimit {
135 MaxRequestSize(usize),
137 BufferMemory,
139}
140
141fn record_too_large_message(record_size: usize, limit: RecordSizeLimit) -> String {
143 match limit {
144 RecordSizeLimit::MaxRequestSize(max_request_size) => format!(
145 "The message is {record_size} bytes when serialized which is larger than {max_request_size}, which is the value of the max_request_size configuration."
146 ),
147 RecordSizeLimit::BufferMemory => format!(
148 "The message is {record_size} bytes when serialized which is larger than the total memory buffer you have configured with the buffer_memory configuration."
149 ),
150 }
151}
152
153fn metadata_timeout_message(
155 topic: &str,
156 partition: Option<i32>,
157 partition_count: Option<i32>,
158 waited: Duration,
159) -> String {
160 let waited_ms = waited.as_millis();
161 match (partition, partition_count) {
162 (Some(partition), Some(count)) => format!(
163 "Partition {partition} of topic {topic} with partition count {count} is not present in metadata after {waited_ms} ms."
164 ),
165 _ => format!("Topic {topic} not present in metadata after {waited_ms} ms."),
166 }
167}
168
169#[cfg(test)]
170mod tests {
171
172 use super::*;
173
174 #[test]
175 fn display_messages() {
176 for (_name, error, expected) in [
177 (
178 "fenced producer",
179 ProducerError::FencedProducer,
180 "fenced by newer producer instance",
181 ),
182 (
183 "invalid config",
184 ProducerError::InvalidConfig("idempotence requires acks=all".to_owned()),
185 "invalid config: idempotence requires acks=all",
186 ),
187 ] {
188 assert2::assert!(error.to_string() == expected);
189 }
190 }
191}