kafka-crab-js 0.4.1 → 0.6.0

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
@@ -8,21 +8,20 @@ crate-type = ["cdylib"]
8
8
 
9
9
  [dependencies]
10
10
  # Default enable napi4 feature, see https://nodejs.org/api/n-api.html#node-api-version-matrix
11
- napi = { version = "2", default-features = false, features = [
11
+ napi = { version = "2.16", default-features = false, features = [
12
12
  "napi8",
13
13
  "tokio_rt",
14
14
  "serde-json",
15
15
  ] }
16
- napi-derive = "2"
16
+ napi-derive = "2.16"
17
17
 
18
18
  anyhow = { version = "1.0", features = ["backtrace"] }
19
19
 
20
- rdkafka = { version = "0.34", features = [
20
+ rdkafka = { version = "0.36", features = [
21
21
  "libz-static",
22
22
  "cmake-build",
23
23
  "tokio",
24
24
  "ssl-vendored",
25
- "ssl",
26
25
  ] }
27
26
  tokio = { version = "1", features = ["full"] }
28
27
 
@@ -34,3 +33,4 @@ napi-build = "2.0.1"
34
33
 
35
34
  [profile.release]
36
35
  lto = true
36
+ strip = "symbols"
@@ -1,14 +1,17 @@
1
1
  /* eslint-disable no-await-in-loop */
2
2
 
3
- import timersPromises from 'node:timers/promises';
3
+ // import timersPromises from 'node:timers/promises';
4
4
  import { Buffer } from 'node:buffer';
5
5
  import { nanoid } from 'nanoid';
6
- import { KafkaClient, CommitMode, PartitionPosition, ConsumerResult } from '../index.js';
6
+ import {
7
+ KafkaClient, CommitMode, PartitionPosition, ConsumerResult,
8
+ } from '../index.js';
7
9
 
8
10
  const kafkaClient = new KafkaClient({
9
11
  brokers: 'localhost:29092',
10
12
  clientId: 'my-js-group',
11
13
  logLevel: 'info',
14
+ brokerAddressFamily: 'v4',
12
15
  });
13
16
  const topic = `topic-${nanoid()}`;
14
17
 
@@ -31,7 +34,7 @@ async function produce() {
31
34
  const result = await producer.send(
32
35
  {
33
36
  topic,
34
- messages: [{ value: Buffer.from(`{"_id":"${i}","name":"Elizeu Drummond Sample js","phone":"555"}`) }],
37
+ messages: [{ payload: Buffer.from(`{"_id":"${i}","name":"Elizeu Drummond Sample js","phone":"555"}`) }],
35
38
  },
36
39
  );
37
40
  console.log('Js message sent. Offset:', result);
@@ -0,0 +1,99 @@
1
+ import { CommitMode, KafkaClient, KafkaStreamConsumer, PartitionPosition } from "../index.js";
2
+ import { setTimeout as sleep } from 'node:timers/promises';
3
+ import { fakerPT_BR } from '@faker-js/faker';
4
+ import { Readable } from "node:stream";
5
+
6
+ const kafkaClient = new KafkaClient({
7
+ brokers: 'localhost:29092',
8
+ clientId: 'my-js-group-11',
9
+ logLevel: 'debug',
10
+ brokerAddressFamily: 'v4',
11
+ });
12
+
13
+ /**
14
+ * Produces some messages to the topic
15
+ * @param {string} topic
16
+ * @param {number} messages
17
+ */
18
+ async function produce(topic, messages=1) {
19
+ const producer = kafkaClient.createProducer({ topic, configuration: { 'message.timeout.ms': '5000' } });
20
+ const index = Math.floor(Math.random() * 100_000)
21
+ for (let i = index ?? 0; i < index+messages; i++) {
22
+ try {
23
+ const result = await producer.send(
24
+ {
25
+ topic,
26
+ messages: [{ value: Buffer.from(`{"_id":"${i}","name":"${fakerPT_BR.person.fullName()}","phone":"${fakerPT_BR.phone.number()}"}`) }],
27
+ },
28
+ );
29
+ console.log('Js message sent. Offset:', result);
30
+ } catch (error) {
31
+ console.error('Js Error on send', error);
32
+ }
33
+ }
34
+ }
35
+
36
+ class KafkaStreamReadable extends Readable {
37
+ /**
38
+ * Creates a new KafkaStream
39
+ * @param {KafkaStreamConsumer} kafkaStreamConsumer
40
+ */
41
+ constructor(kafkaStreamConsumer) {
42
+ super({ objectMode: true });
43
+ this.kafkaStreamConsumer = kafkaStreamConsumer;
44
+ }
45
+
46
+ async _read() {
47
+ let message = await this.kafkaStreamConsumer.recv();
48
+ this.push(message)
49
+ }
50
+ }
51
+
52
+ async function startConsumer() {
53
+ const kafkaStreamConsumer = kafkaClient.createStreamConsumer({
54
+ groupId: `crab-test`,
55
+ enableAutoCommit: true,
56
+ });
57
+
58
+ // await streamerConsumer.subscribe(
59
+ // [{
60
+ // topic: 'foo',
61
+ // partitionOffset: [
62
+ // { partition:0, offset: { position: PartitionPosition.Beginning }}
63
+ // ]
64
+ // },
65
+ // {
66
+ // topic: 'bar',
67
+ // partitionOffset: [
68
+ // { partition:0, offset: { position: PartitionPosition.Stored }},
69
+ // { partition:1, offset: { position: PartitionPosition.Beginning }}
70
+ // ]
71
+ // }]
72
+ // );
73
+ // await streamerConsumer.subscribe([ { topic: 'foo', allOffsets: { position: PartitionPosition.Stored } } ]);
74
+ // await streamerConsumer.subscribe('foo');
75
+ await kafkaStreamConsumer.subscribe([ { topic: 'foo' }, { topic: 'bar' } ]);
76
+
77
+ const maxMessages = 20;
78
+ let counter = 0;
79
+ const kafkaStream = new KafkaStreamReadable(kafkaStreamConsumer);
80
+ console.log('Starting consumer')
81
+ kafkaStream.on('data', (message) => {
82
+ counter++;
83
+ console.log('>>> Message received:', { payload: message.payload.toString(), offset: message.offset, partition: message.partition, topic: message.topic })
84
+ if (counter === maxMessages) {
85
+ kafkaStreamConsumer.unsubscribe();
86
+ kafkaStream.destroy();
87
+ }
88
+ // streamerConsumer.commit(message.partition, message.offset+1, CommitMode.Sync)
89
+ })
90
+ }
91
+
92
+ if (process.argv[2] === 'send') {
93
+ const topic = process.argv[3]
94
+ const messages = process.argv[4] ? Number(process.argv[4]) : 1
95
+ console.log('Sending', messages, 'messages to', topic)
96
+ await produce(topic, messages);
97
+ } else {
98
+ await startConsumer();
99
+ }
package/index.d.ts CHANGED
@@ -3,10 +3,20 @@
3
3
 
4
4
  /* auto-generated by NAPI-RS */
5
5
 
6
- export const enum CommitMode {
7
- AutoCommit = 'AutoCommit',
8
- Sync = 'Sync',
9
- Async = 'Async'
6
+ export const enum ConsumerResult {
7
+ Ok = 'Ok',
8
+ Retry = 'Retry'
9
+ }
10
+ export interface KafkaConsumerConfiguration {
11
+ topic: string
12
+ groupId: string
13
+ retryStrategy?: RetryStrategy
14
+ offset?: OffsetModel
15
+ createTopic?: boolean
16
+ commitMode?: CommitMode
17
+ enableAutoCommit?: boolean
18
+ configuration?: Record<string, string>
19
+ fecthMetadataTimeout?: number
10
20
  }
11
21
  export interface RetryStrategy {
12
22
  retries: number
@@ -16,19 +26,16 @@ export interface RetryStrategy {
16
26
  offset?: OffsetModel
17
27
  configuration?: Record<string, string>
18
28
  }
19
- export const enum ConsumerResult {
20
- Ok = 'Ok',
21
- Retry = 'Retry'
29
+ export const enum CommitMode {
30
+ Sync = 'Sync',
31
+ Async = 'Async'
22
32
  }
23
33
  export interface ConsumerConfiguration {
24
- topic: string
25
34
  groupId: string
26
- retryStrategy?: RetryStrategy
27
- offset?: OffsetModel
28
35
  createTopic?: boolean
29
- commitMode?: CommitMode
30
36
  enableAutoCommit?: boolean
31
37
  configuration?: Record<string, string>
38
+ fecthMetadataTimeout?: number
32
39
  }
33
40
  export const enum SecurityProtocol {
34
41
  Plaintext = 'Plaintext',
@@ -42,15 +49,7 @@ export interface KafkaConfiguration {
42
49
  securityProtocol?: SecurityProtocol
43
50
  configuration?: Record<string, string>
44
51
  logLevel?: string
45
- }
46
- export interface ProducerConfiguration {
47
- queueTimeout?: number
48
- thrownOnError?: boolean
49
- configuration?: Record<string, string>
50
- }
51
- export interface ProducerRecord {
52
- topic: string
53
- messages: Array<MessageModel>
52
+ brokerAddressFamily?: string
54
53
  }
55
54
  export const enum AutoOffsetReset {
56
55
  Smallest = 'Smallest',
@@ -61,26 +60,23 @@ export const enum AutoOffsetReset {
61
60
  End = 'End',
62
61
  Error = 'Error'
63
62
  }
64
- export interface Payload {
65
- value: Buffer
66
- key?: Buffer
67
- headers?: Record<string, Buffer>
68
- topic: string
69
- partition: number
70
- offset: number
71
- }
72
63
  export const enum PartitionPosition {
73
- Beginning = 0,
74
- End = 1,
75
- Stored = 2
64
+ Beginning = 'Beginning',
65
+ End = 'End',
66
+ Stored = 'Stored'
76
67
  }
77
68
  export interface OffsetModel {
78
69
  offset?: number
79
70
  position?: PartitionPosition
80
71
  }
81
- export interface KafkaCrabError {
82
- code: number
83
- message: string
72
+ export interface PartitionOffset {
73
+ partition: number
74
+ offset: OffsetModel
75
+ }
76
+ export interface TopicPartitionConfig {
77
+ topic: string
78
+ allOffsets?: OffsetModel
79
+ partitionOffset?: Array<PartitionOffset>
84
80
  }
85
81
  export interface RecordMetadata {
86
82
  topic: string
@@ -88,19 +84,56 @@ export interface RecordMetadata {
88
84
  offset: number
89
85
  error?: KafkaCrabError
90
86
  }
91
- export interface MessageModel {
87
+ export interface MessageProducer {
88
+ value: Buffer
89
+ key?: Buffer
90
+ headers?: Record<string, Buffer>
91
+ }
92
+ export interface ProducerConfiguration {
93
+ queueTimeout?: number
94
+ thrownOnError?: boolean
95
+ configuration?: Record<string, string>
96
+ }
97
+ export interface ProducerRecord {
98
+ topic: string
99
+ messages: Array<MessageProducer>
100
+ }
101
+ export interface Payload {
92
102
  value: Buffer
93
103
  key?: Buffer
94
104
  headers?: Record<string, Buffer>
105
+ topic: string
106
+ partition: number
107
+ offset: number
108
+ }
109
+ export interface Message {
110
+ payload: Buffer
111
+ key?: Buffer
112
+ headers?: Record<string, Buffer>
113
+ topic: string
114
+ partition: number
115
+ offset: number
116
+ }
117
+ export interface KafkaCrabError {
118
+ code: number
119
+ message: string
95
120
  }
96
121
  export class KafkaConsumer {
97
122
  startConsumer(callback: (error: Error | undefined, result: Payload) => Promise<ConsumerResult | undefined>): Promise<void>
98
123
  }
124
+ export class KafkaStreamConsumer {
125
+ subscribe(topicConfigs: string | Array<TopicPartitionConfig>): Promise<void>
126
+ unsubscribe(): void
127
+ seek(topic: string, partition: number, offsetModel: OffsetModel, timeout?: number | undefined | null): void
128
+ recv(): Promise<Message>
129
+ commit(topic: string, partition: number, offset: number, commit: CommitMode): void
130
+ }
99
131
  export class KafkaClient {
100
132
  readonly kafkaConfiguration: KafkaConfiguration
101
133
  constructor(kafkaConfiguration: KafkaConfiguration)
102
134
  createProducer(producerConfiguration: ProducerConfiguration): KafkaProducer
103
- createConsumer(consumerConfiguration: ConsumerConfiguration): KafkaConsumer
135
+ createConsumer(consumerConfiguration: KafkaConsumerConfiguration): KafkaConsumer
136
+ createStreamConsumer(consumerConfiguration: ConsumerConfiguration): KafkaStreamConsumer
104
137
  }
105
138
  export class KafkaProducer {
106
139
  send(producerRecord: ProducerRecord): Promise<Array<RecordMetadata>>
package/index.js CHANGED
@@ -224,14 +224,72 @@ switch (platform) {
224
224
  }
225
225
  break
226
226
  case 'arm':
227
+ if (isMusl()) {
228
+ localFileExisted = existsSync(
229
+ join(__dirname, 'kafka-crab-js.linux-arm-musleabihf.node')
230
+ )
231
+ try {
232
+ if (localFileExisted) {
233
+ nativeBinding = require('./kafka-crab-js.linux-arm-musleabihf.node')
234
+ } else {
235
+ nativeBinding = require('kafka-crab-js-linux-arm-musleabihf')
236
+ }
237
+ } catch (e) {
238
+ loadError = e
239
+ }
240
+ } else {
241
+ localFileExisted = existsSync(
242
+ join(__dirname, 'kafka-crab-js.linux-arm-gnueabihf.node')
243
+ )
244
+ try {
245
+ if (localFileExisted) {
246
+ nativeBinding = require('./kafka-crab-js.linux-arm-gnueabihf.node')
247
+ } else {
248
+ nativeBinding = require('kafka-crab-js-linux-arm-gnueabihf')
249
+ }
250
+ } catch (e) {
251
+ loadError = e
252
+ }
253
+ }
254
+ break
255
+ case 'riscv64':
256
+ if (isMusl()) {
257
+ localFileExisted = existsSync(
258
+ join(__dirname, 'kafka-crab-js.linux-riscv64-musl.node')
259
+ )
260
+ try {
261
+ if (localFileExisted) {
262
+ nativeBinding = require('./kafka-crab-js.linux-riscv64-musl.node')
263
+ } else {
264
+ nativeBinding = require('kafka-crab-js-linux-riscv64-musl')
265
+ }
266
+ } catch (e) {
267
+ loadError = e
268
+ }
269
+ } else {
270
+ localFileExisted = existsSync(
271
+ join(__dirname, 'kafka-crab-js.linux-riscv64-gnu.node')
272
+ )
273
+ try {
274
+ if (localFileExisted) {
275
+ nativeBinding = require('./kafka-crab-js.linux-riscv64-gnu.node')
276
+ } else {
277
+ nativeBinding = require('kafka-crab-js-linux-riscv64-gnu')
278
+ }
279
+ } catch (e) {
280
+ loadError = e
281
+ }
282
+ }
283
+ break
284
+ case 's390x':
227
285
  localFileExisted = existsSync(
228
- join(__dirname, 'kafka-crab-js.linux-arm-gnueabihf.node')
286
+ join(__dirname, 'kafka-crab-js.linux-s390x-gnu.node')
229
287
  )
230
288
  try {
231
289
  if (localFileExisted) {
232
- nativeBinding = require('./kafka-crab-js.linux-arm-gnueabihf.node')
290
+ nativeBinding = require('./kafka-crab-js.linux-s390x-gnu.node')
233
291
  } else {
234
- nativeBinding = require('kafka-crab-js-linux-arm-gnueabihf')
292
+ nativeBinding = require('kafka-crab-js-linux-s390x-gnu')
235
293
  }
236
294
  } catch (e) {
237
295
  loadError = e
@@ -252,13 +310,14 @@ if (!nativeBinding) {
252
310
  throw new Error(`Failed to load native binding`)
253
311
  }
254
312
 
255
- const { CommitMode, ConsumerResult, KafkaConsumer, SecurityProtocol, KafkaClient, KafkaProducer, AutoOffsetReset, PartitionPosition } = nativeBinding
313
+ const { ConsumerResult, KafkaConsumer, KafkaStreamConsumer, CommitMode, SecurityProtocol, KafkaClient, AutoOffsetReset, PartitionPosition, KafkaProducer } = nativeBinding
256
314
 
257
- module.exports.CommitMode = CommitMode
258
315
  module.exports.ConsumerResult = ConsumerResult
259
316
  module.exports.KafkaConsumer = KafkaConsumer
317
+ module.exports.KafkaStreamConsumer = KafkaStreamConsumer
318
+ module.exports.CommitMode = CommitMode
260
319
  module.exports.SecurityProtocol = SecurityProtocol
261
320
  module.exports.KafkaClient = KafkaClient
262
- module.exports.KafkaProducer = KafkaProducer
263
321
  module.exports.AutoOffsetReset = AutoOffsetReset
264
322
  module.exports.PartitionPosition = PartitionPosition
323
+ module.exports.KafkaProducer = KafkaProducer
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kafka-crab-js",
3
- "version": "0.4.1",
3
+ "version": "0.6.0",
4
4
  "main": "index.js",
5
5
  "types": "index.d.ts",
6
6
  "napi": {
@@ -24,19 +24,24 @@
24
24
  ],
25
25
  "author": "inaiat@gmail.com",
26
26
  "homepage": "https://github.com/inaiat/kafka-crab-js.git",
27
+ "repository": {
28
+ "type": "git",
29
+ "url": "https://github.com/inaiat/kafka-crab-js.git"
30
+ },
27
31
  "devDependencies": {
28
- "@napi-rs/cli": "^2.16.3",
29
- "ava": "^5.3.1",
30
- "benny": "^3.7.1",
31
- "kafkajs": "^2.2.4",
32
- "nanoid": "^4.0.2",
33
- "xo": "^0.54.2"
32
+ "@faker-js/faker": "^8.4.1",
33
+ "@napi-rs/cli": "^2.18.3",
34
+ "@types/node": "^20.12.12",
35
+ "ava": "^6.1.2",
36
+ "nanoid": "^5.0.7",
37
+ "typescript": "^5.4.5",
38
+ "xo": "^0.58.0"
34
39
  },
35
40
  "ava": {
36
41
  "timeout": "3m"
37
42
  },
38
43
  "engines": {
39
- "node": ">= 10"
44
+ "node": ">= 18"
40
45
  },
41
46
  "xo": {
42
47
  "space": true,
@@ -54,10 +59,10 @@
54
59
  "universal": "napi universal",
55
60
  "version": "napi version"
56
61
  },
57
- "packageManager": "yarn@3.6.0",
62
+ "packageManager": "pnpm@9.1.1",
58
63
  "optionalDependencies": {
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"
64
+ "kafka-crab-js-darwin-arm64": "0.6.0",
65
+ "kafka-crab-js-darwin-x64": "0.6.0",
66
+ "kafka-crab-js-linux-x64-gnu": "0.6.0"
62
67
  }
63
68
  }
@@ -0,0 +1,179 @@
1
+ use std::{collections::HashMap, time::Duration};
2
+
3
+ use rdkafka::{
4
+ config::RDKafkaLogLevel,
5
+ consumer::{Consumer, StreamConsumer},
6
+ ClientConfig, TopicPartitionList,
7
+ };
8
+ use tracing::{debug, error, info, warn};
9
+
10
+ use crate::kafka::{
11
+ consumer::model::LoggingConsumer,
12
+ kafka_admin::KafkaAdmin,
13
+ kafka_util::convert_to_rdkafka_offset,
14
+ model::{OffsetModel, PartitionOffset},
15
+ };
16
+
17
+ use super::{model::ConsumerConfiguration, model::CustomContext};
18
+
19
+ pub async fn create_stream_consumer_and_setup_everything(
20
+ client_config: &ClientConfig,
21
+ consumer_configuration: &ConsumerConfiguration,
22
+ topic: &str,
23
+ offset: &Option<OffsetModel>,
24
+ configuration: Option<HashMap<String, String>>,
25
+ timeout: Duration,
26
+ ) -> anyhow::Result<StreamConsumer<CustomContext>> {
27
+ let consumer = create_stream_consumer(client_config, consumer_configuration, configuration)?;
28
+
29
+ if consumer_configuration.create_topic.unwrap_or(true) {
30
+ try_create_topic(topic, client_config).await?;
31
+ }
32
+
33
+ if let Some(offset_model) = offset {
34
+ set_offset_of_all_partitions(offset_model, &consumer, topic, timeout)?;
35
+ }
36
+
37
+ try_subscribe(&consumer, topic)?;
38
+
39
+ Ok(consumer)
40
+ }
41
+
42
+ pub fn create_stream_consumer(
43
+ client_config: &ClientConfig,
44
+ consumer_configuration: &ConsumerConfiguration,
45
+ configuration: Option<HashMap<String, String>>,
46
+ ) -> anyhow::Result<StreamConsumer<CustomContext>> {
47
+ let context = CustomContext;
48
+
49
+ let ConsumerConfiguration {
50
+ group_id,
51
+ enable_auto_commit,
52
+ ..
53
+ } = consumer_configuration.clone();
54
+
55
+ let mut consumer_config: ClientConfig = client_config.clone();
56
+
57
+ if let Some(config) = configuration {
58
+ consumer_config.extend(config);
59
+ }
60
+
61
+ debug!(
62
+ "Creating consumer with configuration: {:?}",
63
+ consumer_config
64
+ );
65
+
66
+ let consumer: LoggingConsumer = consumer_config
67
+ .clone()
68
+ .set("group.id", group_id.clone())
69
+ .set(
70
+ "enable.auto.commit",
71
+ enable_auto_commit.unwrap_or(true).to_string(),
72
+ )
73
+ .set_log_level(RDKafkaLogLevel::Debug)
74
+ .create_with_context(context)?;
75
+
76
+ info!("Consumer created. Group id: {:?}", group_id);
77
+ Ok(consumer)
78
+ }
79
+
80
+ pub fn try_subscribe(consumer: &LoggingConsumer, topic: &str) -> anyhow::Result<()> {
81
+ consumer
82
+ .subscribe(vec![&*topic.to_string()].as_slice())
83
+ .map_err(|e| {
84
+ anyhow::Error::msg(format!(
85
+ "Can't subscribe to specified topics. Error: {:?}",
86
+ e
87
+ ))
88
+ })?;
89
+ debug!("Subscribed to topic: {:?}", topic);
90
+ Ok(())
91
+ }
92
+
93
+ pub async fn try_create_topic(topic: &str, client_config: &ClientConfig) -> anyhow::Result<()> {
94
+ info!("Creating topic: {:?}", topic);
95
+ let admin = KafkaAdmin::new(client_config, None);
96
+ let result = admin.create_topic(topic).await;
97
+ if let Err(e) = result {
98
+ warn!("Fail to create topic {:?}", e);
99
+ return Err(anyhow::Error::msg(format!("Fail to create topic: {:?}", e)));
100
+ }
101
+ info!("Topic created: {:?}", topic);
102
+ Ok(())
103
+ }
104
+
105
+ pub fn set_offset_of_all_partitions(
106
+ offset_model: &OffsetModel,
107
+ consumer: &StreamConsumer<CustomContext>,
108
+ topic: &str,
109
+ timeout: Duration,
110
+ ) -> anyhow::Result<()> {
111
+ let offset = convert_to_rdkafka_offset(offset_model);
112
+ debug!("Setting offset to: {:?}", offset);
113
+ let metadata = consumer.fetch_metadata(Some(topic), timeout)?;
114
+
115
+ metadata.topics().iter().for_each(|meta_topic| {
116
+ let mut tpl = TopicPartitionList::new();
117
+ meta_topic.partitions().iter().for_each(|meta_partition| {
118
+ info!("Adding partition: {:?}", meta_partition.id());
119
+ tpl.add_partition(topic, meta_partition.id());
120
+ });
121
+ match tpl.set_all_offsets(offset) {
122
+ Ok(_) => {
123
+ debug!("Offset set to: {:?}", offset);
124
+ }
125
+ Err(e) => {
126
+ error!("Fail to set offset: {:?}", e)
127
+ }
128
+ };
129
+ match consumer.assign(&tpl) {
130
+ Ok(_) => {
131
+ debug!("Assigning topic: {:?}", topic);
132
+ }
133
+ Err(e) => {
134
+ error!("Fail to assign topic: {:?}", e);
135
+ }
136
+ }
137
+ });
138
+
139
+ Ok(())
140
+ }
141
+
142
+ pub fn assign_offset_or_use_metadata(
143
+ topic: &str,
144
+ partition_offset: Option<Vec<PartitionOffset>>,
145
+ offset_model: Option<&OffsetModel>,
146
+ consumer: &StreamConsumer<CustomContext>,
147
+ timeout: Duration,
148
+ ) -> anyhow::Result<()> {
149
+ let mut tpl = TopicPartitionList::new();
150
+
151
+ if let Some(value) = partition_offset {
152
+ for item in value {
153
+ let offset = convert_to_rdkafka_offset(&item.offset);
154
+ debug!(
155
+ "Adding partition: {:?} with offset: {:?} for topic: {:?}",
156
+ item.partition, offset, topic
157
+ );
158
+ tpl.add_partition_offset(topic, item.partition, offset)?;
159
+ }
160
+ } else if let Some(offset_model) = offset_model {
161
+ let offset = convert_to_rdkafka_offset(offset_model);
162
+ let metadata = consumer.fetch_metadata(Some(topic), timeout)?;
163
+ for meta_topic in metadata.topics() {
164
+ for meta_partition in meta_topic.partitions() {
165
+ info!(
166
+ "Adding partition: {:?} with offset: {:?} for topic: {:?}",
167
+ meta_partition.id(),
168
+ offset,
169
+ topic
170
+ );
171
+ tpl.add_partition_offset(topic, meta_partition.id(), offset)?;
172
+ }
173
+ }
174
+ } else {
175
+ anyhow::bail!("At least one of partition_offset or offset_model should be provided");
176
+ }
177
+ consumer.assign(&tpl)?;
178
+ Ok(())
179
+ }
@@ -12,15 +12,14 @@ use tracing::{debug, error, info, warn};
12
12
 
13
13
  use crate::kafka::{
14
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,
15
+ kafka_util::{create_payload, kakfa_headers_to_hashmap, ExtractValueOnKafkaHashMap},
16
+ producer::model::Payload,
19
17
  };
20
18
 
21
- use super::kafka_consumer::{ConsumerResult, ProducerHelper};
22
-
23
- use super::consumer_context::CustomContext;
19
+ use super::{
20
+ kafka_consumer::{ConsumerResult, ProducerHelper},
21
+ model::CustomContext,
22
+ };
24
23
 
25
24
  pub struct ConsumerThread {
26
25
  pub stream_consumer: StreamConsumer<CustomContext>,
@@ -180,17 +179,3 @@ impl ConsumerThread {
180
179
  }
181
180
  }
182
181
  }
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
- }