@platformatic/kafka 2.3.1 → 2.4.0-alpha.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.
@@ -395,6 +395,10 @@ export class Consumer extends Base {
395
395
  options.rebalanceTimeout ??= this[kOptions].rebalanceTimeout;
396
396
  options.heartbeatInterval ??= this[kOptions].heartbeatInterval;
397
397
  options.protocols ??= this[kOptions].protocols;
398
+ const protocolsMetadata = this[kOptions].protocolsMetadata;
399
+ if (!options.protocolsMetadata && protocolsMetadata) {
400
+ options.protocolsMetadata = protocolsMetadata;
401
+ }
398
402
  this.#validateGroupOptions(options);
399
403
  this.#membershipActive = true;
400
404
  this.#joinGroup(options, callback);
@@ -1146,45 +1150,20 @@ export class Consumer extends Base {
1146
1150
  return;
1147
1151
  }
1148
1152
  this.#cancelHeartbeat();
1149
- const protocols = [];
1150
- for (const protocol of options.protocols) {
1151
- protocols.push({
1152
- name: protocol.name,
1153
- metadata: this.#encodeProtocolSubscriptionMetadata(protocol, this.topics.current)
1154
- });
1155
- }
1156
- this.#performDeduplicateGroupOperaton('joinGroup', (connection, groupCallback) => {
1157
- this[kGetApi]('JoinGroup', (error, api) => {
1158
- if (error) {
1159
- groupCallback(error);
1160
- return;
1161
- }
1162
- api(connection, this.groupId, options.sessionTimeout, options.rebalanceTimeout, this.memberId ?? '', this.groupInstanceId, 'consumer', protocols, '', groupCallback);
1163
- });
1164
- }, (error, response) => {
1165
- if (!this.#membershipActive) {
1166
- callback(null);
1167
- return;
1168
- }
1153
+ this.#createJoinGroupProtocols(options, (error, protocols) => {
1169
1154
  if (error) {
1170
- if (this.#getRejoinError(error)) {
1171
- this.#performJoinGroup(options, callback);
1172
- return;
1173
- }
1174
1155
  callback(error);
1175
1156
  return;
1176
1157
  }
1177
- // This is for Azure Event Hubs compatibility, which does not respond with an error on the first join
1178
- this.memberId = response.memberId;
1179
- this.generationId = response.generationId;
1180
- this.#isLeader = response.leader === this.memberId;
1181
- this.#protocol = response.protocolName;
1182
- this.#members = new Map();
1183
- for (const member of response.members) {
1184
- this.#members.set(member.memberId, this.#decodeProtocolSubscriptionMetadata(member.memberId, member.metadata));
1185
- }
1186
- // Send a syncGroup request
1187
- this.#syncGroup(options.partitionAssigner, (error, response) => {
1158
+ this.#performDeduplicateGroupOperaton('joinGroup', (connection, groupCallback) => {
1159
+ this[kGetApi]('JoinGroup', (error, api) => {
1160
+ if (error) {
1161
+ groupCallback(error);
1162
+ return;
1163
+ }
1164
+ api(connection, this.groupId, options.sessionTimeout, options.rebalanceTimeout, this.memberId ?? '', this.groupInstanceId, 'consumer', protocols, '', groupCallback);
1165
+ });
1166
+ }, (error, response) => {
1188
1167
  if (!this.#membershipActive) {
1189
1168
  callback(null);
1190
1169
  return;
@@ -1197,20 +1176,44 @@ export class Consumer extends Base {
1197
1176
  callback(error);
1198
1177
  return;
1199
1178
  }
1200
- this.assignments = response;
1201
- this.#syncPreferredReadReplicas();
1202
- this.#cancelHeartbeat();
1203
- this.#heartbeatInterval = setTimeout(() => {
1204
- this.#heartbeat(options);
1205
- }, options.heartbeatInterval);
1206
- this.emitWithDebug('consumer', 'group:join', {
1207
- groupId: this.groupId,
1208
- memberId: this.memberId,
1209
- generationId: this.generationId,
1210
- isLeader: this.#isLeader,
1211
- assignments: this.assignments
1179
+ // This is for Azure Event Hubs compatibility, which does not respond with an error on the first join
1180
+ this.memberId = response.memberId;
1181
+ this.generationId = response.generationId;
1182
+ this.#isLeader = response.leader === this.memberId;
1183
+ this.#protocol = response.protocolName;
1184
+ this.#members = new Map();
1185
+ for (const member of response.members) {
1186
+ this.#members.set(member.memberId, this.#decodeProtocolSubscriptionMetadata(member.memberId, member.metadata));
1187
+ }
1188
+ // Send a syncGroup request
1189
+ this.#syncGroup(options.partitionAssigner, (error, response) => {
1190
+ if (!this.#membershipActive) {
1191
+ callback(null);
1192
+ return;
1193
+ }
1194
+ if (error) {
1195
+ if (this.#getRejoinError(error)) {
1196
+ this.#performJoinGroup(options, callback);
1197
+ return;
1198
+ }
1199
+ callback(error);
1200
+ return;
1201
+ }
1202
+ this.assignments = response;
1203
+ this.#syncPreferredReadReplicas();
1204
+ this.#cancelHeartbeat();
1205
+ this.#heartbeatInterval = setTimeout(() => {
1206
+ this.#heartbeat(options);
1207
+ }, options.heartbeatInterval);
1208
+ this.emitWithDebug('consumer', 'group:join', {
1209
+ groupId: this.groupId,
1210
+ memberId: this.memberId,
1211
+ generationId: this.generationId,
1212
+ isLeader: this.#isLeader,
1213
+ assignments: this.assignments
1214
+ });
1215
+ callback(null, this.memberId);
1212
1216
  });
