wsp-ms-core 1.1.24 → 1.1.26

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
@@ -1781,10 +1781,22 @@ var KafkaManager = class extends EventManager {
1781
1781
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
1782
1782
  this.runtimeProcess = globalThis?.process;
1783
1783
  this.producerConnected = false;
1784
- this.exitingDueToLeaderError = false;
1785
- this.brokers = this.resolveBrokerList(this._connection.brokers ?? []);
1784
+ this.isRecoveringLeader = false;
1785
+ this.seedBrokers = this.resolveBrokerList(this._connection.brokers ?? []);
1786
+ this.groupId = this.runtimeProcess?.env?.KAFKA_GROUP || "default";
1787
+ this.initializeKafkaClients(this.seedBrokers);
1788
+ this.leaderSelectionPromise = this.pinToLeaderBroker();
1789
+ }
1790
+ async ensureProducerConnected() {
1791
+ await this.waitForLeaderSelection();
1792
+ if (this.producerConnected)
1793
+ return;
1794
+ await this.producer.connect();
1795
+ this.producerConnected = true;
1796
+ }
1797
+ initializeKafkaClients(brokers) {
1786
1798
  this.kafka = new import_kafkajs.Kafka({
1787
- brokers: this.brokers,
1799
+ brokers,
1788
1800
  ssl: true,
1789
1801
  sasl: {
1790
1802
  mechanism: "scram-sha-256",
@@ -1793,16 +1805,92 @@ var KafkaManager = class extends EventManager {
1793
1805
  },
1794
1806
  logLevel: import_kafkajs.logLevel.ERROR
1795
1807
  });
1796
- const groupId = this.runtimeProcess?.env?.KAFKA_GROUP || "default";
1797
- this.consumer = this.kafka.consumer({ groupId });
1808
+ this.consumer = this.kafka.consumer({ groupId: this.groupId });
1798
1809
  this.producer = this.kafka.producer();
1810
+ this.brokers = brokers;
1811
+ this.producerConnected = false;
1799
1812
  this.registerConsumerEventHandlers();
1800
1813
  }
1801
- async ensureProducerConnected() {
1802
- if (this.producerConnected)
1814
+ async pinToLeaderBroker() {
1815
+ try {
1816
+ const leaderBroker = await this.fetchLeaderBroker(this.kafka);
1817
+ if (!leaderBroker) {
1818
+ console.warn("[KafkaManager] Unable to determine leader broker. Using configured list.", { brokers: this.brokers });
1819
+ return;
1820
+ }
1821
+ if (this.brokers.length === 1 && this.brokers[0] === leaderBroker) {
1822
+ return;
1823
+ }
1824
+ console.info("[KafkaManager] Leader broker resolved. Reinitializing clients", { leaderBroker });
1825
+ this.initializeKafkaClients([leaderBroker]);
1826
+ } catch (error) {
1827
+ console.warn("[KafkaManager] Error resolving leader broker. Falling back to configured brokers.", error);
1828
+ }
1829
+ }
1830
+ buildKafkaClient(brokers) {
1831
+ return new import_kafkajs.Kafka({
1832
+ brokers,
1833
+ ssl: true,
1834
+ sasl: {
1835
+ mechanism: "scram-sha-256",
1836
+ username: this._connection.userName,
1837
+ password: this._connection.password
1838
+ },
1839
+ logLevel: import_kafkajs.logLevel.ERROR
1840
+ });
1841
+ }
1842
+ async fetchLeaderBroker(kafkaClient) {
1843
+ const admin = kafkaClient.admin();
1844
+ try {
1845
+ await admin.connect();
1846
+ const cluster = await admin.describeCluster();
1847
+ const controllerId = cluster?.controller;
1848
+ const controller = controllerId !== void 0 ? cluster?.brokers?.find((node) => node.nodeId === controllerId) : void 0;
1849
+ if (!controller?.host) {
1850
+ return null;
1851
+ }
1852
+ const host = controller.host;
1853
+ const port = controller.port ?? 9092;
1854
+ return this.normalizeBroker(`${host}:${port}`);
1855
+ } finally {
1856
+ await admin.disconnect().catch(() => void 0);
1857
+ }
1858
+ }
1859
+ async recoverFromLeaderChange(origin) {
1860
+ if (this.isRecoveringLeader)
1803
1861
  return;
1804
- await this.producer.connect();
1805
- this.producerConnected = true;
1862
+ this.isRecoveringLeader = true;
1863
+ console.warn("[KafkaManager] Recovering from leader change", { origin, brokers: this.brokers });
1864
+ await this.execCallback(this._onReconnect, {
1865
+ message: "[KafkaManager] Leader change detected. Reconnecting to new leader.",
1866
+ context: { origin }
1867
+ });
1868
+ try {
1869
+ await this.stop().catch((error) => {
1870
+ console.warn("[KafkaManager] Error stopping previous connection during recovery", error);
1871
+ });
1872
+ const seedClient = this.buildKafkaClient(this.seedBrokers);
1873
+ const leaderBroker = await this.fetchLeaderBroker(seedClient).catch((error) => {
1874
+ console.warn("[KafkaManager] Error resolving new leader broker during recovery", error);
1875
+ return null;
1876
+ });
1877
+ const brokersToUse = leaderBroker ? [leaderBroker] : this.seedBrokers;
1878
+ console.info("[KafkaManager] Reconnecting using brokers", { brokersToUse });
1879
+ this.initializeKafkaClients(brokersToUse);
1880
+ this.leaderSelectionPromise = Promise.resolve();
1881
+ await this.run();
1882
+ } catch (error) {
1883
+ console.error("[KafkaManager] Failed to recover from leader change", { origin, error });
1884
+ await this.execCallback(this._onError, error);
1885
+ } finally {
1886
+ this.isRecoveringLeader = false;
1887
+ }
1888
+ }
1889
+ async waitForLeaderSelection() {
1890
+ try {
1891
+ await this.leaderSelectionPromise;
1892
+ } catch {
1893
+ }
1806
1894
  }
1807
1895
  resolveBrokerList(brokers) {
1808
1896
  const envBrokers = (this.runtimeProcess?.env?.KAFKA_BROKERS ?? "").split(",").map((value) => value.trim()).filter(Boolean);
@@ -1843,6 +1931,9 @@ var KafkaManager = class extends EventManager {
1843
1931
  this.consumer.on(events.CRASH, async (event) => {
1844
1932
  await this.logConsumerEvent("CRASH", event, "error");
1845
1933
  this.handleKafkaError(event?.payload?.error, "consumer.CRASH", { broker: event?.payload?.broker });
1934
+ if (event?.payload?.restart === false) {
1935
+ void this.recoverFromLeaderChange("consumer.CRASH.no-restart");
1936
+ }
1846
1937
  });
1847
1938
  this.consumer.on(events.DISCONNECT, async (event) => {
1848
1939
  await this.logConsumerEvent("DISCONNECT", event, "error");
@@ -1884,8 +1975,7 @@ var KafkaManager = class extends EventManager {
1884
1975
  return;
1885
1976
  const errorType = error.type ?? error.name;
1886
1977
  const isNotLeaderError = errorType === "NOT_LEADER_FOR_PARTITION" || error?.name === "KafkaJSNotLeaderForPartition";
1887
- if (!this.exitingDueToLeaderError && isNotLeaderError) {
1888
- this.exitingDueToLeaderError = true;
1978
+ if (isNotLeaderError) {
1889
1979
  const payload = {
1890
1980
  origin,
1891
1981
  broker: error?.broker ?? metadata?.broker,
@@ -1893,12 +1983,12 @@ var KafkaManager = class extends EventManager {
1893
1983
  partition: error?.partition,
1894
1984
  metadata
1895
1985
  };
1896
- console.error("[KafkaManager][NOT_LEADER_FOR_PARTITION] Forcing shutdown", payload);
1986
+ console.error("[KafkaManager][NOT_LEADER_FOR_PARTITION] Reconnecting to new leader", payload);
1897
1987
  void this.execCallback(this._onCrash, {
1898
- message: "[KafkaManager] NOT_LEADER_FOR_PARTITION detected. Forcing shutdown.",
1988
+ message: "[KafkaManager] NOT_LEADER_FOR_PARTITION detected. Reconnecting to new leader.",
1899
1989
  context: payload
1900
1990
  });
1901
- setTimeout(() => this.runtimeProcess?.exit?.(1), 0);
1991
+ void this.recoverFromLeaderChange(origin);
1902
1992
  return;
1903
1993
  }
1904
1994
  if (error instanceof import_kafkajs.KafkaJSRequestTimeoutError || errorType === "REQUEST_TIMEOUT") {
@@ -1911,6 +2001,7 @@ var KafkaManager = class extends EventManager {
1911
2001
  }
1912
2002
  async run(autocommit = false) {
1913
2003
  const __run = async () => {
2004
+ await this.waitForLeaderSelection();
1914
2005
  await this.execCallback(this._onStart, { message: `--- ${this.constructor.name} started ---` });
1915
2006
  await this.consumer.connect();
1916
2007
  await this.execCallback(this._onConnected, { message: `--- ${this.constructor.name} connected to ${this.brokers.join(", ")} ---` });
@@ -1963,6 +2054,7 @@ var KafkaManager = class extends EventManager {
1963
2054
  }
1964
2055
  }
1965
2056
  async send(e) {
2057
+ await this.waitForLeaderSelection();
1966
2058
  const evt = {
1967
2059
  date: DateTime.now().value,
1968
2060
  tenant: e.tenant.value,