kafka-crab-js 0.1.2 → 0.3.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 +1 -1
- package/README.md +0 -2
- package/example/sample.mjs +12 -12
- package/index.d.ts +4 -2
- package/package.json +6 -5
- package/src/kafka/kafka_client.rs +9 -6
- package/src/kafka/kafka_consumer.rs +22 -7
package/Cargo.toml
CHANGED
|
@@ -26,7 +26,7 @@ rdkafka = { version = "0.33", features = [
|
|
|
26
26
|
tokio = { version = "1", features = ["full"] }
|
|
27
27
|
|
|
28
28
|
tracing = "0.1"
|
|
29
|
-
tracing-subscriber = { version = "0.3", features = ["
|
|
29
|
+
tracing-subscriber = { version = "0.3", features = ["json"] }
|
|
30
30
|
|
|
31
31
|
[build-dependencies]
|
|
32
32
|
napi-build = "2.0.1"
|
package/README.md
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
# Kafka Crab Js
|
|
2
2
|
|
|
3
|
-
🚧 Project in Alpha Stage: API Subject to Breaking Changes 🚧
|
|
4
|
-
|
|
5
3
|
Kafka-Crab-JS is a powerful Node.js library that allows developers to interact with Apache Kafka using Rust programming language, seamlessly integrated with Node.js applications. This documentation provides an overview of the Kafka-Crab-JS library, including installation instructions, usage guidelines, and examples.
|
|
6
4
|
|
|
7
5
|
The plugin can be found on [npm](https://www.npmjs.com/package/kafka-crab-js) and the source code is available on [GitHub](https://github.com/inaiat/kafka-crab-js).
|
package/example/sample.mjs
CHANGED
|
@@ -1,29 +1,29 @@
|
|
|
1
1
|
/* eslint-disable no-await-in-loop */
|
|
2
2
|
|
|
3
3
|
import timersPromises from 'node:timers/promises';
|
|
4
|
-
import {Buffer} from 'node:buffer';
|
|
5
|
-
import {nanoid} from 'nanoid';
|
|
6
|
-
import {KafkaClient, CommitMode, PartitionPosition, ConsumerResult} from '../index.js';
|
|
4
|
+
import { Buffer } from 'node:buffer';
|
|
5
|
+
import { nanoid } from 'nanoid';
|
|
6
|
+
import { KafkaClient, CommitMode, PartitionPosition, ConsumerResult } from '../index.js';
|
|
7
7
|
|
|
8
8
|
const kafkaClient = new KafkaClient({
|
|
9
9
|
brokers: 'localhost:29092',
|
|
10
|
-
clientId: 'my-
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
clientId: 'my-js-group',
|
|
11
|
+
logLevel: 'info',
|
|
12
|
+
});
|
|
13
13
|
const topic = `topic-${nanoid()}`;
|
|
14
14
|
|
|
15
15
|
const consumer = kafkaClient.createConsumer({
|
|
16
16
|
topic,
|
|
17
17
|
groupId: 'my-js-group',
|
|
18
|
-
|
|
19
|
-
offset: {position: PartitionPosition.Stored},
|
|
20
|
-
configuration: {'auto.offset.reset': 'earliest'},
|
|
18
|
+
commitMode: CommitMode.AutoCommit,
|
|
19
|
+
offset: { position: PartitionPosition.Stored },
|
|
20
|
+
configuration: { 'auto.offset.reset': 'earliest' },
|
|
21
21
|
retryStrategy: {
|
|
22
22
|
retries: 5,
|
|
23
23
|
pauseConsumerDuration: 5000,
|
|
24
24
|
},
|
|
25
25
|
});
|
|
26
|
-
const producer = kafkaClient.createProducer({topic, configuration: {'message.timeout.ms': '5000'}});
|
|
26
|
+
const producer = kafkaClient.createProducer({ topic, configuration: { 'message.timeout.ms': '5000' } });
|
|
27
27
|
|
|
28
28
|
async function produce() {
|
|
29
29
|
for (let i = 0; i < 10; i++) {
|
|
@@ -31,7 +31,7 @@ async function produce() {
|
|
|
31
31
|
const result = await producer.send(
|
|
32
32
|
{
|
|
33
33
|
topic,
|
|
34
|
-
messages: [{value: Buffer.from(`{"_id":"${i}","name":"Elizeu Drummond Sample js","phone":"555"}`)}],
|
|
34
|
+
messages: [{ value: Buffer.from(`{"_id":"${i}","name":"Elizeu Drummond Sample js","phone":"555"}`) }],
|
|
35
35
|
},
|
|
36
36
|
);
|
|
37
37
|
console.log('Js message sent. Offset:', result);
|
|
@@ -42,7 +42,7 @@ async function produce() {
|
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
async function startConsumer() {
|
|
45
|
-
consumer.startConsumer(async (error, {value, partition, offset}) => {
|
|
45
|
+
consumer.startConsumer(async (error, { value, partition, offset }) => {
|
|
46
46
|
if (error) {
|
|
47
47
|
console.error('Js Consumer error', error);
|
|
48
48
|
return;
|
package/index.d.ts
CHANGED
|
@@ -14,7 +14,7 @@ export interface KafkaConfiguration {
|
|
|
14
14
|
clientId: string
|
|
15
15
|
securityProtocol?: SecurityProtocol
|
|
16
16
|
configuration?: Record<string, string>
|
|
17
|
-
|
|
17
|
+
logLevel?: string
|
|
18
18
|
}
|
|
19
19
|
export const enum CommitMode {
|
|
20
20
|
AutoCommit = 'AutoCommit',
|
|
@@ -26,6 +26,8 @@ export interface RetryStrategy {
|
|
|
26
26
|
retryTopic?: string
|
|
27
27
|
dqlTopic?: string
|
|
28
28
|
pauseConsumerDuration?: number
|
|
29
|
+
offset?: OffsetModel
|
|
30
|
+
configuration?: Record<string, string>
|
|
29
31
|
}
|
|
30
32
|
export const enum ConsumerResult {
|
|
31
33
|
Ok = 'Ok',
|
|
@@ -99,7 +101,7 @@ export class KafkaClient {
|
|
|
99
101
|
}
|
|
100
102
|
export class ProducerHelper { }
|
|
101
103
|
export class KafkaConsumer {
|
|
102
|
-
startConsumer(callback: (
|
|
104
|
+
startConsumer(callback: (error: Error | undefined, result: Payload) => Promise<ConsumerResult | undefined>): Promise<void>
|
|
103
105
|
}
|
|
104
106
|
export class KafkaProducer {
|
|
105
107
|
send(producerRecord: ProducerRecord): Promise<Array<RecordMetadata>>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kafka-crab-js",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"main": "index.js",
|
|
5
5
|
"types": "index.d.ts",
|
|
6
6
|
"napi": {
|
|
@@ -41,7 +41,8 @@
|
|
|
41
41
|
"xo": {
|
|
42
42
|
"space": true,
|
|
43
43
|
"rules": {
|
|
44
|
-
"new-cap": "off"
|
|
44
|
+
"new-cap": "off",
|
|
45
|
+
"object-curly-spacing": "off"
|
|
45
46
|
}
|
|
46
47
|
},
|
|
47
48
|
"scripts": {
|
|
@@ -55,8 +56,8 @@
|
|
|
55
56
|
},
|
|
56
57
|
"packageManager": "yarn@3.6.0",
|
|
57
58
|
"optionalDependencies": {
|
|
58
|
-
"kafka-crab-js-darwin-arm64": "0.
|
|
59
|
-
"kafka-crab-js-darwin-x64": "0.
|
|
60
|
-
"kafka-crab-js-linux-x64-gnu": "0.
|
|
59
|
+
"kafka-crab-js-darwin-arm64": "0.3.0",
|
|
60
|
+
"kafka-crab-js-darwin-x64": "0.3.0",
|
|
61
|
+
"kafka-crab-js-linux-x64-gnu": "0.3.0"
|
|
61
62
|
}
|
|
62
63
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
use std::{collections::HashMap, fmt};
|
|
1
|
+
use std::{collections::HashMap, fmt, str::FromStr};
|
|
2
2
|
|
|
3
3
|
use napi::{bindgen_prelude::*, Result};
|
|
4
4
|
use rdkafka::{
|
|
@@ -9,8 +9,7 @@ use rdkafka::{
|
|
|
9
9
|
topic_partition_list::TopicPartitionList,
|
|
10
10
|
};
|
|
11
11
|
|
|
12
|
-
use tracing::info;
|
|
13
|
-
use tracing_subscriber::EnvFilter;
|
|
12
|
+
use tracing::{info, Level};
|
|
14
13
|
|
|
15
14
|
use super::{
|
|
16
15
|
kafka_consumer::{ConsumerConfiguration, KafkaConsumer},
|
|
@@ -62,7 +61,7 @@ pub struct KafkaConfiguration {
|
|
|
62
61
|
pub client_id: String,
|
|
63
62
|
pub security_protocol: Option<SecurityProtocol>,
|
|
64
63
|
pub configuration: Option<HashMap<String, String>>,
|
|
65
|
-
pub
|
|
64
|
+
pub log_level: Option<String>,
|
|
66
65
|
}
|
|
67
66
|
|
|
68
67
|
#[derive(Clone, Debug)]
|
|
@@ -77,9 +76,13 @@ pub struct KafkaClient {
|
|
|
77
76
|
impl KafkaClient {
|
|
78
77
|
#[napi(constructor)]
|
|
79
78
|
pub fn new(kafka_configuration: KafkaConfiguration) -> Self {
|
|
79
|
+
let log_level = kafka_configuration.clone().log_level;
|
|
80
80
|
match tracing_subscriber::fmt()
|
|
81
|
-
.
|
|
82
|
-
|
|
81
|
+
.with_max_level(
|
|
82
|
+
Level::from_str(log_level.unwrap_or("error".to_owned()).as_str()).unwrap_or(Level::ERROR),
|
|
83
|
+
)
|
|
84
|
+
.json()
|
|
85
|
+
.with_ansi(false)
|
|
83
86
|
.try_init()
|
|
84
87
|
{
|
|
85
88
|
Ok(_) => {}
|
|
@@ -25,7 +25,7 @@ use super::{
|
|
|
25
25
|
kafka_admin::KafkaAdmin,
|
|
26
26
|
kafka_client::KafkaClient,
|
|
27
27
|
kafka_util::{kakfa_headers_to_hashmap_buffer, AnyhowToNapiError},
|
|
28
|
-
model::{OffsetModel, Payload},
|
|
28
|
+
model::{OffsetModel, PartitionPosition, Payload},
|
|
29
29
|
};
|
|
30
30
|
|
|
31
31
|
const RETRY_COUNTER_NAME: &str = "kafka-crab-js-retry-counter";
|
|
@@ -69,6 +69,8 @@ pub struct RetryStrategy {
|
|
|
69
69
|
pub retry_topic: Option<String>,
|
|
70
70
|
pub dql_topic: Option<String>,
|
|
71
71
|
pub pause_consumer_duration: Option<i64>,
|
|
72
|
+
pub offset: Option<OffsetModel>,
|
|
73
|
+
pub configuration: Option<HashMap<String, String>>,
|
|
72
74
|
}
|
|
73
75
|
|
|
74
76
|
#[napi(string_enum)]
|
|
@@ -148,7 +150,7 @@ impl KafkaConsumer {
|
|
|
148
150
|
}
|
|
149
151
|
|
|
150
152
|
#[napi(
|
|
151
|
-
ts_args_type = "callback: (
|
|
153
|
+
ts_args_type = "callback: (error: Error | undefined, result: Payload) => Promise<ConsumerResult | undefined>"
|
|
152
154
|
)]
|
|
153
155
|
pub async fn start_consumer(&self, func: ThreadsafeFunction<Payload>) -> Result<()> {
|
|
154
156
|
let ConsumerConfiguration { retry_strategy, .. } = self.consumer_configuration.clone();
|
|
@@ -184,7 +186,11 @@ impl KafkaConsumer {
|
|
|
184
186
|
};
|
|
185
187
|
let consumer_thread = ConsumerThread {
|
|
186
188
|
stream_consumer: self
|
|
187
|
-
.create_stream_consumer(
|
|
189
|
+
.create_stream_consumer(
|
|
190
|
+
&self.topic,
|
|
191
|
+
self.consumer_configuration.offset.clone(),
|
|
192
|
+
self.consumer_configuration.configuration.clone(),
|
|
193
|
+
)
|
|
188
194
|
.await
|
|
189
195
|
.map_err(|e| e.convert_to_napi())?,
|
|
190
196
|
func: func.clone(),
|
|
@@ -223,9 +229,17 @@ impl KafkaConsumer {
|
|
|
223
229
|
.pause_consumer_duration
|
|
224
230
|
.unwrap_or(DEFAULT_PAUSE_DURATION) as u64,
|
|
225
231
|
));
|
|
232
|
+
let offset_model = strategy.offset.clone().unwrap_or(OffsetModel {
|
|
233
|
+
position: Some(PartitionPosition::Stored),
|
|
234
|
+
offset: None,
|
|
235
|
+
});
|
|
226
236
|
let consumer_thread = ConsumerThread {
|
|
227
237
|
stream_consumer: self
|
|
228
|
-
.create_stream_consumer(
|
|
238
|
+
.create_stream_consumer(
|
|
239
|
+
&retry_topic,
|
|
240
|
+
Some(offset_model),
|
|
241
|
+
strategy.configuration.clone(),
|
|
242
|
+
)
|
|
229
243
|
.await
|
|
230
244
|
.map_err(|e| e.convert_to_napi())?,
|
|
231
245
|
func: func.clone(),
|
|
@@ -246,14 +260,14 @@ impl KafkaConsumer {
|
|
|
246
260
|
async fn create_stream_consumer(
|
|
247
261
|
&self,
|
|
248
262
|
topic: &str,
|
|
263
|
+
offset: Option<OffsetModel>,
|
|
264
|
+
configuration: Option<HashMap<String, String>>,
|
|
249
265
|
) -> anyhow::Result<StreamConsumer<CustomContext>> {
|
|
250
266
|
let context = CustomContext;
|
|
251
267
|
|
|
252
268
|
let ConsumerConfiguration {
|
|
253
|
-
offset,
|
|
254
269
|
create_topic,
|
|
255
270
|
group_id,
|
|
256
|
-
configuration,
|
|
257
271
|
enable_auto_commit,
|
|
258
272
|
..
|
|
259
273
|
} = self.consumer_configuration.clone();
|
|
@@ -339,7 +353,8 @@ impl ConsumerThread {
|
|
|
339
353
|
match self.handle_message_result(&message, message_result).await {
|
|
340
354
|
Ok(_) => {
|
|
341
355
|
debug!(
|
|
342
|
-
"Message consumed successfully. Partition: {}, Offset: {}",
|
|
356
|
+
"Message consumed successfully. Topic: {}, Partition: {}, Offset: {}",
|
|
357
|
+
message.topic(),
|
|
343
358
|
message.partition(),
|
|
344
359
|
message.offset()
|
|
345
360
|
);
|