wsp-ms-core 1.1.28 → 1.1.30

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.d.mts CHANGED
@@ -649,7 +649,9 @@ declare class KafkaManager extends EventManager {
649
649
  private readonly groupId;
650
650
  private leaderSelectionPromise;
651
651
  private producerConnected;
652
+ private producerConnectPromise;
652
653
  private isRecoveringLeader;
654
+ private readonly hasExplicitGroupId;
653
655
  constructor(connection: EventManagerConnection);
654
656
  private ensureProducerConnected;
655
657
  private initializeKafkaClients;
@@ -664,6 +666,7 @@ declare class KafkaManager extends EventManager {
664
666
  private logConsumerEvent;
665
667
  private handleKafkaError;
666
668
  private commitMessage;
669
+ private startHeartbeatLoop;
667
670
  private run;
668
671
  send(e: EventMessage): Promise<void>;
669
672
  start(autocommit?: boolean): Promise<void>;
package/dist/index.d.ts CHANGED
@@ -649,7 +649,9 @@ declare class KafkaManager extends EventManager {
649
649
  private readonly groupId;
650
650
  private leaderSelectionPromise;
651
651
  private producerConnected;
652
+ private producerConnectPromise;
652
653
  private isRecoveringLeader;
654
+ private readonly hasExplicitGroupId;
653
655
  constructor(connection: EventManagerConnection);
654
656
  private ensureProducerConnected;
655
657
  private initializeKafkaClients;
@@ -664,6 +666,7 @@ declare class KafkaManager extends EventManager {
664
666
  private logConsumerEvent;
665
667
  private handleKafkaError;
666
668
  private commitMessage;
669
+ private startHeartbeatLoop;
667
670
  private run;
668
671
  send(e: EventMessage): Promise<void>;
669
672
  start(autocommit?: boolean): Promise<void>;
package/dist/index.js CHANGED
@@ -1731,18 +1731,44 @@ var KafkaManager = class extends EventManager {
1731
1731
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
1732
1732
  this.runtimeProcess = globalThis?.process;
1733
1733
  this.producerConnected = false;
1734
+ this.producerConnectPromise = null;
1734
1735
  this.isRecoveringLeader = false;
1735
1736
  this.seedBrokers = this.resolveBrokerList(this._connection.brokers ?? []);
1736
- this.groupId = this.runtimeProcess?.env?.KAFKA_GROUP || "default";
1737
+ const envGroupId = this.runtimeProcess?.env?.KAFKA_GROUP;
1738
+ this.hasExplicitGroupId = Boolean(envGroupId);
1739
+ this.groupId = envGroupId || "__missing-kafka-group__";
1740
+ if (!this.hasExplicitGroupId) {
1741
+ console.warn("[KafkaManager] KAFKA_GROUP is not set. Producing is allowed, but consuming will fail until a unique group id is configured.");
1742
+ }
1737
1743
  this.initializeKafkaClients(this.seedBrokers);
1738
1744
  this.leaderSelectionPromise = this.pinToLeaderBroker();
1739
1745
  }
1746
+ // La conexión se comparte entre sends concurrentes vía un latch: sin él, dos send() en paralelo
1747
+ // dispararían dos connect() simultáneos sobre el mismo producer. El producer queda conectado
1748
+ // entre sends (kafkajs está diseñado para conexiones long-lived); solo stop() lo desconecta.
1749
+ // Los guards `this.producer === producer` protegen contra initializeKafkaClients() reemplazando
1750
+ // el producer a mitad de un connect (recovery de líder): sin ellos, el latch viejo marcaría como
1751
+ // conectado al producer nuevo sin estarlo, y todos los sends siguientes fallarían.
1740
1752
  async ensureProducerConnected() {
1741
1753
  await this.waitForLeaderSelection();
1742
1754
  if (this.producerConnected)
1743
1755
  return;
1744
- await this.producer.connect();
1745
- this.producerConnected = true;
1756
+ if (!this.producerConnectPromise) {
1757
+ const producer = this.producer;
1758
+ this.producerConnectPromise = producer.connect().then(() => {
1759
+ if (this.producer === producer) {
1760
+ this.producerConnected = true;
1761
+ }
1762
+ }).finally(() => {
1763
+ if (this.producer === producer) {
1764
+ this.producerConnectPromise = null;
1765
+ }
1766
+ });
1767
+ }
1768
+ await this.producerConnectPromise;
1769
+ if (!this.producerConnected) {
1770
+ return this.ensureProducerConnected();
1771
+ }
1746
1772
  }
1747
1773
  initializeKafkaClients(brokers) {
1748
1774
  this.kafka = new Kafka({
@@ -1759,6 +1785,7 @@ var KafkaManager = class extends EventManager {
1759
1785
  this.producer = this.kafka.producer();
1760
1786
  this.brokers = brokers;
1761
1787
  this.producerConnected = false;
1788
+ this.producerConnectPromise = null;
1762
1789
  this.registerConsumerEventHandlers();
1763
1790
  }
1764
1791
  async pinToLeaderBroker() {
@@ -1949,7 +1976,22 @@ var KafkaManager = class extends EventManager {
1949
1976
  const next = (BigInt(message.offset) + 1n).toString();
1950
1977
  await this.consumer.commitOffsets([{ topic, partition, offset: next }]);
1951
1978
  }
1979
+ // Mantiene viva la sesión del consumer mientras un handler tarda más que el sessionTimeout
1980
+ // (default 30s de kafkajs). Sin esto, un handler lento hace que el broker expulse al consumer
1981
+ // a mitad de procesamiento → rebalance → el mensaje se reentrega → loop. kafkajs ya throttlea
1982
+ // heartbeat() internamente al heartbeatInterval, así que llamarlo de más no tiene costo.
1983
+ startHeartbeatLoop(heartbeat) {
1984
+ const interval = setInterval(() => {
1985
+ heartbeat().catch((error) => {
1986
+ console.warn("[KafkaManager] Heartbeat failed during message processing", { error: error?.message ?? error });
1987
+ });
1988
+ }, 5e3);
1989
+ return () => clearInterval(interval);
1990
+ }
1952
1991
  async run(autocommit = false) {
1992
+ if (!this.hasExplicitGroupId) {
1993
+ throw new InternalError(ErrorManager.APP_ERRORS.PROCESS, "KAFKA_GROUP env var is required to consume. Set a unique consumer group id per service.");
1994
+ }
1953
1995
  const __run = async () => {
1954
1996
  await this.waitForLeaderSelection();
1955
1997
  await this.execCallback(this._onStart, { message: `--- ${this.constructor.name} started ---` });
@@ -1997,7 +2039,12 @@ var KafkaManager = class extends EventManager {
1997
2039
  console.log("[KafkaManager] H - before execRoute", {
1998
2040
  offset: message.offset
1999
2041
  });
2000
- await this.execRoute(topic, event);
2042
+ const stopHeartbeatLoop = this.startHeartbeatLoop(heartbeat);
2043
+ try {
2044
+ await this.execRoute(topic, event);
2045
+ } finally {
2046
+ stopHeartbeatLoop();
2047
+ }
2001
2048
  console.log("[KafkaManager] I - after execRoute", {
2002
2049
  elapsedMs: Date.now() - execRouteStart
2003
2050
  });
@@ -2065,8 +2112,6 @@ var KafkaManager = class extends EventManager {
2065
2112
  }
2066
2113
  ]
2067
2114
  });
2068
- await this.producer.disconnect();
2069
- this.producerConnected = false;
2070
2115
  } catch (error) {
2071
2116
  throw new InternalError(error.toString());
2072
2117
  }
@@ -2083,6 +2128,9 @@ var KafkaManager = class extends EventManager {
2083
2128
  async stop() {
2084
2129
  await this.consumer.stop();
2085
2130
  await this.consumer.disconnect();
2131
+ if (this.producerConnectPromise) {
2132
+ await this.producerConnectPromise.catch(() => void 0);
2133
+ }
2086
2134
  if (this.producerConnected) {
2087
2135
  await this.producer.disconnect();
2088
2136
  this.producerConnected = false;
@@ -2113,7 +2161,9 @@ var _MysqlConnector = class _MysqlConnector {
2113
2161
  return rows;
2114
2162
  }
2115
2163
  async getConnection() {
2164
+ console.log("[MysqlConnector] BEFORE pool.getConnection");
2116
2165
  const conn = await this._pool.getConnection();
2166
+ console.log("[MysqlConnector] AFTER pool.getConnection");
2117
2167
  return this.wrap(conn);
2118
2168
  }
2119
2169
  async closePool() {
@@ -2292,6 +2342,7 @@ var DefaultMysqlInboxRunner = class {
2292
2342
  }
2293
2343
  async saveEvent(e, repository) {
2294
2344
  try {
2345
+ console.log("[InboxRunner] BEFORE create inbox record");
2295
2346
  let record = new InboxRecord(
2296
2347
  UUID.create(),
2297
2348
  e.tenant,
@@ -2300,7 +2351,9 @@ var DefaultMysqlInboxRunner = class {
2300
2351
  JSON.stringify(e.message),
2301
2352
  ProcessStatus.PENDING
2302
2353
  );
2354
+ console.log("[InboxRunner] BEFORE repository.create");
2303
2355
  await repository.create(record);
2356
+ console.log("[InboxRunner] AFTER repository.create");
2304
2357
  } catch (error) {
2305
2358
  throw error;
2306
2359
  }