@drarzter/kafka-client 0.6.3 → 0.6.4

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/index.js CHANGED
@@ -563,7 +563,7 @@ async function executeWithRetry(fn, ctx, deps) {
563
563
  }
564
564
  } else {
565
565
  const cap = Math.min(backoffMs * 2 ** (attempt - 1), maxBackoffMs);
566
- await sleep(Math.random() * cap);
566
+ await sleep(Math.floor(Math.random() * cap));
567
567
  }
568
568
  }
569
569
  }
@@ -937,7 +937,8 @@ var KafkaClient = class {
937
937
  kafka;
938
938
  producer;
939
939
  txProducer;
940
- retryTxProducers = /* @__PURE__ */ new Set();
940
+ /** Maps transactionalId Producer for each active retry level consumer. */
941
+ retryTxProducers = /* @__PURE__ */ new Map();
941
942
  consumers = /* @__PURE__ */ new Map();
942
943
  admin;
943
944
  logger;
@@ -1203,18 +1204,32 @@ var KafkaClient = class {
1203
1204
  this.consumerCreationOptions.delete(cGroupId);
1204
1205
  this.logger.log(`Retry consumer disconnected: group "${cGroupId}"`);
1205
1206
  }
1207
+ const txId = `${cGroupId}-tx`;
1208
+ const txProducer = this.retryTxProducers.get(txId);
1209
+ if (txProducer) {
1210
+ await txProducer.disconnect().catch(() => {
1211
+ });
1212
+ this.retryTxProducers.delete(txId);
1213
+ }
1206
1214
  }
1207
1215
  this.companionGroupIds.delete(groupId);
1208
1216
  } else {
1209
- const tasks = Array.from(this.consumers.values()).map(
1210
- (c) => c.disconnect().catch(() => {
1211
- })
1212
- );
1217
+ const tasks = [
1218
+ ...Array.from(this.consumers.values()).map(
1219
+ (c) => c.disconnect().catch(() => {
1220
+ })
1221
+ ),
1222
+ ...Array.from(this.retryTxProducers.values()).map(
1223
+ (p) => p.disconnect().catch(() => {
1224
+ })
1225
+ )
1226
+ ];
1213
1227
  await Promise.allSettled(tasks);
1214
1228
  this.consumers.clear();
1215
1229
  this.runningConsumers.clear();
1216
1230
  this.consumerCreationOptions.clear();
1217
1231
  this.companionGroupIds.clear();
1232
+ this.retryTxProducers.clear();
1218
1233
  this.logger.log("All consumers disconnected");
1219
1234
  }
1220
1235
  }
@@ -1222,6 +1237,12 @@ var KafkaClient = class {
1222
1237
  * Query consumer group lag per partition.
1223
1238
  * Lag = broker high-watermark − last committed offset.
1224
1239
  * A committed offset of -1 (nothing committed yet) counts as full lag.
1240
+ *
1241
+ * Returns an empty array when the consumer group has never committed any
1242
+ * offsets (freshly created group, `autoCommit: false` with no manual commits,
1243
+ * or group not yet assigned). This is a Kafka protocol limitation:
1244
+ * `fetchOffsets` only returns data for topic-partitions that have at least one
1245
+ * committed offset. Use `checkStatus()` to verify broker connectivity in that case.
1225
1246
  */
1226
1247
  async getConsumerLag(groupId) {
1227
1248
  const gid = groupId ?? this.defaultGroupId;
@@ -1270,7 +1291,7 @@ var KafkaClient = class {
1270
1291
  tasks.push(this.txProducer.disconnect());
1271
1292
  this.txProducer = void 0;
1272
1293
  }
1273
- for (const p of this.retryTxProducers) {
1294
+ for (const p of this.retryTxProducers.values()) {
1274
1295
  tasks.push(p.disconnect());
1275
1296
  }
1276
1297
  this.retryTxProducers.clear();
@@ -1289,6 +1310,14 @@ var KafkaClient = class {
1289
1310
  this.logger.log("All connections closed");
1290
1311
  }
1291
1312
  // ── Graceful shutdown ────────────────────────────────────────────
1313
+ /**
1314
+ * NestJS lifecycle hook — called automatically when the host module is torn down.
1315
+ * Drains in-flight handlers and disconnects all producers, consumers, and admin.
1316
+ * `KafkaModule` relies on this method; no separate destroy provider is needed.
1317
+ */
1318
+ async onModuleDestroy() {
1319
+ await this.disconnect();
1320
+ }
1292
1321
  /**
1293
1322
  * Register SIGTERM / SIGINT handlers that drain in-flight messages before
1294
1323
  * disconnecting. Call this once after constructing the client in non-NestJS apps.
@@ -1439,7 +1468,7 @@ var KafkaClient = class {
1439
1468
  }
1440
1469
  });
1441
1470
  await p.connect();
1442
- this.retryTxProducers.add(p);
1471
+ this.retryTxProducers.set(transactionalId, p);
1443
1472
  return p;
1444
1473
  }
1445
1474
  async ensureTopic(topic2) {
@@ -1468,6 +1497,12 @@ var KafkaClient = class {
1468
1497
  `Cannot use ${mode} on consumer group "${gid}" \u2014 it is already running with ${oppositeMode}. Use a different groupId for this consumer.`
1469
1498
  );
1470
1499
  }
1500
+ if (existingMode === mode) {
1501
+ const callerName = mode === "eachMessage" ? "startConsumer" : "startBatchConsumer";
1502
+ throw new Error(
1503
+ `${callerName}("${gid}") called twice \u2014 this group is already consuming. Call stopConsumer("${gid}") first or pass a different groupId.`
1504
+ );
1505
+ }
1471
1506
  const consumer = getOrCreateConsumer(
1472
1507
  gid,
1473
1508
  fromBeginning,
@@ -1682,11 +1717,7 @@ var KafkaModule = class {
1682
1717
  global: options.isGlobal ?? false,
1683
1718
  module: KafkaModule,
1684
1719
  imports: [import_core2.DiscoveryModule],
1685
- providers: [
1686
- kafkaClientProvider,
1687
- KafkaModule.buildDestroyProvider(token),
1688
- KafkaExplorer
1689
- ],
1720
+ providers: [kafkaClientProvider, KafkaExplorer],
1690
1721
  exports: [kafkaClientProvider]
1691
1722
  };
1692
1723
  }
@@ -1702,11 +1733,7 @@ var KafkaModule = class {
1702
1733
  global: asyncOptions.isGlobal ?? false,
1703
1734
  module: KafkaModule,
1704
1735
  imports: [...asyncOptions.imports || [], import_core2.DiscoveryModule],
1705
- providers: [
1706
- kafkaClientProvider,
1707
- KafkaModule.buildDestroyProvider(token),
1708
- KafkaExplorer
1709
- ],
1736
+ providers: [kafkaClientProvider, KafkaExplorer],
1710
1737
  exports: [kafkaClientProvider]
1711
1738
  };
1712
1739
  }
@@ -1728,15 +1755,6 @@ var KafkaModule = class {
1728
1755
  await client.connectProducer();
1729
1756
  return client;
1730
1757
  }
1731
- static buildDestroyProvider(token) {
1732
- return {
1733
- provide: `${token}_DESTROY`,
1734
- useFactory: (client) => ({
1735
- onModuleDestroy: () => client.disconnect()
1736
- }),
1737
- inject: [token]
1738
- };
1739
- }
1740
1758
  };
1741
1759
  KafkaModule = __decorateClass([
1742
1760
  (0, import_common3.Module)({})