kafka-crab-js 1.9.1 → 2.0.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/README.md CHANGED
@@ -10,32 +10,97 @@ A lightweight, flexible, and reliable Kafka client for JavaScript/TypeScript. It
10
10
 
11
11
  ---
12
12
 
13
- ## What's New in Version 1.9.0
13
+ ## What's New in Version 2.0.0
14
+
15
+ ### BREAKING CHANGES ⚠️
16
+
17
+ This major version includes important breaking changes that improve API consistency and memory management:
18
+
19
+ 1. **Consumer Configuration API Changes**:
20
+ - **REMOVED**: `createTopic` field from `ConsumerConfiguration`
21
+ - **Migration**: Use `createTopic` field in `TopicPartitionConfig` instead when subscribing to topics
22
+ - **Before**: `new KafkaClient().createConsumer({ groupId: 'test', createTopic: true })`
23
+ - **After**: `consumer.subscribe([{ topic: 'my-topic', createTopic: true }])`
24
+
25
+ 2. **Stream Lifecycle Management**:
26
+ - Stream consumers now properly implement Node.js stream lifecycle methods (`_destroy()`, `_final()`)
27
+ - **Memory leak prevention**: Streams now automatically disconnect Kafka consumers during destruction
28
+ - Better resource cleanup ensures no hanging connections or memory leaks
29
+ - Error handling during stream destruction is improved with proper error propagation
30
+
31
+ 3. **Stream Error Handling**:
32
+ - Stream errors now use `destroy(error)` instead of `emit('error')` for proper stream termination
33
+ - This ensures streams are properly closed when errors occur, preventing resource leaks
34
+ - Error propagation follows Node.js stream standards more closely
35
+
36
+ 4. **Rust Configuration Simplification**:
37
+ - Removed `MAX_BATCH_TIMEOUT` constant - now uses only `DEFAULT_BATCH_TIMEOUT` (1000ms)
38
+ - Simplified timeout validation logic in Rust layer
39
+ - Invalid timeout values (< 1ms) now log warnings and use default timeout
40
+
41
+ 5. **Code Quality Improvements**:
42
+ - Fixed linting violations: short variable names and `any` type usage
43
+ - Better TypeScript typing throughout the codebase
44
+ - Removed redundant validation functions for cleaner code
45
+
46
+ ### Migration Guide
47
+
48
+ - **No API changes required** - all public interfaces remain the same
49
+ - **Stream memory management** - streams now automatically clean up resources
50
+ - **Error handling** - streams will properly terminate on errors (existing error handling code still works)
51
+ - **Performance** - simplified timeout logic may provide marginal performance improvements
52
+
53
+ ---
54
+
55
+ ## What's New in Version 2.0.0
14
56
 
15
57
  ### Major Updates:
16
- 1. **Consumer Topic Creation Control**:
58
+
59
+ 1. **Performance Benchmark Suite**:
60
+ - Added comprehensive benchmark suite comparing performance against `kafkajs`, `node-rdkafka`, and `@platformatic/rdkafka`
61
+ - Benchmark scripts with automated setup and data generation
62
+ - Performance testing for both serial and batch processing modes
63
+ - Supports configurable iterations and message sizes for realistic testing scenarios
64
+
65
+ 2. **Enhanced Stream Consumer Configuration**:
66
+ - Stream consumer constructor now accepts optional `ReadableOptions` parameter
67
+ - Default `objectMode: true` for better stream processing ergonomics
68
+ - Improved batch processing configuration with proper defaults
69
+
70
+ 3. **Flexible Configuration System**:
71
+ - **BREAKING**: Configuration properties now accept `Record<string, any>` instead of `Record<string, string>`
72
+ - Enhanced type flexibility for complex configuration values (numbers, booleans, objects)
73
+ - Better support for advanced librdkafka configuration options
74
+
75
+ 4. **Enhanced Topic Management**:
76
+ - Added `createTopic`, `numPartitions`, and `replicas` options to `TopicPartitionConfig`
77
+ - Improved topic creation control during consumer subscription
78
+ - Better handling of partition assignment and topic setup
79
+
80
+ 5. **Build System Improvements**:
81
+ - Updated TypeScript configuration with modular `tsconfig.base.json`
82
+ - Added `typecheck` script for standalone type checking
83
+ - Enhanced build pipeline with better declaration file generation
84
+ - Updated to pnpm@10.15.1 for improved dependency management
85
+
86
+ 6. **Development Tools Enhancement**:
87
+ - Enhanced oxlint configuration with benchmark-specific rules
88
+ - Added support for benchmark directory linting and formatting
89
+ - Improved development workflow with better error handling rules
90
+
91
+ ### Previous Version (1.9.0) Updates:
92
+ 7. **Consumer Topic Creation Control**:
17
93
  - Added fine-grained control over consumer topic creation behavior
