@platformatic/kafka 1.30.0 → 1.31.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/dist/apis/callbacks.js +1 -1
- package/dist/clients/admin/admin.d.ts +3 -1
- package/dist/clients/admin/admin.js +63 -35
- package/dist/clients/admin/options.d.ts +22 -0
- package/dist/clients/admin/options.js +15 -1
- package/dist/clients/admin/types.d.ts +11 -1
- package/dist/clients/base/base.d.ts +4 -1
- package/dist/clients/base/base.js +17 -2
- package/dist/clients/base/index.d.ts +1 -1
- package/dist/clients/base/index.js +1 -1
- package/dist/clients/base/options.d.ts +1 -0
- package/dist/clients/base/options.js +1 -0
- package/dist/clients/base/types.d.ts +1 -0
- package/dist/clients/consumer/consumer.d.ts +4 -1
- package/dist/clients/consumer/consumer.js +29 -2
- package/dist/clients/consumer/messages-stream.d.ts +1 -0
- package/dist/clients/consumer/messages-stream.js +61 -15
- package/dist/clients/consumer/options.d.ts +3 -0
- package/dist/clients/consumer/options.js +3 -0
- package/dist/clients/consumer/types.d.ts +3 -0
- package/dist/clients/producer/producer.js +9 -3
- package/dist/errors.js +9 -1
- package/dist/network/connection-pool.d.ts +2 -0
- package/dist/network/connection-pool.js +11 -2
- package/dist/network/connection.d.ts +3 -0
- package/dist/network/connection.js +34 -4
- package/dist/typescript-4/dist/clients/admin/admin.d.ts +3 -1
- package/dist/typescript-4/dist/clients/admin/options.d.ts +22 -0
- package/dist/typescript-4/dist/clients/admin/types.d.ts +11 -1
- package/dist/typescript-4/dist/clients/base/base.d.ts +4 -1
- package/dist/typescript-4/dist/clients/base/index.d.ts +1 -1
- package/dist/typescript-4/dist/clients/base/options.d.ts +1 -0
- package/dist/typescript-4/dist/clients/base/types.d.ts +1 -0
- package/dist/typescript-4/dist/clients/consumer/consumer.d.ts +4 -1
- package/dist/typescript-4/dist/clients/consumer/messages-stream.d.ts +1 -0
- package/dist/typescript-4/dist/clients/consumer/options.d.ts +3 -0
- package/dist/typescript-4/dist/clients/consumer/types.d.ts +3 -0
- package/dist/typescript-4/dist/network/connection-pool.d.ts +2 -0
- package/dist/typescript-4/dist/network/connection.d.ts +3 -0
- package/dist/version.js +1 -1
- package/package.json +2 -1
|
@@ -60,6 +60,7 @@ export class MessagesStream extends Readable {
|
|
|
60
60
|
#closeCallbacks;
|
|
61
61
|
#metricsConsumedMessages;
|
|
62
62
|
#corruptedMessageHandler;
|
|
63
|
+
#context;
|
|
63
64
|
#pushRecordsOperation;
|
|
64
65
|
[kInstance];
|
|
65
66
|
/*
|
|
@@ -79,7 +80,7 @@ export class MessagesStream extends Readable {
|
|
|
79
80
|
*/
|
|
80
81
|
[kConnections];
|
|
81
82
|
constructor(consumer, options) {
|
|
82
|
-
const { autocommit, mode, fallbackMode, maxFetches, offsets, deserializers, onCorruptedMessage, registry, beforeDeserialization,
|
|
83
|
+
const { autocommit, mode, fallbackMode, maxFetches, offsets, context, deserializers, onCorruptedMessage, registry, beforeDeserialization,
|
|
83
84
|
// The options below are only destructured to avoid being part of structuredClone below
|
|
84
85
|
partitionAssigner: _partitionAssigner, ...otherOptions } = options;
|
|
85
86
|
if (offsets && mode !== MessagesStreamModes.MANUAL) {
|
|
@@ -94,7 +95,7 @@ export class MessagesStream extends Readable {
|
|
|
94
95
|
highWaterMark: maxFetches ?? options.highWaterMark ?? defaultConsumerOptions.highWaterMark
|
|
95
96
|
});
|
|
96
97
|
this[kInstance] = currentInstance++;
|
|
97
|
-
this[kConnections] = consumer[kCreateConnectionPool]();
|
|
98
|
+
this[kConnections] = consumer[kCreateConnectionPool](context);
|
|
98
99
|
this.#consumer = consumer;
|
|
99
100
|
this.#mode = mode ?? MessagesStreamModes.LATEST;
|
|
100
101
|
this.#fallbackMode = fallbackMode ?? MessagesStreamFallbackModes.LATEST;
|
|
@@ -108,7 +109,7 @@ export class MessagesStream extends Readable {
|
|
|
108
109
|
this.#fetches = 0;
|
|
109
110
|
this.#maxFetches = maxFetches ?? 0;
|
|
110
111
|
this.#topics = structuredClone(options.topics);
|
|
111
|
-
this.#inflightNodes = new
|
|
112
|
+
this.#inflightNodes = new Map();
|
|
112
113
|
this.#keyDeserializer =
|
|
113
114
|
deserializers?.key ?? noopDeserializer;
|
|
114
115
|
this.#valueDeserializer =
|
|
@@ -120,6 +121,7 @@ export class MessagesStream extends Readable {
|
|
|
120
121
|
this.#closed = false;
|
|
121
122
|
this.#closeCallbacks = [];
|
|
122
123
|
this.#corruptedMessageHandler = onCorruptedMessage ?? defaultCorruptedMessageHandler;
|
|
124
|
+
this.#context = context;
|
|
123
125
|
if (registry) {
|
|
124
126
|
this.#pushRecordsOperation = this.#beforeDeserialization.bind(this, registry.getBeforeDeserializationHook());
|
|
125
127
|
}
|
|
@@ -150,6 +152,7 @@ export class MessagesStream extends Readable {
|
|
|
150
152
|
// having some.
|
|
151
153
|
this.#consumer.on('consumer:group:join', () => {
|
|
152
154
|
this.#offsetsCommitted.clear();
|
|
155
|
+
this.#partitionsEpochs.clear();
|
|
153
156
|
this.#scheduleRefreshOffsetsAndFetch();
|
|
154
157
|
});
|
|
155
158
|
if (consumer[kPrometheus]) {
|
|
@@ -169,6 +172,9 @@ export class MessagesStream extends Readable {
|
|
|
169
172
|
get offsetsToFetch() {
|
|
170
173
|
return this.#offsetsToFetch;
|
|
171
174
|
}
|
|
175
|
+
get context() {
|
|
176
|
+
return this.#context;
|
|
177
|
+
}
|
|
172
178
|
/* c8 ignore next 3 - Simple getter */
|
|
173
179
|
get offsetsToCommit() {
|
|
174
180
|
return this.#offsetsToCommit;
|
|
@@ -239,8 +245,28 @@ export class MessagesStream extends Readable {
|
|
|
239
245
|
return this.#consumer.isConnected();
|
|
240
246
|
}
|
|
241
247
|
resume() {
|
|
248
|
+
const wasPaused = this.#paused;
|
|
242
249
|
this.#paused = false;
|
|
243
|
-
|
|
250
|
+
const result = super.resume();
|
|
251
|
+
// Restart the fetch loop when transitioning from paused → unpaused.
|
|
252
|
+
//
|
|
253
|
+
// When a downstream Duplex (e.g. a batching stream) signals backpressure,
|
|
254
|
+
// pipeline()/pipe() calls pause() on this stream, setting #paused = true.
|
|
255
|
+
// If a previously scheduled process.nextTick(#fetch) fires while #paused is
|
|
256
|
+
// true, #fetch() returns early without scheduling another iteration — the
|
|
257
|
+
// loop is now dead. Later, pipe() calls resume() when the downstream drains,
|
|
258
|
+
// but super.resume() does not reliably trigger _read() when the readable
|
|
259
|
+
// buffer is already empty and the stream is in flowing mode (Node.js
|
|
260
|
+
// considers it "already flowing" and skips the _read() → #fetch() path).
|
|
261
|
+
//
|
|
262
|
+
// The wasPaused guard prevents a premature fetch during initial pipeline()
|
|
263
|
+
// setup, where resume() is called before _construct() completes.
|
|
264
|
+
if (wasPaused) {
|
|
265
|
+
process.nextTick(() => {
|
|
266
|
+
this.#fetch();
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
return result;
|
|
244
270
|
}
|
|
245
271
|
// We want to track if the stream is paused explicitly by the user, while isPaused from Node.js can also
|
|
246
272
|
// be true if the stream is paused because there is no consumer.
|
|
@@ -278,7 +304,9 @@ export class MessagesStream extends Readable {
|
|
|
278
304
|
if (this.#autocommitInterval) {
|
|
279
305
|
clearInterval(this.#autocommitInterval);
|
|
280
306
|
}
|
|
281
|
-
|
|
307
|
+
this[kConnections].close(closeError => {
|
|
308
|
+
callback(closeError ?? error);
|
|
309
|
+
});
|
|
282
310
|
}
|
|
283
311
|
_read() {
|
|
284
312
|
this.#fetch();
|
|
@@ -311,6 +339,14 @@ export class MessagesStream extends Readable {
|
|
|
311
339
|
this.push(null);
|
|
312
340
|
return;
|
|
313
341
|
}
|
|
342
|
+
// Remove stale inflight entries that have been pending for too long.
|
|
343
|
+
// This prevents permanent partition starvation if a fetch callback is never invoked.
|
|
344
|
+
const now = Date.now();
|
|
345
|
+
for (const [node, timestamp] of this.#inflightNodes) {
|
|
346
|
+
if (now - timestamp > 120_000) {
|
|
347
|
+
this.#inflightNodes.delete(node);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
314
350
|
const requests = new Map();
|
|
315
351
|
const topicIds = new Map();
|
|
316
352
|
// Group topic-partitions by the destination broker
|
|
@@ -351,9 +387,12 @@ export class MessagesStream extends Readable {
|
|
|
351
387
|
});
|
|
352
388
|
}
|
|
353
389
|
}
|
|
390
|
+
if (requests.size === 0) {
|
|
391
|
+
return;
|
|
392
|
+
}
|
|
354
393
|
for (const [leader, leaderRequests] of requests) {
|
|
355
|
-
this.#inflightNodes.
|
|
356
|
-
this.#consumer.fetch({ ...this.#options, node: leader, topics: leaderRequests }, (error, response) => {
|
|
394
|
+
this.#inflightNodes.set(leader, Date.now());
|
|
395
|
+
this.#consumer.fetch({ ...this.#options, node: leader, topics: leaderRequests, connectionPool: this[kConnections] }, (error, response) => {
|
|
357
396
|
this.#inflightNodes.delete(leader);
|
|
358
397
|
this.emit('fetch');
|
|
359
398
|
if (error) {
|
|
@@ -381,7 +420,6 @@ export class MessagesStream extends Readable {
|
|
|
381
420
|
}
|
|
382
421
|
#pushRecords(metadata, topicIds, response, requestedOffsets) {
|
|
383
422
|
const autocommit = this.#autocommitEnabled;
|
|
384
|
-
let canPush = true;
|
|
385
423
|
const keyDeserializer = this.#keyDeserializer;
|
|
386
424
|
const valueDeserializer = this.#valueDeserializer;
|
|
387
425
|
const headerKeyDeserializer = this.#headerKeyDeserializer;
|
|
@@ -406,7 +444,7 @@ export class MessagesStream extends Readable {
|
|
|
406
444
|
const firstTimestamp = batch.firstTimestamp;
|
|
407
445
|
const firstOffset = batch.firstOffset;
|
|
408
446
|
const leaderEpoch = metadata.topics.get(topic).partitions[partition].leaderEpoch;
|
|
409
|
-
this.#partitionsEpochs.set(`${topic}:${partition}`,
|
|
447
|
+
this.#partitionsEpochs.set(`${topic}:${partition}`, leaderEpoch);
|
|
410
448
|
// Track offsets
|
|
411
449
|
if (batch === recordsBatches[recordsBatches.length - 1]) {
|
|
412
450
|
// Track the last read offset
|
|
@@ -469,7 +507,7 @@ export class MessagesStream extends Readable {
|
|
|
469
507
|
};
|
|
470
508
|
diagnosticContext.result = message;
|
|
471
509
|
consumerReceivesChannel.asyncStart.publish(diagnosticContext);
|
|
472
|
-
|
|
510
|
+
this.push(message);
|
|
473
511
|
consumerReceivesChannel.asyncEnd.publish(diagnosticContext);
|
|
474
512
|
}
|
|
475
513
|
catch (error) {
|
|
@@ -491,11 +529,19 @@ export class MessagesStream extends Readable {
|
|
|
491
529
|
if (this.#autocommitEnabled && !this.#autocommitInterval) {
|
|
492
530
|
this[kAutocommit]();
|
|
493
531
|
}
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
532
|
+
// Always schedule the next fetch, even when push() returned false.
|
|
533
|
+
//
|
|
534
|
+
// In pull mode, _read() would restart the loop once the buffer drains.
|
|
535
|
+
// In flowing mode with pipeline(), however, _read() is not reliably called
|
|
536
|
+
// again when the buffer is already empty — Node.js considers the stream
|
|
537
|
+
// "already flowing" and does not re-invoke _read(). The fetch loop dies
|
|
538
|
+
// and unconsumed messages remain in Kafka.
|
|
539
|
+
//
|
|
540
|
+
// Unconditionally scheduling is safe because #fetch() checks #paused,
|
|
541
|
+
// #closed, and other guards before issuing a Kafka fetch request.
|
|
542
|
+
process.nextTick(() => {
|
|
543
|
+
this.#fetch();
|
|
544
|
+
});
|
|
499
545
|
if (this.#maxFetches > 0 && ++this.#fetches >= this.#maxFetches) {
|
|
500
546
|
this.push(null);
|
|
501
547
|
}
|
|
@@ -317,6 +317,7 @@ export declare const consumeOptionsSchema: {
|
|
|
317
317
|
pattern: string;
|
|
318
318
|
};
|
|
319
319
|
};
|
|
320
|
+
context: boolean;
|
|
320
321
|
mode: {
|
|
321
322
|
type: string;
|
|
322
323
|
enum: import("./types.ts").MessagesStreamModeValue[];
|
|
@@ -475,6 +476,8 @@ export declare const consumerOptionsSchema: {
|
|
|
475
476
|
type: string;
|
|
476
477
|
pattern: string;
|
|
477
478
|
};
|
|
479
|
+
context: boolean;
|
|
480
|
+
streamContext: boolean;
|
|
478
481
|
};
|
|
479
482
|
required: string[];
|
|
480
483
|
additionalProperties: boolean;
|
|
@@ -74,6 +74,7 @@ export const consumeOptionsSchema = {
|
|
|
74
74
|
type: 'object',
|
|
75
75
|
properties: {
|
|
76
76
|
topics: { type: 'array', items: idProperty },
|
|
77
|
+
context: true,
|
|
77
78
|
mode: { type: 'string', enum: allowedMessagesStreamModes },
|
|
78
79
|
fallbackMode: { type: 'string', enum: allowedMessagesStreamFallbackModes },
|
|
79
80
|
maxFetches: { type: 'number', minimum: 0, default: 0 },
|
|
@@ -97,6 +98,8 @@ export const consumerOptionsSchema = {
|
|
|
97
98
|
type: 'object',
|
|
98
99
|
properties: {
|
|
99
100
|
groupId: idProperty,
|
|
101
|
+
context: true,
|
|
102
|
+
streamContext: true,
|
|
100
103
|
...groupOptionsProperties,
|
|
101
104
|
...consumeOptionsProperties
|
|
102
105
|
},
|
|
@@ -74,6 +74,7 @@ export interface ConsumeBaseOptions<Key, Value, HeaderKey, HeaderValue> {
|
|
|
74
74
|
}
|
|
75
75
|
export interface StreamOptions {
|
|
76
76
|
topics: string[];
|
|
77
|
+
context?: unknown;
|
|
77
78
|
mode?: MessagesStreamModeValue;
|
|
78
79
|
fallbackMode?: MessagesStreamFallbackModeValue;
|
|
79
80
|
maxFetches?: number;
|
|
@@ -83,6 +84,8 @@ export interface StreamOptions {
|
|
|
83
84
|
export type ConsumeOptions<Key, Value, HeaderKey, HeaderValue> = StreamOptions & ConsumeBaseOptions<Key, Value, HeaderKey, HeaderValue> & GroupOptions;
|
|
84
85
|
export type ConsumerOptions<Key, Value, HeaderKey, HeaderValue> = BaseOptions & {
|
|
85
86
|
groupId: string;
|
|
87
|
+
context?: unknown;
|
|
88
|
+
streamContext?: unknown;
|
|
86
89
|
} & (GroupOptions | ConsumerGroupOptions) & ConsumeBaseOptions<Key, Value, HeaderKey, HeaderValue>;
|
|
87
90
|
export type FetchOptions<Key, Value, HeaderKey, HeaderValue> = Pick<ConsumeBaseOptions<Key, Value, HeaderKey, HeaderValue>, 'minBytes' | 'maxBytes' | 'maxWaitTime' | 'isolationLevel'> & {
|
|
88
91
|
node: number;
|
|
@@ -2,7 +2,7 @@ import { randomUUID } from 'crypto';
|
|
|
2
2
|
import { createPromisifiedCallback, kCallbackPromise, runConcurrentCallbacks } from "../../apis/callbacks.js";
|
|
3
3
|
import { FindCoordinatorKeyTypes, ProduceAcks } from "../../apis/enumerations.js";
|
|
4
4
|
import { createDiagnosticContext, producerInitIdempotentChannel, producerSendsChannel, producerTransactionsChannel } from "../../diagnostic.js";
|
|
5
|
-
import { UserError } from "../../errors.js";
|
|
5
|
+
import { GenericError, UserError } from "../../errors.js";
|
|
6
6
|
import { murmur2 } from "../../protocol/murmur2.js";
|
|
7
7
|
import { runAsyncSeries } from "../../registries/abstract.js";
|
|
8
8
|
import { kInstance, kTransaction, kTransactionAddOffsets, kTransactionAddPartitions, kTransactionCancel, kTransactionCommitOffset, kTransactionEnd, kTransactionFindCoordinator, kTransactionPrepare } from "../../symbols.js";
|
|
@@ -671,7 +671,10 @@ export class Producer extends Base {
|
|
|
671
671
|
if (error) {
|
|
672
672
|
// If the last error was due to stale metadata, we retry the operation with this set of messages
|
|
673
673
|
// since the partition is already set, it should attempt on the new destination
|
|
674
|
-
const
|
|
674
|
+
const kafkaError = GenericError.isGenericError(error)
|
|
675
|
+
? error
|
|
676
|
+
: null;
|
|
677
|
+
const hasStaleMetadata = kafkaError?.findBy('hasStaleMetadata', true);
|
|
675
678
|
if (hasStaleMetadata && repeatOnStaleMetadata) {
|
|
676
679
|
this.clearMetadata();
|
|
677
680
|
this.#performSingleDestinationSend(topics, messages, timeout, acks, autocreateTopics, false, produceOptions, callback);
|
|
@@ -682,7 +685,10 @@ export class Producer extends Base {
|
|
|
682
685
|
}
|
|
683
686
|
callback(error, results);
|
|
684
687
|
}, 0, [], error => {
|
|
685
|
-
|
|
688
|
+
if (!repeatOnStaleMetadata || !GenericError.isGenericError(error)) {
|
|
689
|
+
return false;
|
|
690
|
+
}
|
|
691
|
+
return !!error.findBy('hasStaleMetadata', true);
|
|
686
692
|
});
|
|
687
693
|
});
|
|
688
694
|
}
|
package/dist/errors.js
CHANGED
|
@@ -78,6 +78,9 @@ export class MultipleErrors extends AggregateError {
|
|
|
78
78
|
return this;
|
|
79
79
|
}
|
|
80
80
|
for (const error of this.errors) {
|
|
81
|
+
if (!error) {
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
81
84
|
if (error[property] === value) {
|
|
82
85
|
return error;
|
|
83
86
|
}
|
|
@@ -111,7 +114,12 @@ export class ProtocolError extends GenericError {
|
|
|
111
114
|
apiCode: code,
|
|
112
115
|
serverErrorMessage,
|
|
113
116
|
canRetry,
|
|
114
|
-
hasStaleMetadata: [
|
|
117
|
+
hasStaleMetadata: [
|
|
118
|
+
'UNKNOWN_TOPIC_OR_PARTITION',
|
|
119
|
+
'LEADER_NOT_AVAILABLE',
|
|
120
|
+
'NOT_LEADER_OR_FOLLOWER',
|
|
121
|
+
'FENCED_LEADER_EPOCH'
|
|
122
|
+
].includes(id),
|
|
115
123
|
needsRejoin: ['MEMBER_ID_REQUIRED', 'UNKNOWN_MEMBER_ID', 'REBALANCE_IN_PROGRESS'].includes(id),
|
|
116
124
|
producerFenced: id === 'INVALID_PRODUCER_EPOCH',
|
|
117
125
|
rebalanceInProgress: id === 'REBALANCE_IN_PROGRESS',
|
|
@@ -25,6 +25,8 @@ export declare class ConnectionPool extends TypedEventEmitter<ConnectionPoolEven
|
|
|
25
25
|
#private;
|
|
26
26
|
constructor(clientId: string, connectionOptions?: ConnectionOptions);
|
|
27
27
|
get instanceId(): number;
|
|
28
|
+
get ownerId(): number | undefined;
|
|
29
|
+
get context(): unknown;
|
|
28
30
|
get(broker: Broker, callback: CallbackWithPromise<Connection>): void;
|
|
29
31
|
get(broker: Broker): Promise<Connection>;
|
|
30
32
|
getFirstAvailable(brokers: Broker[], callback: CallbackWithPromise<Connection>): void;
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { createPromisifiedCallback, kCallbackPromise, runConcurrentCallbacks } from "../apis/callbacks.js";
|
|
2
2
|
import { connectionsPoolGetsChannel, createDiagnosticContext, notifyCreation } from "../diagnostic.js";
|
|
3
|
-
import { TypedEventEmitter } from "../events.js";
|
|
4
3
|
import { MultipleErrors } from "../errors.js";
|
|
4
|
+
import { TypedEventEmitter } from "../events.js";
|
|
5
5
|
import { Connection, ConnectionStatuses } from "./connection.js";
|
|
6
6
|
let currentInstance = 0;
|
|
7
7
|
export class ConnectionPool extends TypedEventEmitter {
|
|
8
8
|
#instanceId;
|
|
9
9
|
#clientId;
|
|
10
10
|
#closed;
|
|
11
|
-
|
|
11
|
+
#context;
|
|
12
12
|
#ownerId;
|
|
13
13
|
#connections;
|
|
14
14
|
#connectionOptions;
|
|
@@ -17,6 +17,7 @@ export class ConnectionPool extends TypedEventEmitter {
|
|
|
17
17
|
this.#closed = false;
|
|
18
18
|
this.#instanceId = currentInstance++;
|
|
19
19
|
this.#clientId = clientId;
|
|
20
|
+
this.#context = connectionOptions.context;
|
|
20
21
|
this.#ownerId = connectionOptions.ownerId;
|
|
21
22
|
this.#connections = new Map();
|
|
22
23
|
this.#connectionOptions = connectionOptions;
|
|
@@ -25,6 +26,14 @@ export class ConnectionPool extends TypedEventEmitter {
|
|
|
25
26
|
get instanceId() {
|
|
26
27
|
return this.#instanceId;
|
|
27
28
|
}
|
|
29
|
+
/* c8 ignore next 3 - Simple getter */
|
|
30
|
+
get ownerId() {
|
|
31
|
+
return this.#ownerId;
|
|
32
|
+
}
|
|
33
|
+
/* c8 ignore next 3 - Simple getter */
|
|
34
|
+
get context() {
|
|
35
|
+
return this.#context;
|
|
36
|
+
}
|
|
28
37
|
get(broker, callback) {
|
|
29
38
|
if (!callback) {
|
|
30
39
|
callback = createPromisifiedCallback();
|
|
@@ -46,6 +46,7 @@ export interface ConnectionOptions {
|
|
|
46
46
|
sasl?: SASLOptions;
|
|
47
47
|
ownerId?: number;
|
|
48
48
|
handleBackPressure?: boolean;
|
|
49
|
+
context?: unknown;
|
|
49
50
|
}
|
|
50
51
|
export interface Request {
|
|
51
52
|
correlationId: number;
|
|
@@ -83,7 +84,9 @@ export declare class Connection extends TypedEventEmitter<ConnectionEvents> {
|
|
|
83
84
|
get host(): string | undefined;
|
|
84
85
|
get port(): number | undefined;
|
|
85
86
|
get instanceId(): number;
|
|
87
|
+
get ownerId(): number | undefined;
|
|
86
88
|
get status(): ConnectionStatusValue;
|
|
89
|
+
get context(): unknown;
|
|
87
90
|
get socket(): Socket;
|
|
88
91
|
isConnected(): boolean;
|
|
89
92
|
connect(host: string, port: number, callback?: CallbackWithPromise<void>): void | Promise<void>;
|
|
@@ -38,7 +38,7 @@ export class Connection extends TypedEventEmitter {
|
|
|
38
38
|
#status;
|
|
39
39
|
#instanceId;
|
|
40
40
|
#clientId;
|
|
41
|
-
|
|
41
|
+
#context;
|
|
42
42
|
#ownerId;
|
|
43
43
|
#handleBackPressure;
|
|
44
44
|
#correlationId;
|
|
@@ -59,6 +59,7 @@ export class Connection extends TypedEventEmitter {
|
|
|
59
59
|
this.#options.tls ??= this.#options.ssl;
|
|
60
60
|
this.#status = ConnectionStatuses.NONE;
|
|
61
61
|
this.#clientId = clientId;
|
|
62
|
+
this.#context = options.context;
|
|
62
63
|
this.#ownerId = options.ownerId;
|
|
63
64
|
this.#handleBackPressure = options.handleBackPressure ?? false;
|
|
64
65
|
this.#correlationId = 0;
|
|
@@ -82,9 +83,16 @@ export class Connection extends TypedEventEmitter {
|
|
|
82
83
|
get instanceId() {
|
|
83
84
|
return this.#instanceId;
|
|
84
85
|
}
|
|
86
|
+
/* c8 ignore next 3 - Simple getter */
|
|
87
|
+
get ownerId() {
|
|
88
|
+
return this.#ownerId;
|
|
89
|
+
}
|
|
85
90
|
get status() {
|
|
86
91
|
return this.#status;
|
|
87
92
|
}
|
|
93
|
+
get context() {
|
|
94
|
+
return this.#context;
|
|
95
|
+
}
|
|
88
96
|
get socket() {
|
|
89
97
|
return this.#socket;
|
|
90
98
|
}
|
|
@@ -178,16 +186,38 @@ export class Connection extends TypedEventEmitter {
|
|
|
178
186
|
if (!callback) {
|
|
179
187
|
callback = createPromisifiedCallback();
|
|
180
188
|
}
|
|
181
|
-
const
|
|
189
|
+
const cleanup = () => {
|
|
190
|
+
clearTimeout(timeout);
|
|
191
|
+
this.removeListener('connect', onConnect);
|
|
182
192
|
this.removeListener('error', onError);
|
|
193
|
+
this.removeListener('close', onClose);
|
|
194
|
+
};
|
|
195
|
+
const onConnect = () => {
|
|
196
|
+
cleanup();
|
|
183
197
|
callback(null);
|
|
184
198
|
};
|
|
185
199
|
const onError = (error) => {
|
|
186
|
-
|
|
200
|
+
cleanup();
|
|
187
201
|
callback(error);
|
|
188
202
|
};
|
|
203
|
+
const onClose = () => {
|
|
204
|
+
cleanup();
|
|
205
|
+
callback(new NetworkError('Connection closed while waiting for ready.'));
|
|
206
|
+
};
|
|
207
|
+
const timeout = setTimeout(() => {
|
|
208
|
+
cleanup();
|
|
209
|
+
// If the connection already failed (e.g. invalid port), don't double-invoke the callback
|
|
210
|
+
if (this.#status === ConnectionStatuses.ERROR || this.#status === ConnectionStatuses.CLOSED) {
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
this.#socket?.destroy();
|
|
214
|
+
callback(new TimeoutError(this.#host
|
|
215
|
+
? `Connection to ${this.#host}:${this.#port} timed out.`
|
|
216
|
+
: `Connection ready timed out after ${this.#options.connectTimeout}ms.`));
|
|
217
|
+
}, this.#options.connectTimeout);
|
|
189
218
|
this.once('connect', onConnect);
|
|
190
219
|
this.once('error', onError);
|
|
220
|
+
this.once('close', onClose);
|
|
191
221
|
this.emit('ready');
|
|
192
222
|
return callback[kCallbackPromise];
|
|
193
223
|
}
|
|
@@ -218,7 +248,7 @@ export class Connection extends TypedEventEmitter {
|
|
|
218
248
|
});
|
|
219
249
|
this.#status = ConnectionStatuses.CLOSING;
|
|
220
250
|
this.emit('closing');
|
|
221
|
-
this.#socket.
|
|
251
|
+
this.#socket.destroySoon();
|
|
222
252
|
return callback[kCallbackPromise];
|
|
223
253
|
}
|
|
224
254
|
send(apiKey, apiVersion, createPayload, responseParser, hasRequestHeaderTaggedFields, hasResponseHeaderTaggedFields, callback) {
|
|
@@ -5,7 +5,7 @@ import { type CallbackWithPromise } from "../../apis/callbacks";
|
|
|
5
5
|
import { type Callback } from "../../apis/definitions";
|
|
6
6
|
import { type Acl } from "../../apis/types";
|
|
7
7
|
import { Base } from "../base/base";
|
|
8
|
-
import { type AdminListOffsetsOptions, type AdminOptions, type AlterClientQuotasOptions, type AlterConfigsOptions, type AlterConsumerGroupOffsetsOptions, type BrokerLogDirDescription, type ConfigDescription, type CreateAclsOptions, type CreatedTopic, type CreatePartitionsOptions, type CreateTopicsOptions, type DeleteAclsOptions, type DeleteConsumerGroupOffsetsOptions, type DeleteRecordsOptions, type DeleteGroupsOptions, type DeleteTopicsOptions, type DeletedRecordsTopic, type DescribeAclsOptions, type DescribeClientQuotasOptions, type DescribeConfigsOptions, type DescribeGroupsOptions, type DescribeLogDirsOptions, type Group, type GroupBase, type IncrementalAlterConfigsOptions, type ListConsumerGroupOffsetsGroup, type ListConsumerGroupOffsetsOptions, type ListedOffsetsTopic, type ListGroupsOptions, type ListTopicsOptions, type RemoveMembersFromConsumerGroupOptions } from "./types";
|
|
8
|
+
import { type AdminListOffsetsOptions, type AdminOptions, type AlterClientQuotasOptions, type AlterConfigsOptions, type AlterConsumerGroupOffsetsOptions, type BrokerLogDirDescription, type ConfigDescription, type CreateAclsOptions, type CreatedTopic, type CreatePartitionsOptions, type CreateTopicsOptions, type DeleteAclsOptions, type DeleteConsumerGroupOffsetsOptions, type DeleteRecordsOptions, type DeleteGroupsOptions, type DeleteTopicsOptions, type DeletedRecordsTopic, type DescribeAclsOptions, type DescribeClientQuotasOptions, type DescribeConfigsOptions, type DescribeGroupsOptions, type DescribeLogDirsOptions, type FindCoordinatorOptions, type FindCoordinatorResult, type Group, type GroupBase, type IncrementalAlterConfigsOptions, type ListConsumerGroupOffsetsGroup, type ListConsumerGroupOffsetsOptions, type ListedOffsetsTopic, type ListGroupsOptions, type ListTopicsOptions, type RemoveMembersFromConsumerGroupOptions } from "./types";
|
|
9
9
|
export declare class Admin extends Base<AdminOptions> {
|
|
10
10
|
#private;
|
|
11
11
|
constructor(options: AdminOptions);
|
|
@@ -21,6 +21,8 @@ export declare class Admin extends Base<AdminOptions> {
|
|
|
21
21
|
listGroups(options?: ListGroupsOptions): Promise<Map<string, GroupBase>>;
|
|
22
22
|
describeGroups(options: DescribeGroupsOptions, callback: CallbackWithPromise<Map<string, Group>>): void;
|
|
23
23
|
describeGroups(options: DescribeGroupsOptions): Promise<Map<string, Group>>;
|
|
24
|
+
findCoordinator(options: FindCoordinatorOptions, callback: CallbackWithPromise<FindCoordinatorResult[]>): void;
|
|
25
|
+
findCoordinator(options: FindCoordinatorOptions): Promise<FindCoordinatorResult[]>;
|
|
24
26
|
deleteGroups(options: DeleteGroupsOptions, callback: CallbackWithPromise<void>): void;
|
|
25
27
|
deleteGroups(options: DeleteGroupsOptions): Promise<void>;
|
|
26
28
|
removeMembersFromConsumerGroup(options: RemoveMembersFromConsumerGroupOptions, callback: CallbackWithPromise<void>): void;
|
|
@@ -886,6 +886,25 @@ export declare const adminListOffsetsOptionsSchema: {
|
|
|
886
886
|
required: string[];
|
|
887
887
|
additionalProperties: boolean;
|
|
888
888
|
};
|
|
889
|
+
export declare const findCoordinatorOptionsSchema: {
|
|
890
|
+
type: string;
|
|
891
|
+
properties: {
|
|
892
|
+
keyType: {
|
|
893
|
+
type: string;
|
|
894
|
+
enum: (0 | 1 | 2)[];
|
|
895
|
+
};
|
|
896
|
+
keys: {
|
|
897
|
+
type: string;
|
|
898
|
+
items: {
|
|
899
|
+
type: string;
|
|
900
|
+
pattern: string;
|
|
901
|
+
};
|
|
902
|
+
minItems: number;
|
|
903
|
+
};
|
|
904
|
+
};
|
|
905
|
+
required: string[];
|
|
906
|
+
additionalProperties: boolean;
|
|
907
|
+
};
|
|
889
908
|
export declare const createTopicsOptionsValidator: import("ajv").ValidateFunction<{
|
|
890
909
|
[x: string]: {};
|
|
891
910
|
}>;
|
|
@@ -947,4 +966,7 @@ export declare const deleteAclsOptionsValidator: import("ajv").ValidateFunction<
|
|
|
947
966
|
}>;
|
|
948
967
|
export declare const adminListOffsetsOptionsValidator: import("ajv").ValidateFunction<{
|
|
949
968
|
[x: string]: {};
|
|
969
|
+
}>;
|
|
970
|
+
export declare const findCoordinatorOptionsValidator: import("ajv").ValidateFunction<{
|
|
971
|
+
[x: string]: {};
|
|
950
972
|
}>;
|
|
@@ -6,7 +6,7 @@ import { type DescribeClientQuotasRequestComponent } from "../../apis/admin/desc
|
|
|
6
6
|
import { type DescribeConfigsRequestResource, type DescribeConfigsResponseConfig } from "../../apis/admin/describe-configs-v4";
|
|
7
7
|
import { type DescribeLogDirsRequestTopic, type DescribeLogDirsResponse, type DescribeLogDirsResponseResult } from "../../apis/admin/describe-log-dirs-v4";
|
|
8
8
|
import { type IncrementalAlterConfigsRequestResource } from "../../apis/admin/incremental-alter-configs-v1";
|
|
9
|
-
import { type ConfigResourceTypeValue, type ConsumerGroupStateValue, type FetchIsolationLevelValue } from "../../apis/enumerations";
|
|
9
|
+
import { type ConfigResourceTypeValue, type ConsumerGroupStateValue, type FetchIsolationLevelValue, type FindCoordinatorKeyTypeValue } from "../../apis/enumerations";
|
|
10
10
|
import { type Acl, type AclFilter } from "../../apis/types";
|
|
11
11
|
import { type Nullable, type NullableString } from "../../protocol/definitions";
|
|
12
12
|
import { type BaseOptions } from "../base/types";
|
|
@@ -67,6 +67,16 @@ export interface DescribeGroupsOptions {
|
|
|
67
67
|
groups: string[];
|
|
68
68
|
includeAuthorizedOperations?: boolean;
|
|
69
69
|
}
|
|
70
|
+
export interface FindCoordinatorOptions {
|
|
71
|
+
keyType: FindCoordinatorKeyTypeValue;
|
|
72
|
+
keys: string[];
|
|
73
|
+
}
|
|
74
|
+
export interface FindCoordinatorResult {
|
|
75
|
+
key: string;
|
|
76
|
+
nodeId: number;
|
|
77
|
+
host: string;
|
|
78
|
+
port: number;
|
|
79
|
+
}
|
|
70
80
|
export interface DeleteGroupsOptions {
|
|
71
81
|
groups: string[];
|
|
72
82
|
}
|
|
@@ -30,6 +30,7 @@ export declare const kFormatValidationErrors: unique symbol;
|
|
|
30
30
|
export declare const kPrometheus: unique symbol;
|
|
31
31
|
export declare const kClientType: unique symbol;
|
|
32
32
|
export declare const kAfterCreate: unique symbol;
|
|
33
|
+
export declare const kContext: unique symbol;
|
|
33
34
|
export interface BaseEvents extends TypedEvents {
|
|
34
35
|
'client:broker:connect': (payload: ConnectionPoolEventPayload) => void;
|
|
35
36
|
'client:broker:disconnect': (payload: ConnectionPoolEventPayload) => void;
|
|
@@ -52,6 +53,7 @@ export declare class Base<OptionsType extends BaseOptions = BaseOptions, EventsT
|
|
|
52
53
|
[kInstance]: number;
|
|
53
54
|
[kClientId]: string;
|
|
54
55
|
[kClientType]: ClientType;
|
|
56
|
+
[kContext]: unknown;
|
|
55
57
|
[kBootstrapBrokers]: Broker[];
|
|
56
58
|
[kApis]: ApiVersionsResponseApi[];
|
|
57
59
|
[kOptions]: OptionsType;
|
|
@@ -63,6 +65,7 @@ export declare class Base<OptionsType extends BaseOptions = BaseOptions, EventsT
|
|
|
63
65
|
get clientId(): string;
|
|
64
66
|
get closed(): boolean;
|
|
65
67
|
get type(): ClientType;
|
|
68
|
+
get context(): unknown;
|
|
66
69
|
emitWithDebug(section: string | null, name: string, ...args: any[]): boolean;
|
|
67
70
|
close(callback: CallbackWithPromise<void>): void;
|
|
68
71
|
close(): Promise<void>;
|
|
@@ -74,7 +77,7 @@ export declare class Base<OptionsType extends BaseOptions = BaseOptions, EventsT
|
|
|
74
77
|
connectToBrokers(nodeIds?: number[] | null): Promise<Map<number, Connection>>;
|
|
75
78
|
isActive(): boolean;
|
|
76
79
|
isConnected(): boolean;
|
|
77
|
-
[kCreateConnectionPool](): ConnectionPool;
|
|
80
|
+
[kCreateConnectionPool](context?: unknown): ConnectionPool;
|
|
78
81
|
[kListApis](callback: CallbackWithPromise<ApiVersionsResponseApi[]>): void;
|
|
79
82
|
[kMetadata](options: MetadataOptions, callback: CallbackWithPromise<ClusterMetadata>): void;
|
|
80
83
|
[kCheckNotClosed](callback: CallbackWithPromise<any>): boolean;
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { Base, kCheckNotClosed, kGetApi, kGetBootstrapConnection, kGetConnection, kListApis, kMetadata, kOptions, kPerformDeduplicated, kPerformWithRetry, kValidateOptions } from "./base";
|
|
1
|
+
export { Base, kContext, kCheckNotClosed, kGetApi, kGetBootstrapConnection, kGetConnection, kListApis, kMetadata, kOptions, kPerformDeduplicated, kPerformWithRetry, kValidateOptions } from "./base";
|
|
2
2
|
export * from "./options";
|
|
3
3
|
export * from "./types";
|
|
@@ -33,6 +33,7 @@ export type RetryDelayGetter<Owner = object> = (client: Owner, operationId: stri
|
|
|
33
33
|
export interface BaseOptions extends ConnectionOptions {
|
|
34
34
|
clientId: string;
|
|
35
35
|
bootstrapBrokers: Broker[] | string[];
|
|
36
|
+
context?: unknown;
|
|
36
37
|
timeout?: number;
|
|
37
38
|
retries?: number | boolean;
|
|
38
39
|
retryDelay?: number | RetryDelayGetter;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type CallbackWithPromise } from "../../apis/callbacks";
|
|
2
2
|
import { type FetchResponse } from "../../apis/consumer/fetch-v17";
|
|
3
|
-
import {
|
|
3
|
+
import { type ConnectionPool } from "../../network/connection-pool";
|
|
4
|
+
import { Base, type BaseEvents, kCreateConnectionPool } from "../base/base";
|
|
4
5
|
import { MessagesStream } from "./messages-stream";
|
|
5
6
|
import { TopicsMap } from "./topics-map";
|
|
6
7
|
import { type CommitOptions, type ConsumeOptions, type ConsumerGroupJoinPayload, type ConsumerGroupLeavePayload, type ConsumerGroupRebalancePayload, type ConsumerHeartbeatErrorPayload, type ConsumerHeartbeatPayload, type ConsumerOptions, type FetchOptions, type GetLagOptions, type GroupAssignment, type GroupOptions, type ListCommitsOptions, type ListOffsetsOptions, type Offsets, type OffsetsWithTimestamps } from "./types";
|
|
@@ -28,6 +29,7 @@ export declare class Consumer<Key = Buffer, Value = Buffer, HeaderKey = Buffer,
|
|
|
28
29
|
get streamsCount(): number;
|
|
29
30
|
get lastHeartbeat(): Date | null;
|
|
30
31
|
get coordinatorId(): number | null;
|
|
32
|
+
get streamContext(): unknown;
|
|
31
33
|
close(force: boolean | CallbackWithPromise<void>, callback?: CallbackWithPromise<void>): void;
|
|
32
34
|
close(force?: boolean): Promise<void>;
|
|
33
35
|
isActive(): boolean;
|
|
@@ -49,6 +51,7 @@ export declare class Consumer<Key = Buffer, Value = Buffer, HeaderKey = Buffer,
|
|
|
49
51
|
stopLagMonitoring(): void;
|
|
50
52
|
findGroupCoordinator(callback: CallbackWithPromise<number>): void;
|
|
51
53
|
findGroupCoordinator(): Promise<number>;
|
|
54
|
+
[kCreateConnectionPool](context?: unknown): ConnectionPool;
|
|
52
55
|
joinGroup(options: GroupOptions, callback: CallbackWithPromise<string>): void;
|
|
53
56
|
joinGroup(options?: GroupOptions): Promise<string>;
|
|
54
57
|
leaveGroup(force?: boolean | CallbackWithPromise<void>, callback?: CallbackWithPromise<void>): void;
|
|
@@ -15,6 +15,7 @@ export declare class MessagesStream<Key, Value, HeaderKey, HeaderValue> extends
|
|
|
15
15
|
constructor(consumer: Consumer<Key, Value, HeaderKey, HeaderValue>, options: ConsumeOptions<Key, Value, HeaderKey, HeaderValue>);
|
|
16
16
|
get consumer(): Consumer<Key, Value, HeaderKey, HeaderValue>;
|
|
17
17
|
get offsetsToFetch(): Map<string, bigint>;
|
|
18
|
+
get context(): unknown;
|
|
18
19
|
get offsetsToCommit(): Map<string, CommitOptionsPartition>;
|
|
19
20
|
get offsetsCommitted(): Map<string, bigint>;
|
|
20
21
|
get committedOffsets(): Map<string, bigint>;
|
|
@@ -317,6 +317,7 @@ export declare const consumeOptionsSchema: {
|
|
|
317
317
|
pattern: string;
|
|
318
318
|
};
|
|
319
319
|
};
|
|
320
|
+
context: boolean;
|
|
320
321
|
mode: {
|
|
321
322
|
type: string;
|
|
322
323
|
enum: import("./types").MessagesStreamModeValue[];
|
|
@@ -475,6 +476,8 @@ export declare const consumerOptionsSchema: {
|
|
|
475
476
|
type: string;
|
|
476
477
|
pattern: string;
|
|
477
478
|
};
|
|
479
|
+
context: boolean;
|
|
480
|
+
streamContext: boolean;
|
|
478
481
|
};
|
|
479
482
|
required: string[];
|
|
480
483
|
additionalProperties: boolean;
|
|
@@ -74,6 +74,7 @@ export interface ConsumeBaseOptions<Key, Value, HeaderKey, HeaderValue> {
|
|
|
74
74
|
}
|
|
75
75
|
export interface StreamOptions {
|
|
76
76
|
topics: string[];
|
|
77
|
+
context?: unknown;
|
|
77
78
|
mode?: MessagesStreamModeValue;
|
|
78
79
|
fallbackMode?: MessagesStreamFallbackModeValue;
|
|
79
80
|
maxFetches?: number;
|
|
@@ -83,6 +84,8 @@ export interface StreamOptions {
|
|
|
83
84
|
export type ConsumeOptions<Key, Value, HeaderKey, HeaderValue> = StreamOptions & ConsumeBaseOptions<Key, Value, HeaderKey, HeaderValue> & GroupOptions;
|
|
84
85
|
export type ConsumerOptions<Key, Value, HeaderKey, HeaderValue> = BaseOptions & {
|
|
85
86
|
groupId: string;
|
|
87
|
+
context?: unknown;
|
|
88
|
+
streamContext?: unknown;
|
|
86
89
|
} & (GroupOptions | ConsumerGroupOptions) & ConsumeBaseOptions<Key, Value, HeaderKey, HeaderValue>;
|
|
87
90
|
export type FetchOptions<Key, Value, HeaderKey, HeaderValue> = Pick<ConsumeBaseOptions<Key, Value, HeaderKey, HeaderValue>, 'minBytes' | 'maxBytes' | 'maxWaitTime' | 'isolationLevel'> & {
|
|
88
91
|
node: number;
|