wsp-ms-core 1.1.24 → 1.1.27

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.cjs CHANGED
@@ -1320,8 +1320,30 @@ var EventManager = class {
1320
1320
  this._onReconnect = null;
1321
1321
  }
1322
1322
  async execRoute(topic, event) {
1323
- if (this._callbackList[topic]) {
1324
- await this._callbackList[topic](event);
1323
+ console.log("[KafkaManager] execRoute START", {
1324
+ topic
1325
+ });
1326
+ const callback = this._callbackList[topic];
1327
+ if (!callback) {
1328
+ console.log("[KafkaManager] execRoute NO CALLBACK", {
1329
+ topic
1330
+ });
1331
+ return;
1332
+ }
1333
+ const start = Date.now();
1334
+ try {
1335
+ await callback(event);
1336
+ console.log("[KafkaManager] execRoute END", {
1337
+ topic,
1338
+ elapsedMs: Date.now() - start
1339
+ });
1340
+ } catch (e) {
1341
+ console.error("[KafkaManager] execRoute ERROR", {
1342
+ topic,
1343
+ elapsedMs: Date.now() - start,
1344
+ error: e
1345
+ });
1346
+ throw e;
1325
1347
  }
1326
1348
  }
1327
1349
  async execCallback(callback, data) {
@@ -1781,10 +1803,22 @@ var KafkaManager = class extends EventManager {
1781
1803
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
1782
1804
  this.runtimeProcess = globalThis?.process;
1783
1805
  this.producerConnected = false;
1784
- this.exitingDueToLeaderError = false;
1785
- this.brokers = this.resolveBrokerList(this._connection.brokers ?? []);
1806
+ this.isRecoveringLeader = false;
1807
+ this.seedBrokers = this.resolveBrokerList(this._connection.brokers ?? []);
1808
+ this.groupId = this.runtimeProcess?.env?.KAFKA_GROUP || "default";
1809
+ this.initializeKafkaClients(this.seedBrokers);
1810
+ this.leaderSelectionPromise = this.pinToLeaderBroker();
1811
+ }
1812
+ async ensureProducerConnected() {
1813
+ await this.waitForLeaderSelection();
1814
+ if (this.producerConnected)
1815
+ return;
1816
+ await this.producer.connect();
1817
+ this.producerConnected = true;
1818
+ }
1819
+ initializeKafkaClients(brokers) {
1786
1820
  this.kafka = new import_kafkajs.Kafka({
1787
- brokers: this.brokers,
1821
+ brokers,
1788
1822
  ssl: true,
1789
1823
  sasl: {
1790
1824
  mechanism: "scram-sha-256",
@@ -1793,16 +1827,92 @@ var KafkaManager = class extends EventManager {
1793
1827
  },
1794
1828
  logLevel: import_kafkajs.logLevel.ERROR
1795
1829
  });
1796
- const groupId = this.runtimeProcess?.env?.KAFKA_GROUP || "default";
1797
- this.consumer = this.kafka.consumer({ groupId });
1830
+ this.consumer = this.kafka.consumer({ groupId: this.groupId });
1798
1831
  this.producer = this.kafka.producer();
1832
+ this.brokers = brokers;
1833
+ this.producerConnected = false;
1799
1834
  this.registerConsumerEventHandlers();
1800
1835
  }
1801
- async ensureProducerConnected() {
1802
- if (this.producerConnected)
1836
+ async pinToLeaderBroker() {
1837
+ try {
1838
+ const leaderBroker = await this.fetchLeaderBroker(this.kafka);
1839
+ if (!leaderBroker) {
1840
+ console.warn("[KafkaManager] Unable to determine leader broker. Using configured list.", { brokers: this.brokers });
1841
+ return;
1842
+ }
1843
+ if (this.brokers.length === 1 && this.brokers[0] === leaderBroker) {
1844
+ return;
1845
+ }
1846
+ console.info("[KafkaManager] Leader broker resolved. Reinitializing clients", { leaderBroker });
1847
+ this.initializeKafkaClients([leaderBroker]);
1848
+ } catch (error) {
1849
+ console.warn("[KafkaManager] Error resolving leader broker. Falling back to configured brokers.", error);
1850
+ }
1851
+ }
1852
+ buildKafkaClient(brokers) {
1853
+ return new import_kafkajs.Kafka({
1854
+ brokers,
1855
+ ssl: true,
1856
+ sasl: {
1857
+ mechanism: "scram-sha-256",
1858
+ username: this._connection.userName,
1859
+ password: this._connection.password
1860
+ },
1861
+ logLevel: import_kafkajs.logLevel.ERROR
1862
+ });
1863
+ }
1864
+ async fetchLeaderBroker(kafkaClient) {
1865
+ const admin = kafkaClient.admin();
1866
+ try {
1867
+ await admin.connect();
1868
+ const cluster = await admin.describeCluster();
1869
+ const controllerId = cluster?.controller;
1870
+ const controller = controllerId !== void 0 ? cluster?.brokers?.find((node) => node.nodeId === controllerId) : void 0;
1871
+ if (!controller?.host) {
1872
+ return null;
1873
+ }
1874
+ const host = controller.host;
1875
+ const port = controller.port ?? 9092;
1876
+ return this.normalizeBroker(`${host}:${port}`);
1877
+ } finally {
1878
+ await admin.disconnect().catch(() => void 0);
1879
+ }
1880
+ }
1881
+ async recoverFromLeaderChange(origin) {
1882
+ if (this.isRecoveringLeader)
1803
1883
  return;
1804
- await this.producer.connect();
1805
- this.producerConnected = true;
1884
+ this.isRecoveringLeader = true;
1885
+ console.warn("[KafkaManager] Recovering from leader change", { origin, brokers: this.brokers });
1886
+ await this.execCallback(this._onReconnect, {
1887
+ message: "[KafkaManager] Leader change detected. Reconnecting to new leader.",
1888
+ context: { origin }
1889
+ });
1890
+ try {
1891
+ await this.stop().catch((error) => {
1892
+ console.warn("[KafkaManager] Error stopping previous connection during recovery", error);
1893
+ });
1894
+ const seedClient = this.buildKafkaClient(this.seedBrokers);
1895
+ const leaderBroker = await this.fetchLeaderBroker(seedClient).catch((error) => {
1896
+ console.warn("[KafkaManager] Error resolving new leader broker during recovery", error);
1897
+ return null;
1898
+ });
1899
+ const brokersToUse = leaderBroker ? [leaderBroker] : this.seedBrokers;
1900
+ console.info("[KafkaManager] Reconnecting using brokers", { brokersToUse });
1901
+ this.initializeKafkaClients(brokersToUse);
1902
+ this.leaderSelectionPromise = Promise.resolve();
1903
+ await this.run();
1904
+ } catch (error) {
1905
+ console.error("[KafkaManager] Failed to recover from leader change", { origin, error });
1906
+ await this.execCallback(this._onError, error);
1907
+ } finally {
1908
+ this.isRecoveringLeader = false;
1909
+ }
1910
+ }
1911
+ async waitForLeaderSelection() {
1912
+ try {
1913
+ await this.leaderSelectionPromise;
1914
+ } catch {
1915
+ }
1806
1916
  }
1807
1917
  resolveBrokerList(brokers) {
1808
1918
  const envBrokers = (this.runtimeProcess?.env?.KAFKA_BROKERS ?? "").split(",").map((value) => value.trim()).filter(Boolean);
@@ -1843,6 +1953,9 @@ var KafkaManager = class extends EventManager {
1843
1953
  this.consumer.on(events.CRASH, async (event) => {
1844
1954
  await this.logConsumerEvent("CRASH", event, "error");
1845
1955
  this.handleKafkaError(event?.payload?.error, "consumer.CRASH", { broker: event?.payload?.broker });
1956
+ if (event?.payload?.restart === false) {
1957
+ void this.recoverFromLeaderChange("consumer.CRASH.no-restart");
1958
+ }
1846
1959
  });
1847
1960
  this.consumer.on(events.DISCONNECT, async (event) => {
1848
1961
  await this.logConsumerEvent("DISCONNECT", event, "error");
@@ -1884,8 +1997,7 @@ var KafkaManager = class extends EventManager {
1884
1997
  return;
1885
1998
  const errorType = error.type ?? error.name;
1886
1999
  const isNotLeaderError = errorType === "NOT_LEADER_FOR_PARTITION" || error?.name === "KafkaJSNotLeaderForPartition";
1887
- if (!this.exitingDueToLeaderError && isNotLeaderError) {
1888
- this.exitingDueToLeaderError = true;
2000
+ if (isNotLeaderError) {
1889
2001
  const payload = {
1890
2002
  origin,
1891
2003
  broker: error?.broker ?? metadata?.broker,
@@ -1893,12 +2005,12 @@ var KafkaManager = class extends EventManager {
1893
2005
  partition: error?.partition,
1894
2006
  metadata
1895
2007
  };
1896
- console.error("[KafkaManager][NOT_LEADER_FOR_PARTITION] Forcing shutdown", payload);
2008
+ console.error("[KafkaManager][NOT_LEADER_FOR_PARTITION] Reconnecting to new leader", payload);
1897
2009
  void this.execCallback(this._onCrash, {
1898
- message: "[KafkaManager] NOT_LEADER_FOR_PARTITION detected. Forcing shutdown.",
2010
+ message: "[KafkaManager] NOT_LEADER_FOR_PARTITION detected. Reconnecting to new leader.",
1899
2011
  context: payload
1900
2012
  });
1901
- setTimeout(() => this.runtimeProcess?.exit?.(1), 0);
2013
+ void this.recoverFromLeaderChange(origin);
1902
2014
  return;
1903
2015
  }
1904
2016
  if (error instanceof import_kafkajs.KafkaJSRequestTimeoutError || errorType === "REQUEST_TIMEOUT") {
@@ -1911,6 +2023,7 @@ var KafkaManager = class extends EventManager {
1911
2023
  }
1912
2024
  async run(autocommit = false) {
1913
2025
  const __run = async () => {
2026
+ await this.waitForLeaderSelection();
1914
2027
  await this.execCallback(this._onStart, { message: `--- ${this.constructor.name} started ---` });
1915
2028
  await this.consumer.connect();
1916
2029
  await this.execCallback(this._onConnected, { message: `--- ${this.constructor.name} connected to ${this.brokers.join(", ")} ---` });
@@ -1926,31 +2039,70 @@ var KafkaManager = class extends EventManager {
1926
2039
  autoCommit: false,
1927
2040
  // @ts-ignore
1928
2041
  eachMessage: async ({ topic, partition, message, heartbeat }) => {
2042
+ console.log("[KafkaManager] A - eachMessage START", {
2043
+ topic,
2044
+ partition,
2045
+ offset: message.offset
2046
+ });
1929
2047
  try {
1930
- await this.execCallback(this._onMessage, `[New message detected for ${topic}]: ${message.value?.toString()}`);
1931
- let json;
1932
- let event;
1933
- try {
1934
- json = JSON.parse(String(message.value?.toString()));
1935
- if (!json.data)
1936
- json.data = {};
1937
- event = {
1938
- topic: String(topic),
1939
- producer: String(json.producer),
1940
- tenant: UUID.create(String(json.tenant)),
1941
- message: json.data
1942
- };
1943
- } catch (error) {
1944
- await this.commitMessage(topic, partition, message);
1945
- await heartbeat();
1946
- throw error;
2048
+ console.log("[KafkaManager] B - before execCallback(_onMessage)");
2049
+ await this.execCallback(
2050
+ this._onMessage,
2051
+ `[New message detected for ${topic}]: ${message.value?.toString()}`
2052
+ );
2053
+ console.log("[KafkaManager] C - after execCallback(_onMessage)");
2054
+ console.log("[KafkaManager] D - before JSON.parse");
2055
+ const json = JSON.parse(message.value.toString());
2056
+ console.log("[KafkaManager] E - after JSON.parse");
2057
+ if (!json.data) {
2058
+ json.data = {};
1947
2059
  }
2060
+ console.log("[KafkaManager] F - before EventMessage creation");
2061
+ const event = {
2062
+ topic: String(topic),
2063
+ producer: String(json.producer),
2064
+ tenant: UUID.create(String(json.tenant)),
2065
+ message: json.data
2066
+ };
2067
+ console.log("[KafkaManager] G - EventMessage created");
2068
+ const execRouteStart = Date.now();
2069
+ console.log("[KafkaManager] H - before execRoute", {
2070
+ offset: message.offset
2071
+ });
1948
2072
  await this.execRoute(topic, event);
2073
+ console.log("[KafkaManager] I - after execRoute", {
2074
+ elapsedMs: Date.now() - execRouteStart
2075
+ });
2076
+ const commitStart = Date.now();
2077
+ console.log("[KafkaManager] J - before commitMessage");
1949
2078
  await this.commitMessage(topic, partition, message);
2079
+ console.log("[KafkaManager] K - after commitMessage", {
2080
+ elapsedMs: Date.now() - commitStart
2081
+ });
2082
+ const hbStart = Date.now();
2083
+ console.log("[KafkaManager] L - before heartbeat");
1950
2084
  await heartbeat();
2085
+ console.log("[KafkaManager] M - after heartbeat", {
2086
+ elapsedMs: Date.now() - hbStart
2087
+ });
2088
+ console.log("[KafkaManager] N - eachMessage END");
1951
2089
  } catch (error) {
1952
- await this.execCallback(this._onError, error);
1953
- this.handleKafkaError(error, "consumer.eachMessage", { topic, partition });
2090
+ console.error("[KafkaManager] ERROR inside eachMessage", {
2091
+ topic,
2092
+ partition,
2093
+ offset: message.offset,
2094
+ error
2095
+ });
2096
+ try {
2097
+ await this.execCallback(this._onError, error);
2098
+ } catch (callbackError) {
2099
+ console.error("[KafkaManager] ERROR executing _onError callback", callbackError);
2100
+ }
2101
+ this.handleKafkaError(error, "consumer.eachMessage", {
2102
+ topic,
2103
+ partition,
2104
+ offset: message.offset
2105
+ });
1954
2106
  throw error;
1955
2107
  }
1956
2108
  }
@@ -1963,6 +2115,7 @@ var KafkaManager = class extends EventManager {
1963
2115
  }
1964
2116
  }
1965
2117
  async send(e) {
2118
+ await this.waitForLeaderSelection();
1966
2119
  const evt = {
1967
2120
  date: DateTime.now().value,
1968
2121
  tenant: e.tenant.value,