18
94
  - Enhanced consistency in topic management and cleanup processes
19
95
  - Improved handling of topic creation inconsistencies during consumer operations
20
96
 
21
- 2. **Enhanced Manual Commit Testing**:
97
+ 8. **Enhanced Manual Commit Testing**:
22
98
  - Consolidated manual commit tests for better reliability and coverage
23
99
  - Added comprehensive integration tests with PostRebalance event handling
24
100
  - Tests now verify offset persistence across consumer restarts and batch processing
25
- - Improved timing and synchronization in integration test scenarios
26
-
27
- 3. **Optional Producer Configuration**:
28
- - Producer configuration is now optional with sensible defaults
29
- - Enhanced commit logging for better debugging and monitoring
30
- - Improved flexibility in producer setup and configuration
31
-
32
- 4. **Build System Improvements**:
33
- - Added Cargo.lock tracking for reproducible builds across environments
34
- - Enhanced dependency management and version consistency
35
- - Updated TypeScript bindings and CommonJS exports
36
101
 
37
102
  ### Previous Version (1.8.0) Breaking Changes:
38
- 5. **Async Consumer Commit**:
103
+ 9. **Async Consumer Commit**:
39
104
  - **BREAKING**: The `consumer.commit()` method is now async and must be awaited
40
105
  - **Before**: `consumer.commit(topic, partition, offset, 'Sync')`
41
106
  - **After**: `await consumer.commit(topic, partition, offset, 'Sync')`
@@ -51,9 +116,11 @@ A lightweight, flexible, and reliable Kafka client for JavaScript/TypeScript. It
51
116
  - 🎯 Type-safe interfaces (TypeScript support)
52
117
  - ⚡ Async/await support
53
118
  - 🛠️ Configurable consumer and producer options
54
- - 📊 Stream processing support
119
+ - 📊 Stream processing support with configurable stream options
55
120
  - 📦 Message batching capabilities
56
121
  - 🔍 Comprehensive error handling
122
+ - 📈 Performance benchmarking suite
123
+ - 🔧 Flexible configuration system supporting complex data types
57
124
 
58
125
  ## Table of Contents
59
126
 
