@platformatic/kafka 1.0.0 → 1.1.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 +1 -0
- package/dist/apis/definitions.js +0 -1
- package/dist/clients/admin/admin.js +10 -10
- package/dist/clients/base/base.d.ts +9 -2
- package/dist/clients/base/base.js +20 -9
- package/dist/clients/callbacks.d.ts +1 -0
- package/dist/clients/callbacks.js +3 -2
- package/dist/clients/consumer/consumer.js +102 -86
- package/dist/clients/consumer/messages-stream.d.ts +1 -0
- package/dist/clients/consumer/messages-stream.js +81 -47
- package/dist/clients/consumer/options.d.ts +3 -0
- package/dist/clients/consumer/options.js +1 -0
- package/dist/clients/consumer/types.d.ts +7 -2
- package/dist/clients/producer/producer.js +33 -22
- package/dist/diagnostic.d.ts +46 -0
- package/dist/diagnostic.js +37 -0
- package/dist/errors.js +0 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/network/connection-pool.d.ts +2 -1
- package/dist/network/connection-pool.js +36 -25
- package/dist/network/connection.d.ts +2 -2
- package/dist/network/connection.js +135 -81
- package/dist/protocol/compression.js +8 -8
- package/dist/protocol/reader.js +0 -1
- package/dist/protocol/records.js +0 -3
- package/dist/protocol/sasl/scram-sha.d.ts +10 -3
- package/dist/protocol/sasl/scram-sha.js +8 -2
- package/dist/symbols.d.ts +1 -0
- package/dist/symbols.js +2 -0
- package/package.json +15 -13
package/README.md
CHANGED
|
@@ -264,6 +264,7 @@ Many of the methods accept the same options as the client's constructors. The co
|
|
|
264
264
|
- [Admin API](./docs/admin.md)
|
|
265
265
|
- [Base Client](./docs/base.md)
|
|
266
266
|
- [Metrics](./docs/metrics.md)
|
|
267
|
+
- [Diagnostic and Instrumentation](./docs/diagnostic.md)
|
|
267
268
|
- [Other APIs and Types](./docs/other.md)
|
|
268
269
|
|
|
269
270
|
## Requirements
|
package/dist/apis/definitions.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { promisify } from 'node:util';
|
|
2
2
|
export function createAPI(apiKey, apiVersion, createRequest, parseResponse, hasRequestHeaderTaggedFields = true, hasResponseHeaderTaggedFields = true) {
|
|
3
3
|
const api = function api(connection, ...args) {
|
|
4
|
-
/* c8 ignore next */
|
|
5
4
|
const cb = typeof args[args.length - 1] === 'function' ? args.pop() : () => { };
|
|
6
5
|
connection.send(apiKey, apiVersion, () => createRequest(...args), parseResponse, hasRequestHeaderTaggedFields, hasResponseHeaderTaggedFields, cb);
|
|
7
6
|
};
|
|
@@ -5,13 +5,15 @@ import { api as describeGroupsV5 } from "../../apis/admin/describe-groups.js";
|
|
|
5
5
|
import { api as listGroupsV5 } from "../../apis/admin/list-groups.js";
|
|
6
6
|
import { FindCoordinatorKeyTypes } from "../../apis/enumerations.js";
|
|
7
7
|
import { api as findCoordinatorV6 } from "../../apis/metadata/find-coordinator.js";
|
|
8
|
+
import { adminGroupsChannel, adminTopicsChannel, createDiagnosticContext } from "../../diagnostic.js";
|
|
8
9
|
import { Reader } from "../../protocol/reader.js";
|
|
9
|
-
import { Base, kBootstrapBrokers, kCheckNotClosed, kConnections, kMetadata, kOptions, kPerformDeduplicated, kPerformWithRetry, kValidateOptions } from "../base/base.js";
|
|
10
|
+
import { Base, kAfterCreate, kBootstrapBrokers, kCheckNotClosed, kConnections, kMetadata, kOptions, kPerformDeduplicated, kPerformWithRetry, kValidateOptions } from "../base/base.js";
|
|
10
11
|
import { createPromisifiedCallback, kCallbackPromise, runConcurrentCallbacks } from "../callbacks.js";
|
|
11
12
|
import { createTopicsOptionsValidator, deleteGroupsOptionsValidator, deleteTopicsOptionsValidator, describeGroupsOptionsValidator, listGroupsOptionsValidator } from "./options.js";
|
|
12
13
|
export class Admin extends Base {
|
|
13
14
|
constructor(options) {
|
|
14
15
|
super(options);
|
|
16
|
+
this[kAfterCreate]('admin');
|
|
15
17
|
}
|
|
16
18
|
createTopics(options, callback) {
|
|
17
19
|
if (!callback) {
|
|
@@ -25,7 +27,7 @@ export class Admin extends Base {
|
|
|
25
27
|
callback(validationError, undefined);
|
|
26
28
|
return callback[kCallbackPromise];
|
|
27
29
|
}
|
|
28
|
-
this.#createTopics(options, callback);
|
|
30
|
+
adminTopicsChannel.traceCallback(this.#createTopics, 1, createDiagnosticContext({ client: this, operation: 'createTopics', options }), this, options, callback);
|
|
29
31
|
return callback[kCallbackPromise];
|
|
30
32
|
}
|
|
31
33
|
deleteTopics(options, callback) {
|
|
@@ -40,7 +42,7 @@ export class Admin extends Base {
|
|
|
40
42
|
callback(validationError, undefined);
|
|
41
43
|
return callback[kCallbackPromise];
|
|
42
44
|
}
|
|
43
|
-
this.#deleteTopics(options, callback);
|
|
45
|
+
adminTopicsChannel.traceCallback(this.#deleteTopics, 1, createDiagnosticContext({ client: this, operation: 'deleteTopics', options }), this, options, callback);
|
|
44
46
|
return callback[kCallbackPromise];
|
|
45
47
|
}
|
|
46
48
|
listGroups(options, callback) {
|
|
@@ -59,7 +61,7 @@ export class Admin extends Base {
|
|
|
59
61
|
return callback[kCallbackPromise];
|
|
60
62
|
}
|
|
61
63
|
options.types ??= ['classic'];
|
|
62
|
-
this.#listGroups(options, callback);
|
|
64
|
+
adminGroupsChannel.traceCallback(this.#listGroups, 1, createDiagnosticContext({ client: this, operation: 'listGroups', options }), this, options, callback);
|
|
63
65
|
return callback[kCallbackPromise];
|
|
64
66
|
}
|
|
65
67
|
describeGroups(options, callback) {
|
|
@@ -74,7 +76,7 @@ export class Admin extends Base {
|
|
|
74
76
|
callback(validationError, undefined);
|
|
75
77
|
return callback[kCallbackPromise];
|
|
76
78
|
}
|
|
77
|
-
this.#describeGroups(options, callback);
|
|
79
|
+
adminGroupsChannel.traceCallback(this.#describeGroups, 1, createDiagnosticContext({ client: this, operation: 'describeGroups', options }), this, options, callback);
|
|
78
80
|
return callback[kCallbackPromise];
|
|
79
81
|
}
|
|
80
82
|
deleteGroups(options, callback) {
|
|
@@ -89,7 +91,7 @@ export class Admin extends Base {
|
|
|
89
91
|
callback(validationError, undefined);
|
|
90
92
|
return callback[kCallbackPromise];
|
|
91
93
|
}
|
|
92
|
-
this.#deleteGroups(options, callback);
|
|
94
|
+
adminGroupsChannel.traceCallback(this.#deleteGroups, 1, createDiagnosticContext({ client: this, operation: 'deleteGroups', options }), this, options, callback);
|
|
93
95
|
return callback[kCallbackPromise];
|
|
94
96
|
}
|
|
95
97
|
#createTopics(options, callback) {
|
|
@@ -150,7 +152,7 @@ export class Admin extends Base {
|
|
|
150
152
|
deleteTopicsV6(connection, requests, this[kOptions].timeout, retryCallback);
|
|
151
153
|
});
|
|
152
154
|
}, deduplicateCallback, 0);
|
|
153
|
-
}, callback);
|
|
155
|
+
}, error => callback(error));
|
|
154
156
|
}
|
|
155
157
|
#listGroups(options, callback) {
|
|
156
158
|
// Find all the brokers in the cluster
|
|
@@ -296,9 +298,7 @@ export class Admin extends Base {
|
|
|
296
298
|
deleteGroupsV2(connection, groups, retryCallback);
|
|
297
299
|
}, concurrentCallback, 0);
|
|
298
300
|
});
|
|
299
|
-
}, error =>
|
|
300
|
-
callback(error);
|
|
301
|
-
});
|
|
301
|
+
}, error => callback(error));
|
|
302
302
|
});
|
|
303
303
|
});
|
|
304
304
|
}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { type ValidateFunction } from 'ajv';
|
|
2
2
|
import { EventEmitter } from 'node:events';
|
|
3
3
|
import { type Callback } from '../../apis/definitions.ts';
|
|
4
|
+
import { type ClientType } from '../../diagnostic.ts';
|
|
4
5
|
import { ConnectionPool } from '../../network/connection-pool.ts';
|
|
5
6
|
import { type Broker } from '../../network/connection.ts';
|
|
7
|
+
import { kInstance } from '../../symbols.ts';
|
|
6
8
|
import { type CallbackWithPromise } from '../callbacks.ts';
|
|
7
9
|
import { type Metrics } from '../metrics.ts';
|
|
8
10
|
import { type BaseOptions, type ClusterMetadata, type MetadataOptions } from './types.ts';
|
|
@@ -22,20 +24,24 @@ export declare const kPerformDeduplicated: unique symbol;
|
|
|
22
24
|
export declare const kValidateOptions: unique symbol;
|
|
23
25
|
export declare const kInspect: unique symbol;
|
|
24
26
|
export declare const kFormatValidationErrors: unique symbol;
|
|
25
|
-
export declare const kInstance: unique symbol;
|
|
26
27
|
export declare const kPrometheus: unique symbol;
|
|
27
|
-
export declare
|
|
28
|
+
export declare const kClientType: unique symbol;
|
|
29
|
+
export declare const kAfterCreate: unique symbol;
|
|
30
|
+
export declare class Base<OptionsType extends BaseOptions = BaseOptions> extends EventEmitter {
|
|
28
31
|
#private;
|
|
29
32
|
[kInstance]: number;
|
|
30
33
|
[kClientId]: string;
|
|
34
|
+
[kClientType]: ClientType;
|
|
31
35
|
[kBootstrapBrokers]: Broker[];
|
|
32
36
|
[kOptions]: OptionsType;
|
|
33
37
|
[kConnections]: ConnectionPool;
|
|
34
38
|
[kClosed]: boolean;
|
|
35
39
|
[kPrometheus]: Metrics | undefined;
|
|
36
40
|
constructor(options: OptionsType);
|
|
41
|
+
get instanceId(): number;
|
|
37
42
|
get clientId(): string;
|
|
38
43
|
get closed(): boolean;
|
|
44
|
+
get type(): ClientType;
|
|
39
45
|
emitWithDebug(section: string | null, name: string, ...args: any[]): boolean;
|
|
40
46
|
close(callback: CallbackWithPromise<void>): void;
|
|
41
47
|
close(): Promise<void>;
|
|
@@ -51,4 +57,5 @@ export declare class Base<OptionsType extends BaseOptions> extends EventEmitter
|
|
|
51
57
|
[kValidateOptions](target: unknown, validator: ValidateFunction<unknown>, targetName: string, throwOnErrors?: boolean): Error | null;
|
|
52
58
|
[kInspect](...args: unknown[]): void;
|
|
53
59
|
[kFormatValidationErrors](validator: ValidateFunction<unknown>, targetName: string): string;
|
|
60
|
+
[kAfterCreate](type: ClientType): void;
|
|
54
61
|
}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { EventEmitter } from 'node:events';
|
|
2
2
|
import { api as metadataV12 } from "../../apis/metadata/metadata.js";
|
|
3
|
+
import { baseMetadataChannel, createDiagnosticContext, notifyCreation } from "../../diagnostic.js";
|
|
3
4
|
import { MultipleErrors, NetworkError, UserError } from "../../errors.js";
|
|
4
5
|
import { ConnectionPool } from "../../network/connection-pool.js";
|
|
6
|
+
import { kInstance } from "../../symbols.js";
|
|
5
7
|
import { ajv, debugDump, loggers } from "../../utils.js";
|
|
6
8
|
import { createPromisifiedCallback, kCallbackPromise } from "../callbacks.js";
|
|
7
9
|
import { baseOptionsValidator, defaultBaseOptions, defaultPort, metadataOptionsValidator } from "./options.js";
|
|
@@ -21,14 +23,16 @@ export const kPerformDeduplicated = Symbol('plt.kafka.base.performDeduplicated')
|
|
|
21
23
|
export const kValidateOptions = Symbol('plt.kafka.base.validateOptions');
|
|
22
24
|
export const kInspect = Symbol('plt.kafka.base.inspect');
|
|
23
25
|
export const kFormatValidationErrors = Symbol('plt.kafka.base.formatValidationErrors');
|
|
24
|
-
export const kInstance = Symbol('plt.kafka.base.instance');
|
|
25
26
|
export const kPrometheus = Symbol('plt.kafka.base.prometheus');
|
|
27
|
+
export const kClientType = Symbol('plt.kafka.base.clientType');
|
|
28
|
+
export const kAfterCreate = Symbol('plt.kafka.base.afterCreate');
|
|
26
29
|
let currentInstance = 0;
|
|
27
30
|
export class Base extends EventEmitter {
|
|
28
|
-
// This is
|
|
31
|
+
// This is declared using a symbol (a.k.a protected/friend) to make it available in ConnectionPool and MessagesStream
|
|
29
32
|
[kInstance];
|
|
30
33
|
// General status - Use symbols rather than JS private property to make them "protected" as in C++
|
|
31
34
|
[kClientId];
|
|
35
|
+
[kClientType];
|
|
32
36
|
[kBootstrapBrokers];
|
|
33
37
|
[kOptions];
|
|
34
38
|
[kConnections];
|
|
@@ -38,6 +42,7 @@ export class Base extends EventEmitter {
|
|
|
38
42
|
#inflightDeduplications;
|
|
39
43
|
constructor(options) {
|
|
40
44
|
super();
|
|
45
|
+
this[kClientType] = 'base';
|
|
41
46
|
this[kInstance] = currentInstance++;
|
|
42
47
|
// Validate options
|
|
43
48
|
this[kOptions] = Object.assign({}, defaultBaseOptions, options);
|
|
@@ -57,19 +62,22 @@ export class Base extends EventEmitter {
|
|
|
57
62
|
this[kPrometheus] = options.metrics;
|
|
58
63
|
}
|
|
59
64
|
}
|
|
60
|
-
|
|
65
|
+
get instanceId() {
|
|
66
|
+
return this[kInstance];
|
|
67
|
+
}
|
|
61
68
|
get clientId() {
|
|
62
69
|
return this[kClientId];
|
|
63
70
|
}
|
|
64
|
-
/* c8 ignore next 3 */
|
|
65
71
|
get closed() {
|
|
66
72
|
return this[kClosed] === true;
|
|
67
73
|
}
|
|
74
|
+
get type() {
|
|
75
|
+
return this[kClientType];
|
|
76
|
+
}
|
|
68
77
|
emitWithDebug(section, name, ...args) {
|
|
69
78
|
if (!section) {
|
|
70
79
|
return this.emit(name, ...args);
|
|
71
80
|
}
|
|
72
|
-
/* c8 ignore next */
|
|
73
81
|
loggers[section]?.({ event: name, payload: args });
|
|
74
82
|
return this.emit(`${section}:${name}`, ...args);
|
|
75
83
|
}
|
|
@@ -90,7 +98,7 @@ export class Base extends EventEmitter {
|
|
|
90
98
|
callback(validationError, undefined);
|
|
91
99
|
return callback[kCallbackPromise];
|
|
92
100
|
}
|
|
93
|
-
this[kMetadata](options, callback);
|
|
101
|
+
baseMetadataChannel.traceCallback(this[kMetadata], 1, createDiagnosticContext({ client: this, operation: 'metadata' }), this, options, callback);
|
|
94
102
|
return callback[kCallbackPromise];
|
|
95
103
|
}
|
|
96
104
|
[kCreateConnectionPool]() {
|
|
@@ -135,7 +143,7 @@ export class Base extends EventEmitter {
|
|
|
135
143
|
brokers.set(broker.nodeId, { host, port });
|
|
136
144
|
}
|
|
137
145
|
for (const { name, topicId: id, partitions: rawPartitions, isInternal } of metadata.topics) {
|
|
138
|
-
/* c8 ignore next 3 */
|
|
146
|
+
/* c8 ignore next 3 - Sometimes internal topics might be returned by Kafka */
|
|
139
147
|
if (isInternal) {
|
|
140
148
|
continue;
|
|
141
149
|
}
|
|
@@ -242,12 +250,15 @@ export class Base extends EventEmitter {
|
|
|
242
250
|
}
|
|
243
251
|
return null;
|
|
244
252
|
}
|
|
245
|
-
|
|
246
|
-
/* c8 ignore next 3 */
|
|
253
|
+
/* c8 ignore next 3 -- This is a private API used to debug during development */
|
|
247
254
|
[kInspect](...args) {
|
|
248
255
|
debugDump(`client:${this[kInstance]}`, ...args);
|
|
249
256
|
}
|
|
250
257
|
[kFormatValidationErrors](validator, targetName) {
|
|
251
258
|
return ajv.errorsText(validator.errors, { dataVar: '$dataVar$' }).replaceAll('$dataVar$', targetName) + '.';
|
|
252
259
|
}
|
|
260
|
+
[kAfterCreate](type) {
|
|
261
|
+
this[kClientType] = type;
|
|
262
|
+
notifyCreation(type, this);
|
|
263
|
+
}
|
|
253
264
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type Callback } from '../apis/definitions.ts';
|
|
2
2
|
export declare const kCallbackPromise: unique symbol;
|
|
3
|
+
export declare const kNoopCallbackReturnValue: unique symbol;
|
|
3
4
|
export declare const noopCallback: CallbackWithPromise<any>;
|
|
4
5
|
export type CallbackWithPromise<ReturnType> = Callback<ReturnType> & {
|
|
5
6
|
[kCallbackPromise]?: Promise<ReturnType>;
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { MultipleErrors } from "../errors.js";
|
|
2
2
|
export const kCallbackPromise = Symbol('plt.kafka.callbackPromise');
|
|
3
|
-
|
|
3
|
+
// This is only meaningful for testing
|
|
4
|
+
export const kNoopCallbackReturnValue = Symbol('plt.kafka.noopCallbackReturnValue');
|
|
4
5
|
export const noopCallback = () => {
|
|
5
|
-
return Promise.resolve();
|
|
6
|
+
return Promise.resolve(kNoopCallbackReturnValue);
|
|
6
7
|
};
|
|
7
8
|
export function createPromisifiedCallback() {
|
|
8
9
|
const { promise, resolve, reject } = Promise.withResolvers();
|
|
@@ -8,10 +8,11 @@ import { api as offsetFetchV9 } from "../../apis/consumer/offset-fetch.js";
|
|
|
8
8
|
import { api as syncGroupV5 } from "../../apis/consumer/sync-group.js";
|
|
9
9
|
import { FetchIsolationLevels, FindCoordinatorKeyTypes } from "../../apis/enumerations.js";
|
|
10
10
|
import { api as findCoordinatorV6 } from "../../apis/metadata/find-coordinator.js";
|
|
11
|
+
import { consumerCommitsChannel, consumerConsumesChannel, consumerFetchesChannel, consumerGroupChannel, consumerHeartbeatChannel, consumerOffsetsChannel, createDiagnosticContext } from "../../diagnostic.js";
|
|
11
12
|
import { UserError } from "../../errors.js";
|
|
12
13
|
import { Reader } from "../../protocol/reader.js";
|
|
13
14
|
import { Writer } from "../../protocol/writer.js";
|
|
14
|
-
import { Base, kBootstrapBrokers, kCheckNotClosed, kClosed, kConnections, kCreateConnectionPool, kFetchConnections, kFormatValidationErrors, kMetadata, kOptions, kPerformDeduplicated, kPerformWithRetry, kPrometheus, kValidateOptions } from "../base/base.js";
|
|
15
|
+
import { Base, kAfterCreate, kBootstrapBrokers, kCheckNotClosed, kClosed, kConnections, kCreateConnectionPool, kFetchConnections, kFormatValidationErrors, kMetadata, kOptions, kPerformDeduplicated, kPerformWithRetry, kPrometheus, kValidateOptions } from "../base/base.js";
|
|
15
16
|
import { defaultBaseOptions } from "../base/options.js";
|
|
16
17
|
import { createPromisifiedCallback, kCallbackPromise, runConcurrentCallbacks } from "../callbacks.js";
|
|
17
18
|
import { ensureMetric } from "../metrics.js";
|
|
@@ -72,6 +73,7 @@ export class Consumer extends Base {
|
|
|
72
73
|
this.#metricActiveStreams = ensureMetric(this[kPrometheus], 'Gauge', 'kafka_consumers_streams', 'Number of active Kafka consumers streams');
|
|
73
74
|
this.topics.setMetric(ensureMetric(this[kPrometheus], 'Gauge', 'kafka_consumers_topics', 'Number of topics being consumed'));
|
|
74
75
|
}
|
|
76
|
+
this[kAfterCreate]('consumer');
|
|
75
77
|
}
|
|
76
78
|
get streamsCount() {
|
|
77
79
|
return this.#streams.size;
|
|
@@ -153,7 +155,7 @@ export class Consumer extends Base {
|
|
|
153
155
|
callback(validationError, undefined);
|
|
154
156
|
return callback[kCallbackPromise];
|
|
155
157
|
}
|
|
156
|
-
this.#fetch(options, callback);
|
|
158
|
+
consumerFetchesChannel.traceCallback(this.#fetch, 1, createDiagnosticContext({ client: this, operation: 'fetch', options }), this, options, callback);
|
|
157
159
|
return callback[kCallbackPromise];
|
|
158
160
|
}
|
|
159
161
|
commit(options, callback) {
|
|
@@ -168,7 +170,7 @@ export class Consumer extends Base {
|
|
|
168
170
|
callback(validationError);
|
|
169
171
|
return callback[kCallbackPromise];
|
|
170
172
|
}
|
|
171
|
-
this.#commit(options, callback);
|
|
173
|
+
consumerCommitsChannel.traceCallback(this.#commit, 1, createDiagnosticContext({ client: this, operation: 'commit', options }), this, options, callback);
|
|
172
174
|
return callback[kCallbackPromise];
|
|
173
175
|
}
|
|
174
176
|
listOffsets(options, callback) {
|
|
@@ -183,7 +185,7 @@ export class Consumer extends Base {
|
|
|
183
185
|
callback(validationError, undefined);
|
|
184
186
|
return callback[kCallbackPromise];
|
|
185
187
|
}
|
|
186
|
-
this.#listOffsets(options, callback);
|
|
188
|
+
consumerOffsetsChannel.traceCallback(this.#listOffsets, 1, createDiagnosticContext({ client: this, operation: 'listOffsets', options }), this, options, callback);
|
|
187
189
|
return callback[kCallbackPromise];
|
|
188
190
|
}
|
|
189
191
|
listCommittedOffsets(options, callback) {
|
|
@@ -198,7 +200,7 @@ export class Consumer extends Base {
|
|
|
198
200
|
callback(validationError, undefined);
|
|
199
201
|
return callback[kCallbackPromise];
|
|
200
202
|
}
|
|
201
|
-
this.#listCommittedOffsets(options, callback);
|
|
203
|
+
consumerOffsetsChannel.traceCallback(this.#listCommittedOffsets, 1, createDiagnosticContext({ client: this, operation: 'listCommittedOffsets', options }), this, options, callback);
|
|
202
204
|
return callback[kCallbackPromise];
|
|
203
205
|
}
|
|
204
206
|
findGroupCoordinator(callback) {
|
|
@@ -258,37 +260,8 @@ export class Consumer extends Base {
|
|
|
258
260
|
});
|
|
259
261
|
return callback[kCallbackPromise];
|
|
260
262
|
}
|
|
261
|
-
#consume(options, callback
|
|
262
|
-
|
|
263
|
-
let joinNeeded = this.memberId === null;
|
|
264
|
-
if (trackTopics) {
|
|
265
|
-
for (const topic of options.topics) {
|
|
266
|
-
if (this.topics.track(topic)) {
|
|
267
|
-
joinNeeded = true;
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
// If we need to (re)join the group, do that first and then try again
|
|
272
|
-
if (joinNeeded) {
|
|
273
|
-
this.joinGroup(options, error => {
|
|
274
|
-
if (error) {
|
|
275
|
-
callback(error, undefined);
|
|
276
|
-
return;
|
|
277
|
-
}
|
|
278
|
-
this.#consume(options, callback, false);
|
|
279
|
-
});
|
|
280
|
-
return callback[kCallbackPromise];
|
|
281
|
-
}
|
|
282
|
-
// Create the stream and start consuming
|
|
283
|
-
const stream = new MessagesStream(this, options);
|
|
284
|
-
this.#streams.add(stream);
|
|
285
|
-
this.#metricActiveStreams?.inc();
|
|
286
|
-
stream.once('close', () => {
|
|
287
|
-
this.#metricActiveStreams?.dec();
|
|
288
|
-
this.#streams.delete(stream);
|
|
289
|
-
});
|
|
290
|
-
callback(null, stream);
|
|
291
|
-
return callback[kCallbackPromise];
|
|
263
|
+
#consume(options, callback) {
|
|
264
|
+
consumerConsumesChannel.traceCallback(this.#performConsume, 2, createDiagnosticContext({ client: this, operation: 'consume', options }), this, options, true, callback);
|
|
292
265
|
}
|
|
293
266
|
#fetch(options, callback) {
|
|
294
267
|
this[kPerformWithRetry]('fetch', retryCallback => {
|
|
@@ -427,6 +400,91 @@ export class Consumer extends Base {
|
|
|
427
400
|
callback(null, this.#coordinatorId);
|
|
428
401
|
return;
|
|
429
402
|
}
|
|
403
|
+
consumerGroupChannel.traceCallback(this.#performFindGroupCoordinator, 0, createDiagnosticContext({ client: this, operation: 'findGroupCoordinator' }), this, callback);
|
|
404
|
+
}
|
|
405
|
+
#joinGroup(options, callback) {
|
|
406
|
+
consumerGroupChannel.traceCallback(this.#performJoinGroup, 1, createDiagnosticContext({ client: this, operation: 'joinGroup', options }), this, options, callback);
|
|
407
|
+
}
|
|
408
|
+
#leaveGroup(force, callback) {
|
|
409
|
+
consumerGroupChannel.traceCallback(this.#performLeaveGroup, 1, createDiagnosticContext({ client: this, operation: 'leaveGroup', force }), this, force, callback);
|
|
410
|
+
}
|
|
411
|
+
#syncGroup(callback) {
|
|
412
|
+
consumerGroupChannel.traceCallback(this.#performSyncGroup, 1, createDiagnosticContext({ client: this, operation: 'syncGroup' }), this, null, callback);
|
|
413
|
+
}
|
|
414
|
+
#heartbeat(options) {
|
|
415
|
+
const eventPayload = { groupId: this.groupId, memberId: this.memberId, generationId: this.generationId };
|
|
416
|
+
consumerHeartbeatChannel.traceCallback((this.#performDeduplicateGroupOperaton), 2, createDiagnosticContext({ client: this, operation: 'heartbeat' }), this, 'heartbeat', (connection, groupCallback) => {
|
|
417
|
+
// We have left the group in the meanwhile, abort
|
|
418
|
+
if (!this.#membershipActive) {
|
|
419
|
+
this.emitWithDebug('consumer:heartbeat', 'cancel', eventPayload);
|
|
420
|
+
return;
|
|
421
|
+
}
|
|
422
|
+
this.emitWithDebug('consumer:heartbeat', 'start', eventPayload);
|
|
423
|
+
heartbeatV4(connection, this.groupId, this.generationId, this.memberId, null, groupCallback);
|
|
424
|
+
}, error => {
|
|
425
|
+
// The heartbeat has been aborted elsewhere, ignore the response
|
|
426
|
+
if (this.#heartbeatInterval === null || !this.#membershipActive) {
|
|
427
|
+
this.emitWithDebug('consumer:heartbeat', 'cancel', eventPayload);
|
|
428
|
+
return;
|
|
429
|
+
}
|
|
430
|
+
if (error) {
|
|
431
|
+
this.#cancelHeartbeat();
|
|
432
|
+
if (this.#getRejoinError(error)) {
|
|
433
|
+
this[kPerformWithRetry]('rejoinGroup', retryCallback => {
|
|
434
|
+
this.#joinGroup(options, retryCallback);
|
|
435
|
+
}, error => {
|
|
436
|
+
if (error) {
|
|
437
|
+
this.emitWithDebug(null, 'error', error);
|
|
438
|
+
}
|
|
439
|
+
this.emitWithDebug('consumer', 'rejoin');
|
|
440
|
+
}, 0);
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
443
|
+
this.emitWithDebug('consumer:heartbeat', 'error', { ...eventPayload, error });
|
|
444
|
+
// Note that here we purposely do not return, since it was not a group related problem we schedule another heartbeat
|
|
445
|
+
}
|
|
446
|
+
else {
|
|
447
|
+
this.emitWithDebug('consumer:heartbeat', 'end', eventPayload);
|
|
448
|
+
}
|
|
449
|
+
this.#heartbeatInterval?.refresh();
|
|
450
|
+
});
|
|
451
|
+
}
|
|
452
|
+
#cancelHeartbeat() {
|
|
453
|
+
clearTimeout(this.#heartbeatInterval);
|
|
454
|
+
this.#heartbeatInterval = null;
|
|
455
|
+
}
|
|
456
|
+
#performConsume(options, trackTopics, callback) {
|
|
457
|
+
// Subscribe all topics
|
|
458
|
+
let joinNeeded = this.memberId === null;
|
|
459
|
+
if (trackTopics) {
|
|
460
|
+
for (const topic of options.topics) {
|
|
461
|
+
if (this.topics.track(topic)) {
|
|
462
|
+
joinNeeded = true;
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
// If we need to (re)join the group, do that first and then try again
|
|
467
|
+
if (joinNeeded) {
|
|
468
|
+
this.joinGroup(options, error => {
|
|
469
|
+
if (error) {
|
|
470
|
+
callback(error, undefined);
|
|
471
|
+
return;
|
|
472
|
+
}
|
|
473
|
+
this.#performConsume(options, false, callback);
|
|
474
|
+
});
|
|
475
|
+
return;
|
|
476
|
+
}
|
|
477
|
+
// Create the stream and start consuming
|
|
478
|
+
const stream = new MessagesStream(this, options);
|
|
479
|
+
this.#streams.add(stream);
|
|
480
|
+
this.#metricActiveStreams?.inc();
|
|
481
|
+
stream.once('close', () => {
|
|
482
|
+
this.#metricActiveStreams?.dec();
|
|
483
|
+
this.#streams.delete(stream);
|
|
484
|
+
});
|
|
485
|
+
callback(null, stream);
|
|
486
|
+
}
|
|
487
|
+
#performFindGroupCoordinator(callback) {
|
|
430
488
|
this[kPerformDeduplicated]('findGroupCoordinator', deduplicateCallback => {
|
|
431
489
|
this[kPerformWithRetry]('findGroupCoordinator', retryCallback => {
|
|
432
490
|
this[kConnections].getFirstAvailable(this[kBootstrapBrokers], (error, connection) => {
|
|
@@ -447,7 +505,7 @@ export class Consumer extends Base {
|
|
|
447
505
|
}, 0);
|
|
448
506
|
}, callback);
|
|
449
507
|
}
|
|
450
|
-
#
|
|
508
|
+
#performJoinGroup(options, callback) {
|
|
451
509
|
if (!this.#membershipActive) {
|
|
452
510
|
callback(null, undefined);
|
|
453
511
|
return;
|
|
@@ -469,7 +527,7 @@ export class Consumer extends Base {
|
|
|
469
527
|
}
|
|
470
528
|
if (error) {
|
|
471
529
|
if (this.#getRejoinError(error)) {
|
|
472
|
-
this.#
|
|
530
|
+
this.#performJoinGroup(options, callback);
|
|
473
531
|
return;
|
|
474
532
|
}
|
|
475
533
|
callback(error, undefined);
|
|
@@ -483,14 +541,14 @@ export class Consumer extends Base {
|
|
|
483
541
|
this.#members.set(member.memberId, this.#decodeProtocolSubscriptionMetadata(member.memberId, member.metadata));
|
|
484
542
|
}
|
|
485
543
|
// Send a syncGroup request
|
|
486
|
-
this.#syncGroup(
|
|
544
|
+
this.#syncGroup((error, response) => {
|
|
487
545
|
if (!this.#membershipActive) {
|
|
488
546
|
callback(null, undefined);
|
|
489
547
|
return;
|
|
490
548
|
}
|
|
491
549
|
if (error) {
|
|
492
550
|
if (this.#getRejoinError(error)) {
|
|
493
|
-
this.#
|
|
551
|
+
this.#performJoinGroup(options, callback);
|
|
494
552
|
return;
|
|
495
553
|
}
|
|
496
554
|
callback(error, undefined);
|
|
@@ -512,7 +570,7 @@ export class Consumer extends Base {
|
|
|
512
570
|
});
|
|
513
571
|
});
|
|
514
572
|
}
|
|
515
|
-
#
|
|
573
|
+
#performLeaveGroup(force, callback) {
|
|
516
574
|
if (!this.memberId) {
|
|
517
575
|
callback(null);
|
|
518
576
|
return;
|
|
@@ -536,7 +594,7 @@ export class Consumer extends Base {
|
|
|
536
594
|
return;
|
|
537
595
|
}
|
|
538
596
|
// All streams are closed, try the operation again without force
|
|
539
|
-
this.#
|
|
597
|
+
this.#performLeaveGroup(false, callback);
|
|
540
598
|
});
|
|
541
599
|
return;
|
|
542
600
|
}
|
|
@@ -563,7 +621,7 @@ export class Consumer extends Base {
|
|
|
563
621
|
callback(null);
|
|
564
622
|
});
|
|
565
623
|
}
|
|
566
|
-
#
|
|
624
|
+
#performSyncGroup(assignments, callback) {
|
|
567
625
|
if (!this.#membershipActive) {
|
|
568
626
|
callback(null, []);
|
|
569
627
|
return;
|
|
@@ -587,7 +645,7 @@ export class Consumer extends Base {
|
|
|
587
645
|
callback(error, undefined);
|
|
588
646
|
return;
|
|
589
647
|
}
|
|
590
|
-
this.#
|
|
648
|
+
this.#performSyncGroup(this.#roundRobinAssignments(metadata), callback);
|
|
591
649
|
});
|
|
592
650
|
return;
|
|
593
651
|
}
|
|
@@ -618,48 +676,6 @@ export class Consumer extends Base {
|
|
|
618
676
|
callback(error, assignments);
|
|
619
677
|
});
|
|
620
678
|
}
|
|
621
|
-
#heartbeat(options) {
|
|
622
|
-
const eventPayload = { groupId: this.groupId, memberId: this.memberId, generationId: this.generationId };
|
|
623
|
-
this.#performDeduplicateGroupOperaton('heartbeat', (connection, groupCallback) => {
|
|
624
|
-
// We have left the group in the meanwhile, abort
|
|
625
|
-
if (!this.#membershipActive) {
|
|
626
|
-
this.emitWithDebug('consumer:heartbeat', 'cancel', eventPayload);
|
|
627
|
-
return;
|
|
628
|
-
}
|
|
629
|
-
this.emitWithDebug('consumer:heartbeat', 'start', eventPayload);
|
|
630
|
-
heartbeatV4(connection, this.groupId, this.generationId, this.memberId, null, groupCallback);
|
|
631
|
-
}, error => {
|
|
632
|
-
// The heartbeat has been aborted elsewhere, ignore the response
|
|
633
|
-
if (this.#heartbeatInterval === null || !this.#membershipActive) {
|
|
634
|
-
this.emitWithDebug('consumer:heartbeat', 'cancel', eventPayload);
|
|
635
|
-
return;
|
|
636
|
-
}
|
|
637
|
-
if (error) {
|
|
638
|
-
this.#cancelHeartbeat();
|
|
639
|
-
if (this.#getRejoinError(error)) {
|
|
640
|
-
this[kPerformWithRetry]('rejoinGroup', retryCallback => {
|
|
641
|
-
this.#joinGroup(options, retryCallback);
|
|
642
|
-
}, error => {
|
|
643
|
-
if (error) {
|
|
644
|
-
this.emitWithDebug(null, 'error', error);
|
|
645
|
-
}
|
|
646
|
-
this.emitWithDebug('consumer', 'rejoin');
|
|
647
|
-
}, 0);
|
|
648
|
-
return;
|
|
649
|
-
}
|
|
650
|
-
this.emitWithDebug('consumer:heartbeat', 'error', { ...eventPayload, error });
|
|
651
|
-
// Note that here we purposely do not return, since it was not a group related problem we schedule another heartbeat
|
|
652
|
-
}
|
|
653
|
-
else {
|
|
654
|
-
this.emitWithDebug('consumer:heartbeat', 'end', eventPayload);
|
|
655
|
-
}
|
|
656
|
-
this.#heartbeatInterval?.refresh();
|
|
657
|
-
});
|
|
658
|
-
}
|
|
659
|
-
#cancelHeartbeat() {
|
|
660
|
-
clearTimeout(this.#heartbeatInterval);
|
|
661
|
-
this.#heartbeatInterval = null;
|
|
662
|
-
}
|
|
663
679
|
#performDeduplicateGroupOperaton(operationId, operation, callback) {
|
|
664
680
|
return this[kPerformDeduplicated](operationId, deduplicateCallback => {
|
|
665
681
|
this.#performGroupOperation(operationId, operation, deduplicateCallback);
|
|
@@ -5,6 +5,7 @@ import { type CallbackWithPromise } from '../callbacks.ts';
|
|
|
5
5
|
import { type Consumer } from './consumer.ts';
|
|
6
6
|
import { type CommitOptionsPartition, type ConsumeOptions } from './types.ts';
|
|
7
7
|
export declare function noopDeserializer(data?: Buffer): Buffer | undefined;
|
|
8
|
+
export declare function defaultCorruptedMessageHandler(): boolean;
|
|
8
9
|
export declare class MessagesStream<Key, Value, HeaderKey, HeaderValue> extends Readable {
|
|
9
10
|
#private;
|
|
10
11
|
constructor(consumer: Consumer<Key, Value, HeaderKey, HeaderValue>, options: ConsumeOptions<Key, Value, HeaderKey, HeaderValue>);
|