kafka-crab-js 0.3.1 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/Cargo.toml CHANGED
@@ -21,8 +21,8 @@ rdkafka = { version = "0.34", features = [
21
21
  "libz-static",
22
22
  "cmake-build",
23
23
  "tokio",
24
- "ssl",
25
24
  "ssl-vendored",
25
+ "ssl",
26
26
  ] }
27
27
  tokio = { version = "1", features = ["full"] }
28
28
 
package/README.md CHANGED
@@ -72,5 +72,4 @@ consumer.startConsumer(async (error, {value, partition, offset}) => {
72
72
  });
73
73
  ```
74
74
  ## Examples
75
-
76
75
  You can find examples of using Kafka Crab JS in the [GitHub repository](https://github.com/inaiat/kafka-crab-js).
package/index.d.ts CHANGED
@@ -3,19 +3,6 @@
3
3
 
4
4
  /* auto-generated by NAPI-RS */
5
5
 
6
- export const enum SecurityProtocol {
7
- Plaintext = 'Plaintext',
8
- Ssl = 'Ssl',
9
- SaslPlaintext = 'SaslPlaintext',
10
- SaslSsl = 'SaslSsl'
11
- }
12
- export interface KafkaConfiguration {
13
- brokers: string
14
- clientId: string
15
- securityProtocol?: SecurityProtocol
16
- configuration?: Record<string, string>
17
- logLevel?: string
18
- }
19
6
  export const enum CommitMode {
20
7
  AutoCommit = 'AutoCommit',
21
8
  Sync = 'Sync',
@@ -43,6 +30,19 @@ export interface ConsumerConfiguration {
43
30
  enableAutoCommit?: boolean
44
31
  configuration?: Record<string, string>
45
32
  }
33
+ export const enum SecurityProtocol {
34
+ Plaintext = 'Plaintext',
35
+ Ssl = 'Ssl',
36
+ SaslPlaintext = 'SaslPlaintext',
37
+ SaslSsl = 'SaslSsl'
38
+ }
39
+ export interface KafkaConfiguration {
40
+ brokers: string
41
+ clientId: string
42
+ securityProtocol?: SecurityProtocol
43
+ configuration?: Record<string, string>
44
+ logLevel?: string
45
+ }
46
46
  export interface ProducerConfiguration {
47
47
  queueTimeout?: number
48
48
  thrownOnError?: boolean
@@ -93,16 +93,15 @@ export interface MessageModel {
93
93
  key?: Buffer
94
94
  headers?: Record<string, Buffer>
95
95
  }
96
+ export class KafkaConsumer {
97
+ startConsumer(callback: (error: Error | undefined, result: Payload) => Promise<ConsumerResult | undefined>): Promise<void>
98
+ }
96
99
  export class KafkaClient {
97
100
  readonly kafkaConfiguration: KafkaConfiguration
98
101
  constructor(kafkaConfiguration: KafkaConfiguration)
99
102
  createProducer(producerConfiguration: ProducerConfiguration): KafkaProducer
100
103
  createConsumer(consumerConfiguration: ConsumerConfiguration): KafkaConsumer
101
104
  }
102
- export class ProducerHelper { }
103
- export class KafkaConsumer {
104
- startConsumer(callback: (error: Error | undefined, result: Payload) => Promise<ConsumerResult | undefined>): Promise<void>
105
- }
106
105
  export class KafkaProducer {
107
106
  send(producerRecord: ProducerRecord): Promise<Array<RecordMetadata>>
108
107
  }
package/index.js CHANGED
@@ -252,14 +252,13 @@ if (!nativeBinding) {
252
252
  throw new Error(`Failed to load native binding`)
253
253
  }
254
254
 
255
- const { SecurityProtocol, KafkaClient, CommitMode, ConsumerResult, ProducerHelper, KafkaConsumer, KafkaProducer, AutoOffsetReset, PartitionPosition } = nativeBinding
255
+ const { CommitMode, ConsumerResult, KafkaConsumer, SecurityProtocol, KafkaClient, KafkaProducer, AutoOffsetReset, PartitionPosition } = nativeBinding
256
256
 
257
- module.exports.SecurityProtocol = SecurityProtocol
258
- module.exports.KafkaClient = KafkaClient
259
257
  module.exports.CommitMode = CommitMode
260
258
  module.exports.ConsumerResult = ConsumerResult
261
- module.exports.ProducerHelper = ProducerHelper
262
259
  module.exports.KafkaConsumer = KafkaConsumer
260
+ module.exports.SecurityProtocol = SecurityProtocol
261
+ module.exports.KafkaClient = KafkaClient
263
262
  module.exports.KafkaProducer = KafkaProducer
264
263
  module.exports.AutoOffsetReset = AutoOffsetReset
265
264
  module.exports.PartitionPosition = PartitionPosition
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kafka-crab-js",
3
- "version": "0.3.1",
3
+ "version": "0.4.1",
4
4
  "main": "index.js",
5
5
  "types": "index.d.ts",
6
6
  "napi": {
@@ -56,8 +56,8 @@
56
56
  },
57
57
  "packageManager": "yarn@3.6.0",
58
58
  "optionalDependencies": {
59
- "kafka-crab-js-darwin-arm64": "0.3.1",
60
- "kafka-crab-js-darwin-x64": "0.3.1",
61
- "kafka-crab-js-linux-x64-gnu": "0.3.1"
59
+ "kafka-crab-js-darwin-arm64": "0.4.1",
60
+ "kafka-crab-js-darwin-x64": "0.4.1",
61
+ "kafka-crab-js-linux-x64-gnu": "0.4.1"
62
62
  }
63
63
  }
@@ -0,0 +1,29 @@
1
+ use rdkafka::consumer::StreamConsumer;
2
+ use tracing::info;
3
+
4
+ use rdkafka::{
5
+ client::ClientContext,
6
+ consumer::{ConsumerContext, Rebalance},
7
+ error::KafkaResult,
8
+ topic_partition_list::TopicPartitionList,
9
+ };
10
+
11
+ pub type LoggingConsumer = StreamConsumer<CustomContext>;
12
+
13
+ pub struct CustomContext;
14
+
15
+ impl ClientContext for CustomContext {}
16
+
17
+ impl ConsumerContext for CustomContext {
18
+ fn pre_rebalance(&self, rebalance: &Rebalance) {
19
+ info!("Pre rebalance {:?}", rebalance);
20
+ }
21
+
22
+ fn post_rebalance(&self, rebalance: &Rebalance) {
23
+ info!("Post rebalance {:?}", rebalance);
24
+ }
25
+
26
+ fn commit_callback(&self, result: KafkaResult<()>, _offsets: &TopicPartitionList) {
27
+ info!("Committing offsets: {:?}. Offset: {:?}", result, _offsets);
28
+ }
29
+ }
@@ -0,0 +1,196 @@
1
+ use napi::{bindgen_prelude::*, threadsafe_function::ThreadsafeFunction};
2
+
3
+ use rdkafka::{
4
+ consumer::{stream_consumer::StreamConsumer, CommitMode as RdKfafkaCommitMode, Consumer},
5
+ message::{BorrowedMessage, Header, OwnedHeaders},
6
+ producer::FutureRecord,
7
+ Message,
8
+ };
9
+
10
+ use std::time::Duration;
11
+ use tracing::{debug, error, info, warn};
12
+
13
+ use crate::kafka::{
14
+ consumer::kafka_consumer::RETRY_COUNTER_NAME,
15
+ kafka_util::{
16
+ kakfa_headers_to_hashmap, kakfa_headers_to_hashmap_buffer, ExtractValueOnKafkaHashMap,
17
+ },
18
+ model::Payload,
19
+ };
20
+
21
+ use super::kafka_consumer::{ConsumerResult, ProducerHelper};
22
+
23
+ use super::consumer_context::CustomContext;
24
+
25
+ pub struct ConsumerThread {
26
+ pub stream_consumer: StreamConsumer<CustomContext>,
27
+ pub func: ThreadsafeFunction<Payload>,
28
+ pub commit_mode: Option<RdKfafkaCommitMode>,
29
+ pub retries: i32,
30
+ pub next_topic_on_fail: Option<String>,
31
+ pub producer: ProducerHelper,
32
+ pub pause_consumer_duration: Option<Duration>,
33
+ }
34
+
35
+ impl ConsumerThread {
36
+ pub async fn start(&self) {
37
+ loop {
38
+ if let Some(duration) = &self.pause_consumer_duration {
39
+ tracing::debug!("Pause consumer for {}", duration.as_millis());
40
+ tokio::time::sleep(*duration).await;
41
+ };
42
+ match self.stream_consumer.recv().await {
43
+ Err(e) => error!("Error while receiving from stream consumer: {:?}", e),
44
+ Ok(message) => {
45
+ let message_result = self.process_message(&message).await;
46
+ match self.handle_message_result(&message, message_result).await {
47
+ Ok(_) => {
48
+ debug!(
49
+ "Message consumed successfully. Topic: {}, Partition: {}, Offset: {}",
50
+ message.topic(),
51
+ message.partition(),
52
+ message.offset()
53
+ );
54
+ }
55
+ Err(err) => {
56
+ error!("Message processing failed. Error: {:?}", err);
57
+ }
58
+ }
59
+ }
60
+ };
61
+ }
62
+ }
63
+
64
+ async fn process_message(
65
+ &self,
66
+ message: &BorrowedMessage<'_>,
67
+ ) -> anyhow::Result<Option<ConsumerResult>> {
68
+ match message.payload_view::<[u8]>() {
69
+ None => Ok(None),
70
+ Some(Ok(payload)) => {
71
+ let payload_js = create_payload(message, payload);
72
+ match self
73
+ .func
74
+ .call_async::<Promise<Option<ConsumerResult>>>(Ok(payload_js))
75
+ .await
76
+ {
77
+ Ok(js_result) => match js_result.await {
78
+ Ok(value) => match value {
79
+ None | Some(ConsumerResult::Ok) => {
80
+ if let Some(commit_mode) = self.commit_mode {
81
+ match self.stream_consumer.commit_message(message, commit_mode) {
82
+ Ok(_) => Ok(Some(ConsumerResult::Ok)),
83
+ Err(e) => Err(anyhow::Error::new(e)),
84
+ }
85
+ } else {
86
+ Ok(Some(ConsumerResult::Ok))
87
+ }
88
+ }
89
+ Some(ConsumerResult::Retry) => Ok(Some(ConsumerResult::Retry)),
90
+ },
91
+ Err(_) => {
92
+ warn!("Error while calling return function");
93
+ Ok(Some(ConsumerResult::Ok))
94
+ }
95
+ },
96
+ Err(err) => {
97
+ warn!("Error while calling function: {:?}", err.to_string());
98
+ Ok(Some(ConsumerResult::Retry))
99
+ }
100
+ }
101
+ }
102
+ Some(_) => Err(anyhow::Error::msg(
103
+ "Error while deserializing message payload",
104
+ )),
105
+ }
106
+ }
107
+
108
+ async fn handle_message_result(
109
+ &self,
110
+ message: &BorrowedMessage<'_>,
111
+ message_result: anyhow::Result<Option<ConsumerResult>>,
112
+ ) -> anyhow::Result<()> {
113
+ match message_result {
114
+ Ok(consumer_result) => match consumer_result {
115
+ Some(result) => match result {
116
+ ConsumerResult::Ok => Ok(()),
117
+ ConsumerResult::Retry => match self.send_to_retry_strategy(message).await {
118
+ Ok(_) => {
119
+ debug!("Message sent to retry strategy");
120
+ Ok(())
121
+ }
122
+ Err(e) => Err(anyhow::Error::msg(format!(
123
+ "Error while sending message to retry strategy: {:?}",
124
+ e
125
+ ))),
126
+ },
127
+ },
128
+ None => Ok(()),
129
+ },
130
+ Err(err) => Err(err),
131
+ }
132
+ }
133
+
134
+ async fn send_to_retry_strategy(&self, message: &BorrowedMessage<'_>) -> anyhow::Result<()> {
135
+ let retry_counter: usize = {
136
+ let headers_map = kakfa_headers_to_hashmap(message.headers());
137
+ let mut counter = headers_map.get_value(RETRY_COUNTER_NAME).unwrap_or(1);
138
+ if self.retries > 0 {
139
+ counter += 1;
140
+ }
141
+ counter
142
+ };
143
+
144
+ let next_topic = if self.retries > 0 && retry_counter <= self.retries.try_into().unwrap() {
145
+ Some(message.topic().to_string())
146
+ } else {
147
+ self.next_topic_on_fail.clone()
148
+ };
149
+
150
+ match next_topic {
151
+ Some(next_topic) => {
152
+ info!("Message sent to next topic: {}", next_topic);
153
+ let new_headers = message
154
+ .headers()
155
+ .unwrap_or(OwnedHeaders::new().as_borrowed())
156
+ .detach()
157
+ .insert(Header {
158
+ key: RETRY_COUNTER_NAME,
159
+ value: Some(retry_counter.to_string().as_str()),
160
+ });
161
+ let key = message.key().unwrap_or(&[]);
162
+
163
+ let record = FutureRecord::to(&next_topic)
164
+ .key(key)
165
+ .headers(new_headers)
166
+ .payload(message.payload().unwrap_or(&[]));
167
+
168
+ let _result = self
169
+ .producer
170
+ .future_producer
171
+ .send(record, self.producer.queue_timeout)
172
+ .await
173
+ .map_err(|e| anyhow::Error::new(e.0))?;
174
+ Ok(())
175
+ }
176
+ None => {
177
+ debug!("There is no next topic. Commit message");
178
+ Ok(())
179
+ }
180
+ }
181
+ }
182
+ }
183
+
184
+ fn create_payload(message: &BorrowedMessage<'_>, payload: &[u8]) -> Payload {
185
+ let key: Option<Buffer> = message.key().map(|bytes| bytes.into());
186
+ let headers = Some(kakfa_headers_to_hashmap_buffer(message.headers()));
187
+ let payload_js = Payload::new(
188
+ payload.into(),
189
+ key,
190
+ headers,
191
+ message.topic().to_owned(),
192
+ message.partition(),
193
+ message.offset(),
194
+ );
195
+ payload_js
196
+ }
@@ -1,58 +1,33 @@
1
1
  use napi::{bindgen_prelude::*, threadsafe_function::ThreadsafeFunction, Result};
2
2
 
3
3
  use rdkafka::{
4
- client::ClientContext,
5
4
  config::RDKafkaLogLevel,
6
- consumer::{
7
- stream_consumer::StreamConsumer, CommitMode as RdKfafkaCommitMode, Consumer, ConsumerContext,
8
- Rebalance,
9
- },
10
- error::KafkaResult,
11
- message::{BorrowedMessage, Header, OwnedHeaders},
12
- producer::{FutureProducer, FutureRecord},
5
+ consumer::{stream_consumer::StreamConsumer, CommitMode as RdKfafkaCommitMode, Consumer},
6
+ producer::FutureProducer,
13
7
  topic_partition_list::TopicPartitionList,
14
- ClientConfig, Message,
8
+ ClientConfig,
15
9
  };
16
10
 
17
11
  use std::{collections::HashMap, time::Duration, vec};
18
12
  use tracing::{debug, error, info, warn};
19
13
 
20
- use crate::kafka::kafka_util::{
21
- convert_to_rdkafka_offset, kakfa_headers_to_hashmap, ExtractValueOnKafkaHashMap,
22
- };
23
-
24
- use super::{
14
+ use crate::kafka::{
25
15
  kafka_admin::KafkaAdmin,
26
16
  kafka_client::KafkaClient,
27
- kafka_util::{kakfa_headers_to_hashmap_buffer, AnyhowToNapiError},
17
+ kafka_util::{convert_to_rdkafka_offset, AnyhowToNapiError},
28
18
  model::{OffsetModel, PartitionPosition, Payload},
29
19
  };
30
20
 
31
- const RETRY_COUNTER_NAME: &str = "kafka-crab-js-retry-counter";
32
- const DEFAULT_QUEUE_TIMEOUT: u64 = 5000;
33
- const DEFAULT_PAUSE_DURATION: i64 = 1000;
34
- const DEFAULT_RETRY_TOPIC_SUFFIX: &str = "-retry";
35
- const DEFAULT_DLQ_TOPIC_SUFFIX: &str = "-dlq";
36
-
37
- type LoggingConsumer = StreamConsumer<CustomContext>;
38
-
39
- pub struct CustomContext;
40
-
41
- impl ClientContext for CustomContext {}
42
-
43
- impl ConsumerContext for CustomContext {
44
- fn pre_rebalance(&self, rebalance: &Rebalance) {
45
- info!("Pre rebalance {:?}", rebalance);
46
- }
47
-
48
- fn post_rebalance(&self, rebalance: &Rebalance) {
49
- info!("Post rebalance {:?}", rebalance);
50
- }
21
+ use super::{
22
+ consumer_context::{CustomContext, LoggingConsumer},
23
+ consumer_thread::ConsumerThread,
24
+ };
51
25
 
52
- fn commit_callback(&self, result: KafkaResult<()>, _offsets: &TopicPartitionList) {
53
- info!("Committing offsets: {:?}. Offset: {:?}", result, _offsets);
54
- }
55
- }
26
+ pub const RETRY_COUNTER_NAME: &str = "kafka-crab-js-retry-counter";
27
+ pub const DEFAULT_QUEUE_TIMEOUT: u64 = 5000;
28
+ pub const DEFAULT_PAUSE_DURATION: i64 = 1000;
29
+ pub const DEFAULT_RETRY_TOPIC_SUFFIX: &str = "-retry";
30
+ pub const DEFAULT_DLQ_TOPIC_SUFFIX: &str = "-dlq";
56
31
 
57
32
  #[napi(string_enum)]
58
33
  #[derive(Debug, PartialEq)]
@@ -81,10 +56,9 @@ pub enum ConsumerResult {
81
56
  }
82
57
 
83
58
  #[derive(Clone)]
84
- #[napi]
85
59
  pub struct ProducerHelper {
86
- future_producer: FutureProducer,
87
- queue_timeout: Duration,
60
+ pub future_producer: FutureProducer,
61
+ pub queue_timeout: Duration,
88
62
  }
89
63
 
90
64
  fn setup_future_producer(
@@ -149,6 +123,11 @@ impl KafkaConsumer {
149
123
  })
150
124
  }
151
125
 
126
+ // #[napi(ts_args_type = "callback: (error: Error) => Promise<ConsumerResult | undefined>")]
127
+ // pub async fn create_consumer(&self) -> Result<()> {
128
+ // Ok(())
129
+ // }
130
+
152
131
  #[napi(
153
132
  ts_args_type = "callback: (error: Error | undefined, result: Payload) => Promise<ConsumerResult | undefined>"
154
133
  )]
@@ -286,29 +265,46 @@ impl KafkaConsumer {
286
265
  enable_auto_commit.unwrap_or(true).to_string(),
287
266
  )
288
267
  .set_log_level(RDKafkaLogLevel::Debug)
289
- .create_with_context(context)
290
- .expect("Consumer creation failed");
268
+ .create_with_context(context)?;
291
269
 
292
270
  if create_topic.unwrap_or(true) {
271
+ info!("Creating topic: {:?}", topic);
293
272
  let admin = KafkaAdmin::new(&self.client_config);
294
- admin.create_topic(topic).await?;
273
+ let result = admin.create_topic(topic).await;
274
+ if let Err(e) = result {
275
+ warn!("Fail to create topic {:?}", e);
276
+ return Err(anyhow::Error::msg(format!("Fail to create topic: {:?}", e)));
277
+ }
278
+ info!("Topic created: {:?}", topic)
295
279
  }
296
280
 
297
281
  if let Some(offset) = convert_to_rdkafka_offset(offset) {
298
282
  debug!("Setting offset to: {:?}", offset);
299
- let metadata = consumer
300
- .fetch_metadata(Some(topic), Duration::from_millis(1500))
301
- .expect("Fail to retrive metadata from consumer");
283
+ let metadata = consumer.fetch_metadata(Some(topic), Duration::from_millis(1500))?;
302
284
 
303
285
  metadata.topics().iter().for_each(|meta_topic| {
304
286
  let mut tpl = TopicPartitionList::new();
305
287
  meta_topic.partitions().iter().for_each(|meta_partition| {
306
288
  tpl.add_partition(topic, meta_partition.id());
307
289
  });
308
- tpl.set_all_offsets(offset).expect("Fail to set offset");
309
- consumer
310
- .assign(&tpl)
311
- .expect("Assign topic partition list failed");
290
+ //TODO: Handle error
291
+ match tpl.set_all_offsets(offset) {
292
+ Ok(_) => {
293
+ debug!("Offset set to: {:?}", offset);
294
+ }
295
+ Err(e) => {
296
+ error!("Fail to set offset: {:?}", e)
297
+ }
298
+ };
299
+ //TODO: Handle error
300
+ match consumer.assign(&tpl) {
301
+ Ok(_) => {
302
+ debug!("Assigning topic: {:?}", topic);
303
+ }
304
+ Err(e) => {
305
+ error!("Fail to assign topic: {:?}", e);
306
+ }
307
+ }
312
308
  });
313
309
  }
314
310
 
@@ -328,176 +324,3 @@ impl KafkaConsumer {
328
324
  Ok(consumer)
329
325
  }
330
326
  }
331
-
332
- pub struct ConsumerThread {
333
- stream_consumer: StreamConsumer<CustomContext>,
334
- func: ThreadsafeFunction<Payload>,
335
- commit_mode: Option<RdKfafkaCommitMode>,
336
- retries: i32,
337
- next_topic_on_fail: Option<String>,
338
- producer: ProducerHelper,
339
- pause_consumer_duration: Option<Duration>,
340
- }
341
-
342
- impl ConsumerThread {
343
- pub async fn start(&self) {
344
- loop {
345
- if let Some(duration) = &self.pause_consumer_duration {
346
- tracing::debug!("Pause consumer for {}", duration.as_millis());
347
- tokio::time::sleep(*duration).await;
348
- };
349
- match self.stream_consumer.recv().await {
350
- Err(e) => error!("Error while receiving from stream consumer: {:?}", e),
351
- Ok(message) => {
352
- let message_result = self.process_message(&message).await;
353
- match self.handle_message_result(&message, message_result).await {
354
- Ok(_) => {
355
- debug!(
356
- "Message consumed successfully. Topic: {}, Partition: {}, Offset: {}",
357
- message.topic(),
358
- message.partition(),
359
- message.offset()
360
- );
361
- }
362
- Err(err) => {
363
- error!("Message processing failed. Error: {:?}", err);
364
- }
365
- }
366
- }
367
- };
368
- }
369
- }
370
-
371
- async fn process_message(
372
- &self,
373
- message: &BorrowedMessage<'_>,
374
- ) -> anyhow::Result<Option<ConsumerResult>> {
375
- match message.payload_view::<[u8]>() {
376
- None => Ok(None),
377
- Some(Ok(payload)) => {
378
- let payload_js = create_payload(message, payload);
379
- match self
380
- .func
381
- .call_async::<Promise<Option<ConsumerResult>>>(Ok(payload_js))
382
- .await
383
- {
384
- Ok(js_result) => match js_result.await {
385
- Ok(value) => match value {
386
- None | Some(ConsumerResult::Ok) => {
387
- if let Some(commit_mode) = self.commit_mode {
388
- match self.stream_consumer.commit_message(message, commit_mode) {
389
- Ok(_) => Ok(Some(ConsumerResult::Ok)),
390
- Err(e) => Err(anyhow::Error::new(e)),
391
- }
392
- } else {
393
- Ok(Some(ConsumerResult::Ok))
394
- }
395
- }
396
- Some(ConsumerResult::Retry) => Ok(Some(ConsumerResult::Retry)),
397
- },
398
- Err(_) => {
399
- warn!("Error while calling return function");
400
- Ok(Some(ConsumerResult::Ok))
401
- }
402
- },
403
- Err(err) => {
404
- warn!("Error while calling function: {:?}", err.to_string());
405
- Ok(Some(ConsumerResult::Retry))
406
- }
407
- }
408
- }
409
- Some(_) => Err(anyhow::Error::msg(
410
- "Error while deserializing message payload",
411
- )),
412
- }
413
- }
414
-
415
- async fn handle_message_result(
416
- &self,
417
- message: &BorrowedMessage<'_>,
418
- message_result: anyhow::Result<Option<ConsumerResult>>,
419
- ) -> anyhow::Result<()> {
420
- match message_result {
421
- Ok(consumer_result) => match consumer_result {
422
- Some(result) => match result {
423
- ConsumerResult::Ok => Ok(()),
424
- ConsumerResult::Retry => match self.send_to_retry_strategy(message).await {
425
- Ok(_) => {
426
- debug!("Message sent to retry strategy");
427
- Ok(())
428
- }
429
- Err(e) => Err(anyhow::Error::msg(format!(
430
- "Error while sending message to retry strategy: {:?}",
431
- e
432
- ))),
433
- },
434
- },
435
- None => Ok(()),
436
- },
437
- Err(err) => Err(err),
438
- }
439
- }
440
-
441
- async fn send_to_retry_strategy(&self, message: &BorrowedMessage<'_>) -> anyhow::Result<()> {
442
- let retry_counter: usize = {
443
- let headers_map = kakfa_headers_to_hashmap(message.headers());
444
- let mut counter = headers_map.get_value(RETRY_COUNTER_NAME).unwrap_or(1);
445
- if self.retries > 0 {
446
- counter += 1;
447
- }
448
- counter
449
- };
450
-
451
- let next_topic = if self.retries > 0 && retry_counter <= self.retries.try_into().unwrap() {
452
- Some(message.topic().to_string())
453
- } else {
454
- self.next_topic_on_fail.clone()
455
- };
456
-
457
- match next_topic {
458
- Some(next_topic) => {
459
- info!("Message sent to next topic: {}", next_topic);
460
- let new_headers = message
461
- .headers()
462
- .unwrap_or(OwnedHeaders::new().as_borrowed())
463
- .detach()
464
- .insert(Header {
465
- key: RETRY_COUNTER_NAME,
466
- value: Some(retry_counter.to_string().as_str()),
467
- });
468
- let key = message.key().unwrap_or(&[]);
469
-
470
- let record = FutureRecord::to(&next_topic)
471
- .key(key)
472
- .headers(new_headers)
473
- .payload(message.payload().unwrap_or(&[]));
474
-
475
- let _result = self
476
- .producer
477
- .future_producer
478
- .send(record, self.producer.queue_timeout)
479
- .await
480
- .map_err(|e| anyhow::Error::new(e.0))?;
481
- Ok(())
482
- }
483
- None => {
484
- debug!("There is no next topic. Commit message");
485
- Ok(())
486
- }
487
- }
488
- }
489
- }
490
-
491
- fn create_payload(message: &BorrowedMessage<'_>, payload: &[u8]) -> Payload {
492
- let key: Option<Buffer> = message.key().map(|bytes| bytes.into());
493
- let headers = Some(kakfa_headers_to_hashmap_buffer(message.headers()));
494
- let payload_js = Payload::new(
495
- payload.into(),
496
- key,
497
- headers,
498
- message.topic().to_owned(),
499
- message.partition(),
500
- message.offset(),
501
- );
502
- payload_js
503
- }
@@ -0,0 +1,3 @@
1
+ pub mod consumer_context;
2
+ pub mod consumer_thread;
3
+ pub mod kafka_consumer;
@@ -33,14 +33,9 @@ impl<'a> KafkaAdmin<'a> {
33
33
  }
34
34
 
35
35
  async fn fetch_config_resource(&self) -> Result<HashMap<String, String>, KafkaError> {
36
- let consumer: BaseConsumer = self
37
- .client_config
38
- .create()
39
- .expect("Consumer creation failed");
36
+ let consumer: BaseConsumer = self.client_config.create()?;
40
37
 
41
- let metadata = consumer
42
- .fetch_metadata(None, Duration::from_secs(5))
43
- .expect("Failed to fetch metadata");
38
+ let metadata = consumer.fetch_metadata(None, Duration::from_secs(5))?;
44
39
 
45
40
  for broker in metadata.brokers() {
46
41
  info!(
@@ -63,9 +58,9 @@ impl<'a> KafkaAdmin<'a> {
63
58
  }
64
59
 
65
60
  pub async fn create_topic(&self, topic_name: &str) -> anyhow::Result<()> {
61
+ debug!("Fetching broker properties");
66
62
  let broker_properties = self.fetch_config_resource().await?.clone();
67
-
68
- // If you want print all properties => dbg!(broker_properties.clone());
63
+ debug!("Broker properties: {:?}", broker_properties);
69
64
 
70
65
  self
71
66
  .admin_client
@@ -12,7 +12,7 @@ use rdkafka::{
12
12
  use tracing::{info, Level};
13
13
 
14
14
  use super::{
15
- kafka_consumer::{ConsumerConfiguration, KafkaConsumer},
15
+ consumer::kafka_consumer::{ConsumerConfiguration, KafkaConsumer},
16
16
  kafka_producer::{KafkaProducer, ProducerConfiguration},
17
17
  };
18
18
 
package/src/kafka/mod.rs CHANGED
@@ -1,6 +1,6 @@
1
+ pub mod consumer;
1
2
  pub mod kafka_admin;
2
3
  pub mod kafka_client;
3
- pub mod kafka_consumer;
4
4
  pub mod kafka_producer;
5
5
  pub mod kafka_util;
6
6
  pub mod model;