@@ -63,9 +130,10 @@ A lightweight, flexible, and reliable Kafka client for JavaScript/TypeScript. It
63
130
  4. [Producer Examples](#producer-examples)
64
131
  5. [Stream Processing](#stream-processing)
65
132
  6. [Configuration](#configuration)
66
- 7. [Best Practices](#best-practices)
67
- 8. [Contributing](#contributing)
68
- 9. [License](#license)
133
+ 7. [Performance Benchmarks](#performance-benchmarks)
134
+ 8. [Best Practices](#best-practices)
135
+ 9. [Contributing](#contributing)
136
+ 10. [License](#license)
69
137
 
70
138
  ## Installation
71
139
 
@@ -91,12 +159,21 @@ async function run() {
91
159
  brokerAddressFamily: 'v4',
92
160
  });
93
161
 
94
- // Create consumer
162
+ // Create consumer with topic creation control
95
163
  const consumer = kafkaClient.createConsumer({
96
164
  groupId: 'foo-group',
165
+ configuration: {
166
+ 'auto.offset.reset': 'earliest',
167
+ 'enable.auto.commit': false, // Use manual commit for better control
168
+ }
97
169
  });
98
170
 
99
- await consumer.subscribe([{ topic: 'foo' }]);
171
+ // Subscribe with topic creation options
172
+ await consumer.subscribe([{
173
+ topic: 'foo',
174
+ createTopic: true,
175
+ numPartitions: 3
176
+ }]);
100
177
 
101
178
  const message = await consumer.recv();
102
179
  const { payload, partition, offset, topic } = message;
@@ -111,7 +188,6 @@ async function run() {
111
188
  await consumer.commit(topic, partition, offset + 1, 'Sync');
112
189
 
113
190
  consumer.unsubscribe();
114
-
115
191
  }
116
192
 
117
193
  await run();
@@ -129,7 +205,14 @@ const kafkaClient = new KafkaClient({
129
205
  brokerAddressFamily: 'v4',
130
206
  });
131
207
 
132
- const producer = kafkaClient.createProducer({ configuration: { 'message.timeout.ms': '5000' } });
208
+ // Producer configuration is now optional with sensible defaults
209
+ const producer = kafkaClient.createProducer({
210
+ configuration: {
211
+ 'message.timeout.ms': 5000, // Now supports number values
212
+ 'batch.size': 16384,
213
+ 'compression.type': 'snappy'
214
+ }
215
+ });
133
216
 
134
217
  const message = {
135
218
  id: 1,
@@ -150,12 +233,11 @@ if (errors.length > 0) {
150
233
  } else {
151
234
  console.log('Message sent. Offset:', result);
152
235
  }
153
-
154
236
  ```
155
237
 
156
238
  ## Stream Processing
157
239
 
158
- ### Stream Consumer Example
240
+ ### Enhanced Stream Consumer Example
159
241
 
160
242
  ```javascript
161
243
  import { KafkaClient } from 'kafka-crab-js';
@@ -167,24 +249,38 @@ const kafkaClient = new KafkaClient({
167
249
  brokerAddressFamily: 'v4',
168
250
  });
169
251
 
252
+ // Stream consumer with custom ReadableOptions (v1.10.0+)
170
253
  const kafkaStream = kafkaClient.createStreamConsumer({
171
- groupId: `my-groud-id`,
254
+ groupId: `my-group-id`,
172
255
  enableAutoCommit: true,
256
+ }, {
257
+ objectMode: true, // Default in v1.10.0+
258
+ highWaterMark: 1024,
259
+ encoding: null
173
260
  });
174
261
 
175
- await kafkaStream.subscribe([{ topic: 'foo' }, { topic: 'bar' }])
262
+ await kafkaStream.subscribe([
263
+ { topic: 'foo', createTopic: true },
264
+ { topic: 'bar', createTopic: true }
265
+ ]);
176
266
 
177
267
  kafkaStream.on('data', (message) => {
178
- console.log('>>> Message received:', { payload: message.payload.toString(), offset: message.offset, partition: message.partition, topic: message.topic })
268
+ console.log('>>> Message received:', {
269
+ payload: message.payload.toString(),
270
+ offset: message.offset,
271
+ partition: message.partition,
272
+ topic: message.topic
273
+ });
274
+
179
275
  if (message.offset > 10) {
180
276
  kafkaStream.destroy();
181
277
  }
182
- })
278
+ });
183
279
 
184
280
  kafkaStream.on('close', () => {
185
281
  kafkaStream.unsubscribe();
186
- console.log('Stream ended')
187
- })
282
+ console.log('Stream ended');
283
+ });
188
284
  ```
189
285
 
190
286
  ## Producer Examples
@@ -197,7 +293,16 @@ const kafkaClient = new KafkaClient({
197
293
  clientId: 'my-client-id',
198
294
  brokerAddressFamily: 'v4',
199
295
  });
200
- const producer = kafkaClient.createProducer({});
296
+
297
+ // Enhanced producer with flexible configuration
298
+ const producer = kafkaClient.createProducer({
299
+ configuration: {
300
+ 'batch.size': 50000, // Number value supported
301
+ 'linger.ms': 10, // Number value supported
302
+ 'compression.type': 'lz4',
303
+ 'enable.idempotence': true // Boolean value supported
304
+ }
305
+ });
201
306
 
202
307
  const messages = Array.from({ length: 100 }, (_, i) => ({
203
308
  payload: Buffer.from(JSON.stringify({
@@ -223,11 +328,17 @@ try {
223
328
 
224
329
  ```javascript
225
330
  async function produceWithMetadata() {
226
- const producer = await kafkaCrab.createProducer({ config });
331
+ const producer = kafkaClient.createProducer({
332
+ configuration: {
333
+ 'acks': 'all',
334
+ 'retries': 5,
335
+ 'max.in.flight.requests.per.connection': 1
336
+ }
337
+ });
227
338
 
228
339
  try {
229
340
  await producer.send({
230
- topic,
341
+ topic: 'user-events',
231
342
  messages: [{
232
343
  key: 'user-123',
233
344
  payload: Buffer.from(JSON.stringify({
@@ -258,6 +369,8 @@ const kafkaClient = new KafkaClient({
258
369
  brokerAddressFamily: 'v4',
259
370
  configuration: {
260
371
  'auto.offset.reset': 'earliest',
372
+ 'session.timeout.ms': 30000,
373
+ 'heartbeat.interval.ms': 10000
261
374
  },
262
375
  })
263
376
 
@@ -268,10 +381,14 @@ async function createConsumer() {
268
381
  const kafkaStream = kafkaClient.createStreamConsumer({
269
382
  groupId: 'reconnect-test',
270
383
  enableAutoCommit: true,
384
+ }, {
385
+ highWaterMark: 100,
386
+ objectMode: true
271
387
  })
388
+
272
389
  await kafkaStream.subscribe([
273
- { topic: 'foo' },
274
- { topic: 'bar' },
390
+ { topic: 'foo', createTopic: true },
391
+ { topic: 'bar', createTopic: true },
275
392
  ])
276
393
  return kafkaStream
277
394
  }
@@ -343,7 +460,7 @@ await startConsumer()
343
460
 
344
461
  ### Examples
345
462
 
346
- You can find some examples on the [example](https://github.com/mthssdrbrg/kafka-crab-js/tree/main/example) folder of this project.
463
+ You can find some examples on the [example](https://github.com/flash-tecnologia/kafka-crab-js/tree/main/example) folder of this project.
347
464
 
348
465
  ## Configuration
349
466
 
@@ -356,13 +473,101 @@ You can find some examples on the [example](https://github.com/mthssdrbrg/kafka-
356
473
  | `brokers` | `string` || List of brokers to connect to |
357
474
  | `clientId` | `string` || Client id to use for the connection |
358
475
  | `securityProtocol` | `SecurityProtocol` || Security protocol to use (PLAINTEXT, SSL, SASL_PLAINTEXT, SASL_SSL) |
359
- | `logLevel` | `string` | `info` | Client id to use for the connection |
476
+ | `logLevel` | `string` | `info` | Log level for the client |
360
477
  | `brokerAddressFamily` | `string` | `"v4"` | Address family to use for the connection (v4, v6) |
361
- | `configuration` | `Map<string, string>` | `{}` | Additional configuration options for the client. See [librdkafka](https://docs.confluent.io/platform/current/clients/librdkafka/html/md_CONFIGURATION.html) |
478
+ | `configuration` | `Record<string, any>` | `{}` | Additional configuration options for the client. **v1.10.0+**: Now supports any value type (string, number, boolean, object) |
479
+
480
+ ### ConsumerConfiguration
481
+
482
+ | Property | Type | Default | Description |
483
+ | --- | --- | --- | --- |
484
+ | `groupId` | `string` || Consumer group ID |
485
+ | `enableAutoCommit` | `boolean` | `true` | Enable automatic offset commits |
486
+ | `configuration` | `Record<string, any>` | `{}` | Additional consumer configuration options |
487
+ | `fetchMetadataTimeout` | `number` | `60000` | Timeout for fetching metadata (ms) |
488
+ | `maxBatchMessages` | `number` | `1000` | Maximum messages in a batch operation |
489
+
490
+ ### ProducerConfiguration
491
+
492
+ | Property | Type | Default | Description |
493
+ | --- | --- | --- | --- |
494
+ | `queueTimeout` | `number` | `5000` | Queue timeout in milliseconds |
495
+ | `autoFlush` | `boolean` | `true` | Enable automatic message flushing |
496
+ | `configuration` | `Record<string, any>` | `{}` | Additional producer configuration options |
497
+
498
+ ### TopicPartitionConfig
499
+
500
+ | Property | Type | Default | Description |
501
+ | --- | --- | --- | --- |
502
+ | `topic` | `string` || Topic name |
503
+ | `allOffsets` | `OffsetModel` || Offset configuration for all partitions |
504
+ | `partitionOffset` | `Array<PartitionOffset>` || Per-partition offset configuration |
505
+ | `createTopic` | `boolean` | `false` | **v2.0.x+**: Create topic if it doesn't exist |
506
+ | `numPartitions` | `number` | `1` | **v2.0.x+**: Number of partitions when creating topic |
507
+ | `replicas` | `number` | `1` | **v2.0.x+**: Number of replicas when creating topic |
362
508
 
363
509
  You can see the available options here: [librdkafka](https://docs.confluent.io/platform/current/clients/librdkafka/html/md_CONFIGURATION.html).
364
510
 
511
+ ## Performance Benchmarks
512
+
513
+ ### Running Benchmarks
514
+
515
+ kafka-crab-js v1.10.0+ includes a comprehensive benchmark suite to compare performance against other popular Kafka clients:
516
+
517
+ ```bash
518
+ # Set up benchmark environment (requires Kafka running locally)
519
+ pnpm add -D tsx # For running TypeScript files directly
520
+ npx tsx benchmark/utils/setup.ts
521
+
522
+ # Run consumer performance benchmarks
523
+ node benchmark/consumer.ts
524
+ ```
525
+
526
+ ### Benchmark Results
527
+
528
+ *Benchmarks run on macOS with Apple M1 chip processing 50,000 messages*
529
+
530
+ ```
531
+ ╔════════════════════════╤═════════╤══════════════════╤═══════════╤══════════════════════════╗
532
+ ║ Slower tests │ Samples │ Result │ Tolerance │ Difference with previous ║
533
+ ╟────────────────────────┼─────────┼──────────────────┼───────────┼──────────────────────────╢
534
+ ║ kafkajs │ 50000 │ 2316.67 op/sec │ ± 1.12 % │ ║
535
+ ║ kafka-crab-js (serial) │ 50000 │ 38414.57 op/sec │ ± 2.97 % │ + 1558.18 % ║
536
+ ║ node-rdkafka (stream) │ 50000 │ 43681.44 op/sec │ ± 13.98 % │ + 13.71 % ║
537
+ ║ node-rdkafka (evented) │ 53841 │ 74137.97 op/sec │ ± 69.30 % │ + 69.72 % ║
538
+ ╟────────────────────────┼─────────┼──────────────────┼───────────┼──────────────────────────╢
539
+ ║ Fastest test │ Samples │ Result │ Tolerance │ Difference with previous ║
540
+ ╟────────────────────────┼─────────┼──────────────────┼───────────┼──────────────────────────╢
541
+ ║ kafka-crab-js (batch) │ 50000 │ 153533.53 op/sec │ ± 15.98 % │ + 107.09 % ║
542
+ ╚════════════════════════╧═════════╧══════════════════╧═══════════╧══════════════════════════╝
543
+ ```
544
+
545
+ The benchmark suite compares:
546
+ - **kafka-crab-js (serial)**: Single message processing - **38,415 ops/sec**
547
+ - **kafka-crab-js (batch)**: Batch message processing - **153,534 ops/sec** (fastest)
548
+ - **node-rdkafka (evented)**: Event-based processing - **74,138 ops/sec**
549
+ - **node-rdkafka (stream)**: Stream-based processing - **43,681 ops/sec**
550
+ - **kafkajs**: Official KafkaJS client - **2,317 ops/sec**
551
+
552
+ Performance characteristics:
553
+ - **17x faster than kafkajs** in serial mode, **66x faster in batch mode**
554
+ - **High throughput**: Batch processing provides 4x performance improvement over serial mode
555
+ - **Low latency**: Optimized for both single and batch message processing
556
+ - **Memory efficient**: Lock-free data structures minimize memory overhead
557
+ - **Concurrent processing**: Zero-contention concurrent operations
558
+
559
+ ### Benchmark Configuration
365
560
 
561
+ You can customize benchmark parameters in `benchmark/utils/definitions.ts`:
562
+
563
+ ```typescript
564
+ export const topic = 'benchmarks'
565
+ export const brokers = ['localhost:9092', 'localhost:9093', 'localhost:9094']
566
+
567
+ // Benchmark parameters can be adjusted in consumer.ts:
568
+ const iterations = 10_000 // Number of messages to process
569
+ const maxBytes = 200 // Maximum message size
570
+ ```
366
571
 
367
572
  ## Best Practices
368
573
 
@@ -373,21 +578,60 @@ You can see the available options here: [librdkafka](https://docs.confluent.io/p
373
578
 
374
579
  ### Performance
375
580
  - Use batch operations for high-throughput scenarios
376
- - Configure appropriate batch sizes and compression
581
+ - Configure appropriate batch sizes and compression settings
377
582
  - Monitor and tune consumer group performance
583
+ - Leverage the benchmark suite to optimize your specific use case
584
+
585
+ ### Configuration (v1.10.0+)
586
+ - Use the flexible configuration system with proper data types:
587
+ ```javascript
588
+ const config = {
589
+ 'batch.size': 16384, // number
590
+ 'compression.type': 'snappy', // string
591
+ 'enable.idempotence': true, // boolean
592
+ 'retries': 5 // number
593
+ }
594
+ ```
378
595
 
379
596
  ### Message Processing
380
597
  - Validate message formats before processing
381
598
  - Implement proper serialization/deserialization
382
599
  - Handle message ordering when required
600
+ - Use topic creation options for better topic management
601
+
602
+ ### Stream Processing (v1.10.0+)
603
+ - Configure appropriate `ReadableOptions` for your use case
604
+ - Use `objectMode: true` for structured message processing
605
+ - Set appropriate `highWaterMark` based on memory constraints
383
606
 
384
607
  ## Contributing
385
608
 
386
609
  1. Fork the repository
387
610
  2. Create your feature branch (`git checkout -b feature/amazing-feature`)
388
- 3. Commit your changes (`git commit -m 'Add some amazing feature'`)
389
- 4. Push to the branch (`git push origin feature/amazing-feature`)
390
- 5. Open a Pull Request
611
+ 3. Run the benchmark suite to ensure performance isn't degraded
612
+ 4. Commit your changes (`git commit -m 'Add some amazing feature'`)
613
+ 5. Push to the branch (`git push origin feature/amazing-feature`)
614
+ 6. Open a Pull Request
615
+
616
+ ### Development Commands
617
+
618
+ ```bash
619
+ # Build the project
620
+ pnpm build
621
+
622
+ # Run type checking
623
+ pnpm typecheck
624
+
625
+ # Run linting
626
+ pnpm lint
627
+
628
+ # Format code
629
+ pnpm fmt
630
+
631
+ # Run benchmarks
632
+ npx tsx benchmark/utils/setup.ts
633
+ npx tsx benchmark/consumer.ts
634
+ ```
391
635
 
392
636
  ## License
393
637