1213
- callback(null, this.memberId);
1214
1217
  });
1215
1218
  });
1216
1219
  }
@@ -1370,6 +1373,61 @@ export class Consumer extends Base {
1370
1373
  throw new UserError(this[kFormatValidationErrors](validator, '/options'));
1371
1374
  }
1372
1375
  }
1376
+ #createJoinGroupProtocols(options, callback) {
1377
+ if (typeof options.protocolsMetadata !== 'function') {
1378
+ callback(null, options.protocols.map(protocol => ({
1379
+ name: protocol.name,
1380
+ metadata: this.#encodeProtocolSubscriptionMetadata(protocol, this.topics.current)
1381
+ })));
1382
+ return;
1383
+ }
1384
+ this[kMetadata]({ topics: this.topics.current, forceUpdate: true }, (error, metadata) => {
1385
+ if (error) {
1386
+ callback(this.#handleError(error));
1387
+ return;
1388
+ }
1389
+ this.#resolveProtocolsMetadata(options.protocolsMetadata, options.protocols, this.topics.current, metadata, (error, userData) => {
1390
+ if (error) {
1391
+ callback(error);
1392
+ return;
1393
+ }
1394
+ else if (!Buffer.isBuffer(userData)) {
1395
+ callback(new UserError('protocolsMetadata must resolve to a Buffer.'));
1396
+ return;
1397
+ }
1398
+ callback(null, options.protocols.map(protocol => ({
1399
+ name: protocol.name,
1400
+ metadata: this.#encodeProtocolSubscriptionMetadata({ ...protocol, metadata: userData }, this.topics.current)
1401
+ })));
1402
+ });
1403
+ });
1404
+ }
1405
+ #resolveProtocolsMetadata(protocolsMetadata, protocols, topics, metadata, callback) {
1406
+ let settled = false;
1407
+ function resolveCallback(error, userData) {
1408
+ if (settled) {
1409
+ return;
1410
+ }
1411
+ settled = true;
1412
+ callback(error, userData);
1413
+ }
1414
+ let result;
1415
+ try {
1416
+ result = protocolsMetadata(protocols, topics, metadata, resolveCallback);
1417
+ if (typeof result !== 'undefined') {
1418
+ if (typeof result.then === 'function') {
1419
+ ;
1420
+ result.then(userData => process.nextTick(resolveCallback, null, userData), error => process.nextTick(resolveCallback, error));
1421
+ }
1422
+ else {
1423
+ resolveCallback(null, result);
1424
+ }
1425
+ }
1426
+ }
1427
+ catch (error) {
1428
+ resolveCallback(error);
1429
+ }
1430
+ }
1373
1431
  /*
1374
1432
  The following two methods follow:
1375
1433
  https://github.com/apache/kafka/blob/trunk/clients/src/main/resources/common/message/ConsumerProtocolSubscription.json
@@ -53,6 +53,9 @@ export declare const groupOptionsProperties: {
53
53
  };
54
54
  };
55
55
  };
56
+ protocolsMetadata: {
57
+ function: boolean;
58
+ };
56
59
  partitionAssigner: {
57
60
  function: boolean;
58
61
  };
@@ -195,6 +198,9 @@ export declare const groupOptionsSchema: {
195
198
  };
196
199
  };
197
200
  };
201
+ protocolsMetadata: {
202
+ function: boolean;
203
+ };
198
204
  partitionAssigner: {
199
205
  function: boolean;
200
206
  };
@@ -315,6 +321,9 @@ export declare const consumeOptionsSchema: {
315
321
  };
316
322
  };
317
323
  };
324
+ protocolsMetadata: {
325
+ function: boolean;
326
+ };
318
327
  partitionAssigner: {
319
328
  function: boolean;
320
329
  };
@@ -481,6 +490,9 @@ export declare const consumerOptionsSchema: {
481
490
  };
482
491
  };
483
492
  };
493
+ protocolsMetadata: {
494
+ function: boolean;
495
+ };
484
496
  partitionAssigner: {
485
497
  function: boolean;
486
498
  };
@@ -608,6 +620,9 @@ export declare const fetchOptionsSchema: {
608
620
  };
609
621
  };
610
622
  };
623
+ protocolsMetadata: {
624
+ function: boolean;
625
+ };
611
626
  partitionAssigner: {
612
627
  function: boolean;
613
628
  };
@@ -25,6 +25,7 @@ export const groupOptionsProperties = {
25
25
  }
26
26
  }
27
27
  },
28
+ protocolsMetadata: { function: true },
28
29
  partitionAssigner: { function: true }
29
30
  };
30
31
  export const groupOptionsAdditionalValidations = {
@@ -1,3 +1,4 @@
1
+ import { type Callback } from '../../apis/definitions.ts';
1
2
  import { type FetchRequestTopic } from '../../apis/consumer/fetch-v17.ts';
2
3
  import { type GroupProtocols } from '../../apis/enumerations.ts';
3
4
  import { type ConnectionPool } from '../../network/connection-pool.ts';
@@ -33,6 +34,7 @@ export interface PreferredReadReplica {
33
34
  }
34
35
  export type CorruptedMessageHandler = (record: KafkaRecord, topic: string, partition: number, firstTimestamp: bigint, firstOffset: bigint, commit: Message['commit']) => boolean;
35
36
  export type GroupPartitionsAssigner = (current: string, members: Map<string, ExtendedGroupProtocolSubscription>, topics: Set<string>, metadata: ClusterMetadata) => GroupPartitionsAssignments[];
37
+ export type GroupProtocolsMetadataCallback = (protocols: GroupProtocolSubscription[], topics: string[], metadata: ClusterMetadata, callback: Callback<Buffer>) => Buffer | Promise<Buffer> | undefined;
36
38
  export declare const MessagesStreamModes: {
37
39
  readonly LATEST: "latest";
38
40
  readonly EARLIEST: "earliest";
@@ -57,6 +59,7 @@ export interface GroupOptions {
57
59
  rebalanceTimeout?: number;
58
60
  heartbeatInterval?: number;
59
61
  protocols?: GroupProtocolSubscription[];
62
+ protocolsMetadata?: GroupProtocolsMetadataCallback;
60
63
  partitionAssigner?: GroupPartitionsAssigner;
61
64
  assignmentUserData?: Buffer;
62
65
  }
@@ -53,6 +53,9 @@ export declare const groupOptionsProperties: {
53
53
  };
54
54
  };
55
55
  };
56
+ protocolsMetadata: {
57
+ function: boolean;
58
+ };
56
59
  partitionAssigner: {
57
60
  function: boolean;
58
61
  };
@@ -195,6 +198,9 @@ export declare const groupOptionsSchema: {
195
198
  };
196
199
  };
197
200
  };
201
+ protocolsMetadata: {
202
+ function: boolean;
203
+ };
198
204
  partitionAssigner: {
199
205
  function: boolean;
200
206
  };
@@ -315,6 +321,9 @@ export declare const consumeOptionsSchema: {
315
321
  };
316
322
  };
317
323
  };
324
+ protocolsMetadata: {
325
+ function: boolean;
326
+ };
318
327
  partitionAssigner: {
319
328
  function: boolean;
320
329
  };
@@ -481,6 +490,9 @@ export declare const consumerOptionsSchema: {
481
490
  };
482
491
  };
483
492
  };
493
+ protocolsMetadata: {
494
+ function: boolean;
495
+ };
484
496
  partitionAssigner: {
485
497
  function: boolean;
486
498
  };
@@ -608,6 +620,9 @@ export declare const fetchOptionsSchema: {
608
620
  };
609
621
  };
610
622
  };
623
+ protocolsMetadata: {
624
+ function: boolean;
625
+ };
611
626
  partitionAssigner: {
612
627
  function: boolean;
613
628
  };
@@ -1,3 +1,4 @@
1
+ import { type Callback } from "../../apis/definitions";
1
2
  import { type FetchRequestTopic } from "../../apis/consumer/fetch-v17";
2
3
  import { type GroupProtocols } from "../../apis/enumerations";
3
4
  import { type ConnectionPool } from "../../network/connection-pool";
@@ -33,6 +34,7 @@ export interface PreferredReadReplica {
33
34
  }
34
35
  export type CorruptedMessageHandler = (record: KafkaRecord, topic: string, partition: number, firstTimestamp: bigint, firstOffset: bigint, commit: Message['commit']) => boolean;
35
36
  export type GroupPartitionsAssigner = (current: string, members: Map<string, ExtendedGroupProtocolSubscription>, topics: Set<string>, metadata: ClusterMetadata) => GroupPartitionsAssignments[];
37
+ export type GroupProtocolsMetadataCallback = (protocols: GroupProtocolSubscription[], topics: string[], metadata: ClusterMetadata, callback: Callback<Buffer>) => Buffer | Promise<Buffer> | undefined;
36
38
  export declare const MessagesStreamModes: {
37
39
  readonly LATEST: "latest";
38
40
  readonly EARLIEST: "earliest";
@@ -57,6 +59,7 @@ export interface GroupOptions {
57
59
  rebalanceTimeout?: number;
58
60
  heartbeatInterval?: number;
59
61
  protocols?: GroupProtocolSubscription[];
62
+ protocolsMetadata?: GroupProtocolsMetadataCallback;
60
63
  partitionAssigner?: GroupPartitionsAssigner;
61
64
  assignmentUserData?: Buffer;
62
65
  }
package/dist/version.js CHANGED
@@ -1,2 +1,2 @@
1
1
  export const name = "@platformatic/kafka";
2
- export const version = "2.3.1";
2
+ export const version = "2.4.0-alpha.1";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@platformatic/kafka",
3
- "version": "2.3.1",
3
+ "version": "2.4.0-alpha.1",
4
4
  "description": "Modern and performant client for Apache Kafka",
5
5
  "homepage": "https://github.com/platformatic/kafka",
6
6
  "author": "Platformatic Inc. <oss@platformatic.dev> (https://platformatic.dev)",