kafka-crab-js 0.6.0 → 0.8.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 +1 -1
- package/example/stream_consumer.mjs +6 -2
- package/index.d.ts +36 -45
- package/index.js +2 -3
- package/package.json +10 -10
- package/src/kafka/consumer/consumer_helper.rs +35 -24
- package/src/kafka/consumer/consumer_thread.rs +17 -1
- package/src/kafka/consumer/kafka_consumer.rs +7 -7
- package/src/kafka/consumer/kafka_stream_consumer.rs +20 -27
- package/src/kafka/consumer/model.rs +34 -7
- package/src/kafka/kafka_admin.rs +22 -22
- package/src/kafka/kafka_client.rs +2 -26
- package/src/kafka/kafka_util.rs +2 -31
- package/src/kafka/mod.rs +0 -1
- package/src/kafka/producer/kafka_producer.rs +5 -29
- package/src/kafka/producer/model.rs +24 -0
- package/src/kafka/model.rs +0 -48
package/Cargo.toml
CHANGED
|
@@ -23,7 +23,7 @@ async function produce(topic, messages=1) {
|
|
|
23
23
|
const result = await producer.send(
|
|
24
24
|
{
|
|
25
25
|
topic,
|
|
26
|
-
messages: [{
|
|
26
|
+
messages: [{ payload: Buffer.from(`{"_id":"${i}","name":"${fakerPT_BR.person.fullName()}","phone":"${fakerPT_BR.phone.number()}"}`) }],
|
|
27
27
|
},
|
|
28
28
|
);
|
|
29
29
|
console.log('Js message sent. Offset:', result);
|
|
@@ -82,11 +82,15 @@ async function startConsumer() {
|
|
|
82
82
|
counter++;
|
|
83
83
|
console.log('>>> Message received:', { payload: message.payload.toString(), offset: message.offset, partition: message.partition, topic: message.topic })
|
|
84
84
|
if (counter === maxMessages) {
|
|
85
|
-
kafkaStreamConsumer.unsubscribe();
|
|
86
85
|
kafkaStream.destroy();
|
|
87
86
|
}
|
|
88
87
|
// streamerConsumer.commit(message.partition, message.offset+1, CommitMode.Sync)
|
|
89
88
|
})
|
|
89
|
+
|
|
90
|
+
kafkaStream.on('close', () => {
|
|
91
|
+
kafkaStreamConsumer.unsubscribe();
|
|
92
|
+
console.log('Stream ended')
|
|
93
|
+
})
|
|
90
94
|
}
|
|
91
95
|
|
|
92
96
|
if (process.argv[2] === 'send') {
|
package/index.d.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
/* auto-generated by NAPI-RS */
|
|
5
5
|
|
|
6
|
-
export
|
|
6
|
+
export enum ConsumerResult {
|
|
7
7
|
Ok = 'Ok',
|
|
8
8
|
Retry = 'Retry'
|
|
9
9
|
}
|
|
@@ -26,9 +26,9 @@ export interface RetryStrategy {
|
|
|
26
26
|
offset?: OffsetModel
|
|
27
27
|
configuration?: Record<string, string>
|
|
28
28
|
}
|
|
29
|
-
export
|
|
30
|
-
Sync =
|
|
31
|
-
Async =
|
|
29
|
+
export enum CommitMode {
|
|
30
|
+
Sync = 0,
|
|
31
|
+
Async = 1
|
|
32
32
|
}
|
|
33
33
|
export interface ConsumerConfiguration {
|
|
34
34
|
groupId: string
|
|
@@ -37,30 +37,7 @@ export interface ConsumerConfiguration {
|
|
|
37
37
|
configuration?: Record<string, string>
|
|
38
38
|
fecthMetadataTimeout?: number
|
|
39
39
|
}
|
|
40
|
-
export
|
|
41
|
-
Plaintext = 'Plaintext',
|
|
42
|
-
Ssl = 'Ssl',
|
|
43
|
-
SaslPlaintext = 'SaslPlaintext',
|
|
44
|
-
SaslSsl = 'SaslSsl'
|
|
45
|
-
}
|
|
46
|
-
export interface KafkaConfiguration {
|
|
47
|
-
brokers: string
|
|
48
|
-
clientId: string
|
|
49
|
-
securityProtocol?: SecurityProtocol
|
|
50
|
-
configuration?: Record<string, string>
|
|
51
|
-
logLevel?: string
|
|
52
|
-
brokerAddressFamily?: string
|
|
53
|
-
}
|
|
54
|
-
export const enum AutoOffsetReset {
|
|
55
|
-
Smallest = 'Smallest',
|
|
56
|
-
Earliest = 'Earliest',
|
|
57
|
-
Beginning = 'Beginning',
|
|
58
|
-
Largest = 'Largest',
|
|
59
|
-
Latest = 'Latest',
|
|
60
|
-
End = 'End',
|
|
61
|
-
Error = 'Error'
|
|
62
|
-
}
|
|
63
|
-
export const enum PartitionPosition {
|
|
40
|
+
export enum PartitionPosition {
|
|
64
41
|
Beginning = 'Beginning',
|
|
65
42
|
End = 'End',
|
|
66
43
|
Stored = 'Stored'
|
|
@@ -78,26 +55,25 @@ export interface TopicPartitionConfig {
|
|
|
78
55
|
allOffsets?: OffsetModel
|
|
79
56
|
partitionOffset?: Array<PartitionOffset>
|
|
80
57
|
}
|
|
81
|
-
export
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
58
|
+
export enum SecurityProtocol {
|
|
59
|
+
Plaintext = 'Plaintext',
|
|
60
|
+
Ssl = 'Ssl',
|
|
61
|
+
SaslPlaintext = 'SaslPlaintext',
|
|
62
|
+
SaslSsl = 'SaslSsl'
|
|
86
63
|
}
|
|
87
|
-
export interface
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
64
|
+
export interface KafkaConfiguration {
|
|
65
|
+
brokers: string
|
|
66
|
+
clientId: string
|
|
67
|
+
securityProtocol?: SecurityProtocol
|
|
68
|
+
configuration?: Record<string, string>
|
|
69
|
+
logLevel?: string
|
|
70
|
+
brokerAddressFamily?: string
|
|
91
71
|
}
|
|
92
72
|
export interface ProducerConfiguration {
|
|
93
73
|
queueTimeout?: number
|
|
94
74
|
thrownOnError?: boolean
|
|
95
75
|
configuration?: Record<string, string>
|
|
96
76
|
}
|
|
97
|
-
export interface ProducerRecord {
|
|
98
|
-
topic: string
|
|
99
|
-
messages: Array<MessageProducer>
|
|
100
|
-
}
|
|
101
77
|
export interface Payload {
|
|
102
78
|
value: Buffer
|
|
103
79
|
key?: Buffer
|
|
@@ -114,27 +90,42 @@ export interface Message {
|
|
|
114
90
|
partition: number
|
|
115
91
|
offset: number
|
|
116
92
|
}
|
|
93
|
+
export interface RecordMetadata {
|
|
94
|
+
topic: string
|
|
95
|
+
partition: number
|
|
96
|
+
offset: number
|
|
97
|
+
error?: KafkaCrabError
|
|
98
|
+
}
|
|
99
|
+
export interface MessageProducer {
|
|
100
|
+
payload: Buffer
|
|
101
|
+
key?: Buffer
|
|
102
|
+
headers?: Record<string, Buffer>
|
|
103
|
+
}
|
|
104
|
+
export interface ProducerRecord {
|
|
105
|
+
topic: string
|
|
106
|
+
messages: Array<MessageProducer>
|
|
107
|
+
}
|
|
117
108
|
export interface KafkaCrabError {
|
|
118
109
|
code: number
|
|
119
110
|
message: string
|
|
120
111
|
}
|
|
121
|
-
export class KafkaConsumer {
|
|
112
|
+
export declare class KafkaConsumer {
|
|
122
113
|
startConsumer(callback: (error: Error | undefined, result: Payload) => Promise<ConsumerResult | undefined>): Promise<void>
|
|
123
114
|
}
|
|
124
|
-
export class KafkaStreamConsumer {
|
|
115
|
+
export declare class KafkaStreamConsumer {
|
|
125
116
|
subscribe(topicConfigs: string | Array<TopicPartitionConfig>): Promise<void>
|
|
126
117
|
unsubscribe(): void
|
|
127
118
|
seek(topic: string, partition: number, offsetModel: OffsetModel, timeout?: number | undefined | null): void
|
|
128
119
|
recv(): Promise<Message>
|
|
129
120
|
commit(topic: string, partition: number, offset: number, commit: CommitMode): void
|
|
130
121
|
}
|
|
131
|
-
export class KafkaClient {
|
|
122
|
+
export declare class KafkaClient {
|
|
132
123
|
readonly kafkaConfiguration: KafkaConfiguration
|
|
133
124
|
constructor(kafkaConfiguration: KafkaConfiguration)
|
|
134
125
|
createProducer(producerConfiguration: ProducerConfiguration): KafkaProducer
|
|
135
126
|
createConsumer(consumerConfiguration: KafkaConsumerConfiguration): KafkaConsumer
|
|
136
127
|
createStreamConsumer(consumerConfiguration: ConsumerConfiguration): KafkaStreamConsumer
|
|
137
128
|
}
|
|
138
|
-
export class KafkaProducer {
|
|
129
|
+
export declare class KafkaProducer {
|
|
139
130
|
send(producerRecord: ProducerRecord): Promise<Array<RecordMetadata>>
|
|
140
131
|
}
|
package/index.js
CHANGED
|
@@ -310,14 +310,13 @@ if (!nativeBinding) {
|
|
|
310
310
|
throw new Error(`Failed to load native binding`)
|
|
311
311
|
}
|
|
312
312
|
|
|
313
|
-
const { ConsumerResult, KafkaConsumer, KafkaStreamConsumer, CommitMode, SecurityProtocol, KafkaClient,
|
|
313
|
+
const { ConsumerResult, KafkaConsumer, KafkaStreamConsumer, CommitMode, PartitionPosition, SecurityProtocol, KafkaClient, KafkaProducer } = nativeBinding
|
|
314
314
|
|
|
315
315
|
module.exports.ConsumerResult = ConsumerResult
|
|
316
316
|
module.exports.KafkaConsumer = KafkaConsumer
|
|
317
317
|
module.exports.KafkaStreamConsumer = KafkaStreamConsumer
|
|
318
318
|
module.exports.CommitMode = CommitMode
|
|
319
|
+
module.exports.PartitionPosition = PartitionPosition
|
|
319
320
|
module.exports.SecurityProtocol = SecurityProtocol
|
|
320
321
|
module.exports.KafkaClient = KafkaClient
|
|
321
|
-
module.exports.AutoOffsetReset = AutoOffsetReset
|
|
322
|
-
module.exports.PartitionPosition = PartitionPosition
|
|
323
322
|
module.exports.KafkaProducer = KafkaProducer
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kafka-crab-js",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.1",
|
|
4
4
|
"main": "index.js",
|
|
5
5
|
"types": "index.d.ts",
|
|
6
6
|
"napi": {
|
|
@@ -30,12 +30,12 @@
|
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|
|
32
32
|
"@faker-js/faker": "^8.4.1",
|
|
33
|
-
"@napi-rs/cli": "^2.18.
|
|
34
|
-
"@types/node": "^
|
|
35
|
-
"ava": "^6.1.
|
|
33
|
+
"@napi-rs/cli": "^2.18.4",
|
|
34
|
+
"@types/node": "^22.4.1",
|
|
35
|
+
"ava": "^6.1.3",
|
|
36
36
|
"nanoid": "^5.0.7",
|
|
37
37
|
"typescript": "^5.4.5",
|
|
38
|
-
"xo": "^0.
|
|
38
|
+
"xo": "^0.59.3"
|
|
39
39
|
},
|
|
40
40
|
"ava": {
|
|
41
41
|
"timeout": "3m"
|
|
@@ -52,17 +52,17 @@
|
|
|
52
52
|
},
|
|
53
53
|
"scripts": {
|
|
54
54
|
"artifacts": "napi artifacts",
|
|
55
|
-
"build": "napi build --platform --release",
|
|
55
|
+
"build": "napi build --no-const-enum --platform --release",
|
|
56
56
|
"build:debug": "napi build --platform",
|
|
57
57
|
"prepublishOnly": "napi prepublish -t npm",
|
|
58
58
|
"test": "ava",
|
|
59
59
|
"universal": "napi universal",
|
|
60
60
|
"version": "napi version"
|
|
61
61
|
},
|
|
62
|
-
"packageManager": "pnpm@9.
|
|
62
|
+
"packageManager": "pnpm@9.7.1",
|
|
63
63
|
"optionalDependencies": {
|
|
64
|
-
"kafka-crab-js-darwin-arm64": "0.
|
|
65
|
-
"kafka-crab-js-darwin-x64": "0.
|
|
66
|
-
"kafka-crab-js-linux-x64-gnu": "0.
|
|
64
|
+
"kafka-crab-js-darwin-arm64": "0.8.1",
|
|
65
|
+
"kafka-crab-js-darwin-x64": "0.8.1",
|
|
66
|
+
"kafka-crab-js-linux-x64-gnu": "0.8.1"
|
|
67
67
|
}
|
|
68
68
|
}
|
|
@@ -3,18 +3,27 @@ use std::{collections::HashMap, time::Duration};
|
|
|
3
3
|
use rdkafka::{
|
|
4
4
|
config::RDKafkaLogLevel,
|
|
5
5
|
consumer::{Consumer, StreamConsumer},
|
|
6
|
-
ClientConfig, TopicPartitionList,
|
|
6
|
+
ClientConfig, Offset, TopicPartitionList,
|
|
7
7
|
};
|
|
8
8
|
use tracing::{debug, error, info, warn};
|
|
9
9
|
|
|
10
|
-
use crate::kafka::{
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
model::{OffsetModel, PartitionOffset},
|
|
10
|
+
use crate::kafka::{consumer::model::LoggingConsumer, kafka_admin::KafkaAdmin};
|
|
11
|
+
|
|
12
|
+
use super::model::{
|
|
13
|
+
ConsumerConfiguration, CustomContext, OffsetModel, PartitionOffset, PartitionPosition,
|
|
15
14
|
};
|
|
16
15
|
|
|
17
|
-
|
|
16
|
+
pub fn convert_to_rdkafka_offset(offset_model: &OffsetModel) -> Offset {
|
|
17
|
+
match offset_model.position {
|
|
18
|
+
Some(PartitionPosition::Beginning) => Offset::Beginning,
|
|
19
|
+
Some(PartitionPosition::End) => Offset::End,
|
|
20
|
+
Some(PartitionPosition::Stored) => Offset::Stored,
|
|
21
|
+
None => match offset_model.offset {
|
|
22
|
+
Some(value) => Offset::Offset(value),
|
|
23
|
+
None => Offset::Stored, // Default to stored
|
|
24
|
+
},
|
|
25
|
+
}
|
|
26
|
+
}
|
|
18
27
|
|
|
19
28
|
pub async fn create_stream_consumer_and_setup_everything(
|
|
20
29
|
client_config: &ClientConfig,
|
|
@@ -27,14 +36,14 @@ pub async fn create_stream_consumer_and_setup_everything(
|
|
|
27
36
|
let consumer = create_stream_consumer(client_config, consumer_configuration, configuration)?;
|
|
28
37
|
|
|
29
38
|
if consumer_configuration.create_topic.unwrap_or(true) {
|
|
30
|
-
try_create_topic(topic, client_config).await?;
|
|
39
|
+
try_create_topic(&vec![topic.to_owned()], client_config, timeout).await?;
|
|
31
40
|
}
|
|
32
41
|
|
|
33
42
|
if let Some(offset_model) = offset {
|
|
34
43
|
set_offset_of_all_partitions(offset_model, &consumer, topic, timeout)?;
|
|
35
44
|
}
|
|
36
45
|
|
|
37
|
-
try_subscribe(&consumer, topic)?;
|
|
46
|
+
try_subscribe(&consumer, &vec![topic.to_owned()])?;
|
|
38
47
|
|
|
39
48
|
Ok(consumer)
|
|
40
49
|
}
|
|
@@ -77,28 +86,30 @@ pub fn create_stream_consumer(
|
|
|
77
86
|
Ok(consumer)
|
|
78
87
|
}
|
|
79
88
|
|
|
80
|
-
pub fn try_subscribe(consumer: &LoggingConsumer,
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
debug!("Subscribed to topic: {:?}", topic);
|
|
89
|
+
pub fn try_subscribe(consumer: &LoggingConsumer, topics: &Vec<String>) -> anyhow::Result<()> {
|
|
90
|
+
let topics_ref = topics.iter().map(|x| x.as_str()).collect::<Vec<&str>>();
|
|
91
|
+
consumer.subscribe(topics_ref.as_slice()).map_err(|e| {
|
|
92
|
+
anyhow::Error::msg(format!(
|
|
93
|
+
"Can't subscribe to specified topic(s): {:?}. Error: {:?}",
|
|
94
|
+
topics_ref, e
|
|
95
|
+
))
|
|
96
|
+
})?;
|
|
97
|
+
debug!("Subscribed to topic(s): {:?}", topics_ref);
|
|
90
98
|
Ok(())
|
|
91
99
|
}
|
|
92
100
|
|
|
93
|
-
pub async fn try_create_topic(
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
101
|
+
pub async fn try_create_topic(
|
|
102
|
+
topics: &Vec<String>,
|
|
103
|
+
client_config: &ClientConfig,
|
|
104
|
+
fetch_metadata_timeout: Duration,
|
|
105
|
+
) -> anyhow::Result<()> {
|
|
106
|
+
let admin = KafkaAdmin::new(client_config, Some(fetch_metadata_timeout));
|
|
107
|
+
let result = admin.create_topic(topics).await;
|
|
97
108
|
if let Err(e) = result {
|
|
98
109
|
warn!("Fail to create topic {:?}", e);
|
|
99
110
|
return Err(anyhow::Error::msg(format!("Fail to create topic: {:?}", e)));
|
|
100
111
|
}
|
|
101
|
-
info!("Topic created: {:?}",
|
|
112
|
+
info!("Topic(s) created: {:?}", topics);
|
|
102
113
|
Ok(())
|
|
103
114
|
}
|
|
104
115
|
|
|
@@ -12,7 +12,9 @@ use tracing::{debug, error, info, warn};
|
|
|
12
12
|
|
|
13
13
|
use crate::kafka::{
|
|
14
14
|
consumer::kafka_consumer::RETRY_COUNTER_NAME,
|
|
15
|
-
kafka_util::{
|
|
15
|
+
kafka_util::{
|
|
16
|
+
kakfa_headers_to_hashmap, kakfa_headers_to_hashmap_buffer, ExtractValueOnKafkaHashMap,
|
|
17
|
+
},
|
|
16
18
|
producer::model::Payload,
|
|
17
19
|
};
|
|
18
20
|
|
|
@@ -179,3 +181,17 @@ impl ConsumerThread {
|
|
|
179
181
|
}
|
|
180
182
|
}
|
|
181
183
|
}
|
|
184
|
+
|
|
185
|
+
pub fn create_payload(message: &BorrowedMessage<'_>, payload: &[u8]) -> Payload {
|
|
186
|
+
let key: Option<Buffer> = message.key().map(|bytes| bytes.into());
|
|
187
|
+
let headers = Some(kakfa_headers_to_hashmap_buffer(message.headers()));
|
|
188
|
+
let payload_js = Payload::new(
|
|
189
|
+
payload.into(),
|
|
190
|
+
key,
|
|
191
|
+
headers,
|
|
192
|
+
message.topic().to_owned(),
|
|
193
|
+
message.partition(),
|
|
194
|
+
message.offset(),
|
|
195
|
+
);
|
|
196
|
+
payload_js
|
|
197
|
+
}
|
|
@@ -5,16 +5,16 @@ use rdkafka::{consumer::CommitMode as RdKfafkaCommitMode, producer::FutureProduc
|
|
|
5
5
|
use std::{collections::HashMap, time::Duration};
|
|
6
6
|
|
|
7
7
|
use crate::kafka::{
|
|
8
|
-
kafka_client::KafkaClient,
|
|
9
|
-
kafka_util::AnyhowToNapiError,
|
|
10
|
-
model::{OffsetModel, PartitionPosition},
|
|
11
|
-
producer::model::Payload,
|
|
8
|
+
kafka_client::KafkaClient, kafka_util::AnyhowToNapiError, producer::model::Payload,
|
|
12
9
|
};
|
|
13
10
|
|
|
14
11
|
use super::{
|
|
15
12
|
consumer_helper::create_stream_consumer_and_setup_everything,
|
|
16
13
|
consumer_thread::ConsumerThread,
|
|
17
|
-
model::{
|
|
14
|
+
model::{
|
|
15
|
+
CommitMode, ConsumerConfiguration, OffsetModel, PartitionPosition, RetryStrategy,
|
|
16
|
+
DEFAULT_FECTH_METADATA_TIMEOUT,
|
|
17
|
+
},
|
|
18
18
|
};
|
|
19
19
|
|
|
20
20
|
pub const RETRY_COUNTER_NAME: &str = "kafka-crab-js-retry-counter";
|
|
@@ -103,7 +103,7 @@ impl KafkaConsumer {
|
|
|
103
103
|
commit_mode,
|
|
104
104
|
topic,
|
|
105
105
|
fetch_metadata_timeout: Duration::from_millis(
|
|
106
|
-
fecth_metadata_timeout.unwrap_or(DEFAULT_FECTH_METADATA_TIMEOUT) as u64,
|
|
106
|
+
fecth_metadata_timeout.unwrap_or(DEFAULT_FECTH_METADATA_TIMEOUT.as_millis() as i64) as u64,
|
|
107
107
|
),
|
|
108
108
|
})
|
|
109
109
|
}
|
|
@@ -229,6 +229,6 @@ fn convert_to_consumer_configuration(config: &KafkaConsumerConfiguration) -> Con
|
|
|
229
229
|
create_topic: config.create_topic,
|
|
230
230
|
enable_auto_commit: config.enable_auto_commit,
|
|
231
231
|
configuration: config.configuration.clone(),
|
|
232
|
-
fecth_metadata_timeout:
|
|
232
|
+
fecth_metadata_timeout: config.fecth_metadata_timeout,
|
|
233
233
|
}
|
|
234
234
|
}
|
|
@@ -11,16 +11,20 @@ use rdkafka::{
|
|
|
11
11
|
use tracing::{debug, error, info};
|
|
12
12
|
|
|
13
13
|
use crate::kafka::{
|
|
14
|
-
consumer::consumer_helper::{
|
|
14
|
+
consumer::consumer_helper::{
|
|
15
|
+
assign_offset_or_use_metadata, convert_to_rdkafka_offset, try_create_topic, try_subscribe,
|
|
16
|
+
},
|
|
15
17
|
kafka_client::KafkaClient,
|
|
16
|
-
kafka_util::{
|
|
17
|
-
model::{OffsetModel, TopicPartitionConfig},
|
|
18
|
+
kafka_util::{create_message, AnyhowToNapiError},
|
|
18
19
|
producer::model::Message,
|
|
19
20
|
};
|
|
20
21
|
|
|
21
22
|
use super::{
|
|
22
23
|
consumer_helper::{create_stream_consumer, set_offset_of_all_partitions},
|
|
23
|
-
model::{
|
|
24
|
+
model::{
|
|
25
|
+
CommitMode, ConsumerConfiguration, CustomContext, OffsetModel, TopicPartitionConfig,
|
|
26
|
+
DEFAULT_FECTH_METADATA_TIMEOUT,
|
|
27
|
+
},
|
|
24
28
|
};
|
|
25
29
|
|
|
26
30
|
pub const DEFAULT_SEEK_TIMEOUT: i64 = 1500;
|
|
@@ -51,7 +55,7 @@ impl KafkaStreamConsumer {
|
|
|
51
55
|
fecth_metadata_timeout: Duration::from_millis(
|
|
52
56
|
consumer_configuration
|
|
53
57
|
.fecth_metadata_timeout
|
|
54
|
-
.unwrap_or(DEFAULT_FECTH_METADATA_TIMEOUT) as u64,
|
|
58
|
+
.unwrap_or(DEFAULT_FECTH_METADATA_TIMEOUT.as_millis() as i64) as u64,
|
|
55
59
|
),
|
|
56
60
|
})
|
|
57
61
|
}
|
|
@@ -63,7 +67,7 @@ impl KafkaStreamConsumer {
|
|
|
63
67
|
) -> Result<()> {
|
|
64
68
|
let topics = match topic_configs {
|
|
65
69
|
Either::A(config) => {
|
|
66
|
-
|
|
70
|
+
debug!("Subscribing to topic: {:#?}", &config);
|
|
67
71
|
vec![TopicPartitionConfig {
|
|
68
72
|
topic: config,
|
|
69
73
|
all_offsets: None,
|
|
@@ -71,7 +75,7 @@ impl KafkaStreamConsumer {
|
|
|
71
75
|
}]
|
|
72
76
|
}
|
|
73
77
|
Either::B(config) => {
|
|
74
|
-
|
|
78
|
+
debug!("Subscribing to topic config: {:#?}", &config);
|
|
75
79
|
config
|
|
76
80
|
}
|
|
77
81
|
};
|
|
@@ -80,28 +84,17 @@ impl KafkaStreamConsumer {
|
|
|
80
84
|
.iter()
|
|
81
85
|
.map(|x| x.topic.clone())
|
|
82
86
|
.collect::<Vec<String>>();
|
|
83
|
-
let topics_ref = topics_name
|
|
84
|
-
.iter()
|
|
85
|
-
.map(|x| x.as_str())
|
|
86
|
-
.collect::<Vec<&str>>();
|
|
87
87
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
88
|
+
debug!("Creating topics if not exists: {:?}", &topics_name);
|
|
89
|
+
try_create_topic(
|
|
90
|
+
&topics_name,
|
|
91
|
+
&self.client_config,
|
|
92
|
+
self.fecth_metadata_timeout,
|
|
93
|
+
)
|
|
94
|
+
.await
|
|
95
|
+
.map_err(|e| e.convert_to_napi())?;
|
|
94
96
|
|
|
95
|
-
|
|
96
|
-
self
|
|
97
|
-
.stream_consumer
|
|
98
|
-
.subscribe(&topics_ref.as_slice())
|
|
99
|
-
.map_err(|e| {
|
|
100
|
-
napi::Error::new(
|
|
101
|
-
napi::Status::GenericFailure,
|
|
102
|
-
format!("Error while subscribing to topic: {:?}", e),
|
|
103
|
-
)
|
|
104
|
-
})?;
|
|
97
|
+
try_subscribe(&self.stream_consumer, &topics_name).map_err(|e| e.convert_to_napi())?;
|
|
105
98
|
|
|
106
99
|
topics.iter().for_each(|item| {
|
|
107
100
|
if let Some(all_offsets) = item.all_offsets.clone() {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
use std::collections::HashMap;
|
|
1
|
+
use std::{collections::HashMap, time::Duration};
|
|
2
2
|
|
|
3
3
|
use rdkafka::{
|
|
4
4
|
consumer::{ConsumerContext, Rebalance, StreamConsumer},
|
|
@@ -7,12 +7,10 @@ use rdkafka::{
|
|
|
7
7
|
};
|
|
8
8
|
use tracing::info;
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
pub const DEFAULT_FECTH_METADATA_TIMEOUT: Duration = Duration::from_millis(2000);
|
|
11
11
|
|
|
12
12
|
pub type LoggingConsumer = StreamConsumer<CustomContext>;
|
|
13
13
|
|
|
14
|
-
pub const DEFAULT_FECTH_METADATA_TIMEOUT: i64 = 2000;
|
|
15
|
-
|
|
16
14
|
pub struct CustomContext;
|
|
17
15
|
|
|
18
16
|
impl ClientContext for CustomContext {}
|
|
@@ -42,11 +40,11 @@ pub struct RetryStrategy {
|
|
|
42
40
|
pub configuration: Option<HashMap<String, String>>,
|
|
43
41
|
}
|
|
44
42
|
|
|
45
|
-
#[napi
|
|
43
|
+
#[napi]
|
|
46
44
|
#[derive(Debug, PartialEq)]
|
|
47
45
|
pub enum CommitMode {
|
|
48
|
-
Sync,
|
|
49
|
-
Async,
|
|
46
|
+
Sync = 0,
|
|
47
|
+
Async = 1,
|
|
50
48
|
}
|
|
51
49
|
|
|
52
50
|
#[napi(object)]
|
|
@@ -58,3 +56,32 @@ pub struct ConsumerConfiguration {
|
|
|
58
56
|
pub configuration: Option<HashMap<String, String>>,
|
|
59
57
|
pub fecth_metadata_timeout: Option<i64>,
|
|
60
58
|
}
|
|
59
|
+
|
|
60
|
+
#[napi(string_enum)]
|
|
61
|
+
#[derive(Debug)]
|
|
62
|
+
pub enum PartitionPosition {
|
|
63
|
+
Beginning,
|
|
64
|
+
End,
|
|
65
|
+
Stored,
|
|
66
|
+
}
|
|
67
|
+
#[napi(object)]
|
|
68
|
+
#[derive(Clone, Debug)]
|
|
69
|
+
pub struct OffsetModel {
|
|
70
|
+
pub offset: Option<i64>,
|
|
71
|
+
pub position: Option<PartitionPosition>,
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
#[napi(object)]
|
|
75
|
+
#[derive(Clone, Debug)]
|
|
76
|
+
pub struct PartitionOffset {
|
|
77
|
+
pub partition: i32,
|
|
78
|
+
pub offset: OffsetModel,
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
#[napi(object)]
|
|
82
|
+
#[derive(Clone, Debug)]
|
|
83
|
+
pub struct TopicPartitionConfig {
|
|
84
|
+
pub topic: String,
|
|
85
|
+
pub all_offsets: Option<OffsetModel>,
|
|
86
|
+
pub partition_offset: Option<Vec<PartitionOffset>>,
|
|
87
|
+
}
|
package/src/kafka/kafka_admin.rs
CHANGED
|
@@ -12,7 +12,7 @@ use tracing::{debug, info, trace};
|
|
|
12
12
|
|
|
13
13
|
use std::{collections::HashMap, str::FromStr, time::Duration};
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
const DEFAULT_FECTH_METADATA_TIMEOUT: Duration = Duration::from_millis(2000);
|
|
16
16
|
|
|
17
17
|
const DEFAULT_NUM_PARTITIONS: i32 = 3;
|
|
18
18
|
const DEFAULT_REPLICATION: i32 = 3;
|
|
@@ -24,7 +24,7 @@ pub struct KafkaAdmin<'a> {
|
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
impl<'a> KafkaAdmin<'a> {
|
|
27
|
-
pub fn new(client_config: &'a ClientConfig, fetch_metadata_timeout: Option<
|
|
27
|
+
pub fn new(client_config: &'a ClientConfig, fetch_metadata_timeout: Option<Duration>) -> Self {
|
|
28
28
|
let admin_client: AdminClient<DefaultClientContext> = client_config
|
|
29
29
|
.create()
|
|
30
30
|
.expect("admin client creation failed");
|
|
@@ -32,9 +32,7 @@ impl<'a> KafkaAdmin<'a> {
|
|
|
32
32
|
KafkaAdmin {
|
|
33
33
|
client_config,
|
|
34
34
|
admin_client,
|
|
35
|
-
fetch_metadata_timeout:
|
|
36
|
-
fetch_metadata_timeout.unwrap_or(DEFAULT_FECTH_METADATA_TIMEOUT) as u64,
|
|
37
|
-
),
|
|
35
|
+
fetch_metadata_timeout: fetch_metadata_timeout.unwrap_or(DEFAULT_FECTH_METADATA_TIMEOUT),
|
|
38
36
|
}
|
|
39
37
|
}
|
|
40
38
|
|
|
@@ -63,31 +61,33 @@ impl<'a> KafkaAdmin<'a> {
|
|
|
63
61
|
Ok(extract_config_resource(config_resource))
|
|
64
62
|
}
|
|
65
63
|
|
|
66
|
-
pub async fn create_topic(&self,
|
|
64
|
+
pub async fn create_topic(&self, topics: &Vec<String>) -> anyhow::Result<()> {
|
|
67
65
|
let broker_properties = self.fetch_config_resource().await?.clone();
|
|
68
66
|
trace!("Broker properties: {:?}", broker_properties);
|
|
69
67
|
|
|
68
|
+
let new_topics: Vec<NewTopic> = topics
|
|
69
|
+
.iter()
|
|
70
|
+
.map(|topic| NewTopic {
|
|
71
|
+
name: topic,
|
|
72
|
+
num_partitions: broker_properties
|
|
73
|
+
.get("num.partitions")
|
|
74
|
+
.get_parsed_or_default_value(DEFAULT_NUM_PARTITIONS),
|
|
75
|
+
replication: TopicReplication::Fixed(
|
|
76
|
+
broker_properties
|
|
77
|
+
.get("default.replication.factor")
|
|
78
|
+
.get_parsed_or_default_value(DEFAULT_REPLICATION),
|
|
79
|
+
),
|
|
80
|
+
config: vec![],
|
|
81
|
+
})
|
|
82
|
+
.collect();
|
|
83
|
+
|
|
70
84
|
self
|
|
71
85
|
.admin_client
|
|
72
|
-
.create_topics(
|
|
73
|
-
&[NewTopic {
|
|
74
|
-
name: topic_name,
|
|
75
|
-
num_partitions: broker_properties
|
|
76
|
-
.get("num.partitions")
|
|
77
|
-
.get_parsed_or_default_value(DEFAULT_NUM_PARTITIONS),
|
|
78
|
-
replication: TopicReplication::Fixed(
|
|
79
|
-
broker_properties
|
|
80
|
-
.get("default.replication.factor")
|
|
81
|
-
.get_parsed_or_default_value(DEFAULT_REPLICATION),
|
|
82
|
-
),
|
|
83
|
-
config: vec![],
|
|
84
|
-
}],
|
|
85
|
-
&AdminOptions::default(),
|
|
86
|
-
)
|
|
86
|
+
.create_topics(&new_topics, &AdminOptions::default())
|
|
87
87
|
.await
|
|
88
88
|
.map_err(anyhow::Error::new)?;
|
|
89
89
|
|
|
90
|
-
info!("Topic {} was created successfully",
|
|
90
|
+
info!("Topic(s) {:?} was created successfully", topics);
|
|
91
91
|
Ok(())
|
|
92
92
|
}
|
|
93
93
|
}
|
|
@@ -1,15 +1,9 @@
|
|
|
1
1
|
use std::{collections::HashMap, fmt, str::FromStr};
|
|
2
2
|
|
|
3
3
|
use napi::{bindgen_prelude::*, Result};
|
|
4
|
-
use rdkafka::{
|
|
5
|
-
client::ClientContext,
|
|
6
|
-
config::{ClientConfig, RDKafkaLogLevel},
|
|
7
|
-
consumer::{ConsumerContext, Rebalance},
|
|
8
|
-
error::KafkaResult,
|
|
9
|
-
topic_partition_list::TopicPartitionList,
|
|
10
|
-
};
|
|
4
|
+
use rdkafka::config::{ClientConfig, RDKafkaLogLevel};
|
|
11
5
|
|
|
12
|
-
use tracing::
|
|
6
|
+
use tracing::Level;
|
|
13
7
|
|
|
14
8
|
use super::{
|
|
15
9
|
consumer::{
|
|
@@ -20,24 +14,6 @@ use super::{
|
|
|
20
14
|
producer::kafka_producer::{KafkaProducer, ProducerConfiguration},
|
|
21
15
|
};
|
|
22
16
|
|
|
23
|
-
struct CustomContext;
|
|
24
|
-
|
|
25
|
-
impl ClientContext for CustomContext {}
|
|
26
|
-
|
|
27
|
-
impl ConsumerContext for CustomContext {
|
|
28
|
-
fn pre_rebalance(&self, rebalance: &Rebalance) {
|
|
29
|
-
info!("Pre rebalance {:?}", rebalance);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
fn post_rebalance(&self, rebalance: &Rebalance) {
|
|
33
|
-
info!("Post rebalance {:?}", rebalance);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
fn commit_callback(&self, result: KafkaResult<()>, _offsets: &TopicPartitionList) {
|
|
37
|
-
info!("Committing offsets: {:?}. Offset: {:?}", result, _offsets);
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
17
|
#[derive(Debug)]
|
|
42
18
|
#[napi(string_enum)]
|
|
43
19
|
pub enum SecurityProtocol {
|
package/src/kafka/kafka_util.rs
CHANGED
|
@@ -3,13 +3,10 @@ use std::collections::HashMap;
|
|
|
3
3
|
use napi::{bindgen_prelude::Buffer, Status};
|
|
4
4
|
use rdkafka::{
|
|
5
5
|
message::{BorrowedHeaders, BorrowedMessage, Header, Headers, OwnedHeaders},
|
|
6
|
-
Message as RdMessage,
|
|
6
|
+
Message as RdMessage,
|
|
7
7
|
};
|
|
8
8
|
|
|
9
|
-
use super::
|
|
10
|
-
model::{OffsetModel, PartitionPosition},
|
|
11
|
-
producer::model::{Message, Payload},
|
|
12
|
-
};
|
|
9
|
+
use super::producer::model::Message;
|
|
13
10
|
|
|
14
11
|
pub trait AnyhowToNapiError {
|
|
15
12
|
fn convert_to_napi(&self) -> napi::Error;
|
|
@@ -21,18 +18,6 @@ impl AnyhowToNapiError for anyhow::Error {
|
|
|
21
18
|
}
|
|
22
19
|
}
|
|
23
20
|
|
|
24
|
-
pub fn convert_to_rdkafka_offset(offset_model: &OffsetModel) -> Offset {
|
|
25
|
-
match offset_model.position {
|
|
26
|
-
Some(PartitionPosition::Beginning) => Offset::Beginning,
|
|
27
|
-
Some(PartitionPosition::End) => Offset::End,
|
|
28
|
-
Some(PartitionPosition::Stored) => Offset::Stored,
|
|
29
|
-
None => match offset_model.offset {
|
|
30
|
-
Some(value) => Offset::Offset(value),
|
|
31
|
-
None => Offset::Stored, // Default to stored
|
|
32
|
-
},
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
21
|
pub fn hashmap_to_kafka_headers(map: &HashMap<String, Buffer>) -> OwnedHeaders {
|
|
37
22
|
map.iter().fold(OwnedHeaders::new(), |acc, (key, value)| {
|
|
38
23
|
let value: &[u8] = value.as_ref();
|
|
@@ -89,20 +74,6 @@ impl ExtractValueOnKafkaHashMap<usize> for HashMap<&str, &[u8]> {
|
|
|
89
74
|
}
|
|
90
75
|
}
|
|
91
76
|
|
|
92
|
-
pub fn create_payload(message: &BorrowedMessage<'_>, payload: &[u8]) -> Payload {
|
|
93
|
-
let key: Option<Buffer> = message.key().map(|bytes| bytes.into());
|
|
94
|
-
let headers = Some(kakfa_headers_to_hashmap_buffer(message.headers()));
|
|
95
|
-
let payload_js = Payload::new(
|
|
96
|
-
payload.into(),
|
|
97
|
-
key,
|
|
98
|
-
headers,
|
|
99
|
-
message.topic().to_owned(),
|
|
100
|
-
message.partition(),
|
|
101
|
-
message.offset(),
|
|
102
|
-
);
|
|
103
|
-
payload_js
|
|
104
|
-
}
|
|
105
|
-
|
|
106
77
|
pub fn create_message(message: &BorrowedMessage<'_>, payload: &[u8]) -> Message {
|
|
107
78
|
let key: Option<Buffer> = message.key().map(|bytes| bytes.into());
|
|
108
79
|
let headers = Some(kakfa_headers_to_hashmap_buffer(message.headers()));
|
package/src/kafka/mod.rs
CHANGED
|
@@ -16,11 +16,9 @@ use rdkafka::{
|
|
|
16
16
|
ClientConfig, ClientContext, Message, Statistics,
|
|
17
17
|
};
|
|
18
18
|
|
|
19
|
-
use napi::bindgen_prelude::Buffer;
|
|
20
|
-
|
|
21
19
|
use crate::kafka::kafka_util::hashmap_to_kafka_headers;
|
|
22
20
|
|
|
23
|
-
use super::model::KafkaCrabError;
|
|
21
|
+
use super::model::{KafkaCrabError, MessageProducer, ProducerRecord, RecordMetadata};
|
|
24
22
|
|
|
25
23
|
const DEFAULT_QUEUE_TIMEOUT: i64 = 5000;
|
|
26
24
|
|
|
@@ -82,23 +80,6 @@ where
|
|
|
82
80
|
.unwrap()
|
|
83
81
|
}
|
|
84
82
|
|
|
85
|
-
#[napi(object)]
|
|
86
|
-
#[derive(Clone)]
|
|
87
|
-
pub struct RecordMetadata {
|
|
88
|
-
pub topic: String,
|
|
89
|
-
pub partition: i32,
|
|
90
|
-
pub offset: i64,
|
|
91
|
-
pub error: Option<KafkaCrabError>,
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
#[napi(object)]
|
|
95
|
-
#[derive(Clone)]
|
|
96
|
-
pub struct MessageProducer {
|
|
97
|
-
pub value: Buffer,
|
|
98
|
-
pub key: Option<Buffer>,
|
|
99
|
-
pub headers: Option<HashMap<String, Buffer>>,
|
|
100
|
-
}
|
|
101
|
-
|
|
102
83
|
#[napi(object)]
|
|
103
84
|
#[derive(Clone, Debug)]
|
|
104
85
|
pub struct ProducerConfiguration {
|
|
@@ -107,13 +88,6 @@ pub struct ProducerConfiguration {
|
|
|
107
88
|
pub configuration: Option<HashMap<String, String>>,
|
|
108
89
|
}
|
|
109
90
|
|
|
110
|
-
#[napi(object)]
|
|
111
|
-
#[derive(Clone)]
|
|
112
|
-
pub struct ProducerRecord {
|
|
113
|
-
pub topic: String,
|
|
114
|
-
pub messages: Vec<MessageProducer>,
|
|
115
|
-
}
|
|
116
|
-
|
|
117
91
|
#[napi]
|
|
118
92
|
pub struct KafkaProducer {
|
|
119
93
|
queue_timeout: Duration,
|
|
@@ -153,7 +127,9 @@ impl KafkaProducer {
|
|
|
153
127
|
let producer = threaded_producer_with_context(context.clone(), self.client_config.clone());
|
|
154
128
|
|
|
155
129
|
for message in producer_record.messages {
|
|
156
|
-
let MessageProducer {
|
|
130
|
+
let MessageProducer {
|
|
131
|
+
payload, headers, ..
|
|
132
|
+
} = message;
|
|
157
133
|
let headers = match headers {
|
|
158
134
|
Some(v) => hashmap_to_kafka_headers(&v),
|
|
159
135
|
None => OwnedHeaders::new(),
|
|
@@ -167,7 +143,7 @@ impl KafkaProducer {
|
|
|
167
143
|
producer
|
|
168
144
|
.send(
|
|
169
145
|
BaseRecord::to(topic)
|
|
170
|
-
.payload(
|
|
146
|
+
.payload(payload.to_bytes())
|
|
171
147
|
.headers(headers)
|
|
172
148
|
.key(rd_key),
|
|
173
149
|
)
|
|
@@ -65,6 +65,30 @@ impl Message {
|
|
|
65
65
|
}
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
+
#[napi(object)]
|
|
69
|
+
#[derive(Clone)]
|
|
70
|
+
pub struct RecordMetadata {
|
|
71
|
+
pub topic: String,
|
|
72
|
+
pub partition: i32,
|
|
73
|
+
pub offset: i64,
|
|
74
|
+
pub error: Option<KafkaCrabError>,
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
#[napi(object)]
|
|
78
|
+
#[derive(Clone)]
|
|
79
|
+
pub struct MessageProducer {
|
|
80
|
+
pub payload: Buffer,
|
|
81
|
+
pub key: Option<Buffer>,
|
|
82
|
+
pub headers: Option<HashMap<String, Buffer>>,
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
#[napi(object)]
|
|
86
|
+
#[derive(Clone)]
|
|
87
|
+
pub struct ProducerRecord {
|
|
88
|
+
pub topic: String,
|
|
89
|
+
pub messages: Vec<MessageProducer>,
|
|
90
|
+
}
|
|
91
|
+
|
|
68
92
|
#[napi(object)]
|
|
69
93
|
#[derive(Clone)]
|
|
70
94
|
pub struct KafkaCrabError {
|
package/src/kafka/model.rs
DELETED
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
use std::fmt;
|
|
2
|
-
|
|
3
|
-
#[derive(Debug)]
|
|
4
|
-
#[napi(string_enum)]
|
|
5
|
-
pub enum AutoOffsetReset {
|
|
6
|
-
Smallest,
|
|
7
|
-
Earliest,
|
|
8
|
-
Beginning,
|
|
9
|
-
Largest,
|
|
10
|
-
Latest,
|
|
11
|
-
End,
|
|
12
|
-
Error,
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
impl fmt::Display for AutoOffsetReset {
|
|
16
|
-
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
17
|
-
write!(f, "{}", format!("{:?}", self).to_lowercase())
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
#[napi(string_enum)]
|
|
22
|
-
#[derive(Debug)]
|
|
23
|
-
pub enum PartitionPosition {
|
|
24
|
-
Beginning,
|
|
25
|
-
End,
|
|
26
|
-
Stored,
|
|
27
|
-
}
|
|
28
|
-
#[napi(object)]
|
|
29
|
-
#[derive(Clone, Debug)]
|
|
30
|
-
pub struct OffsetModel {
|
|
31
|
-
pub offset: Option<i64>,
|
|
32
|
-
pub position: Option<PartitionPosition>,
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
#[napi(object)]
|
|
36
|
-
#[derive(Clone, Debug)]
|
|
37
|
-
pub struct PartitionOffset {
|
|
38
|
-
pub partition: i32,
|
|
39
|
-
pub offset: OffsetModel,
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
#[napi(object)]
|
|
43
|
-
#[derive(Clone, Debug)]
|
|
44
|
-
pub struct TopicPartitionConfig {
|
|
45
|
-
pub topic: String,
|
|
46
|
-
pub all_offsets: Option<OffsetModel>,
|
|
47
|
-
pub partition_offset: Option<Vec<PartitionOffset>>,
|
|
48
|
-
}
|