podverse-mq 5.1.12 → 5.1.13-alpha.0

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.
@@ -29,6 +29,12 @@ export declare class ActiveMQArtemisService {
29
29
  private logger;
30
30
  private connecting;
31
31
  private isShuttingDown;
32
+ private readonly tcpKeepAliveMs;
33
+ private readonly idleTimeOutMs;
34
+ private keepAliveApplied;
35
+ private readonly enableAmqpPing;
36
+ private heartbeatSender;
37
+ private heartbeatInterval;
32
38
  constructor(params: ActiveMQArtemisServiceParams, logger: LoggerService);
33
39
  initialize(): Promise<void>;
34
40
  private connect;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/services/activeMQArtemis/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAgC,YAAY,EAAE,MAAM,MAAM,CAAC;AAClE,OAAO,EAAE,aAAa,EAAE,MAAM,0CAA0C,CAAC;AAGzE,OAAO,EAAE,oCAAoC,EAAE,MAAM,qCAAqC,CAAC;AAE3F,MAAM,MAAM,WAAW,GACnB,YAAY,GACZ,eAAe,GACf,UAAU,GACV,OAAO,YAAY,GAAG,eAAe,GAAG,UAAU,EAAE,CAAC;AAEzD,KAAK,YAAY,GAAG;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,OAAO,EAAE,oCAAoC,CAAC;CAC/C,CAAC;AAEF,KAAK,OAAO,GAAG,YAAY,CAAC;AAE5B,KAAK,iBAAiB,GAAG;IACvB,SAAS,EAAE,WAAW,CAAA;IACtB,OAAO,EAAE,OAAO,CAAA;IAChB,QAAQ,EAAE,QAAQ,GAAG,MAAM,CAAA;IAC3B,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAA;CACjC,CAAA;AAED,MAAM,WAAW,4BAA4B;IAC3C,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,qBAAa,sBAAsB;IACjC,OAAO,CAAC,UAAU,CAA2B;IAC7C,OAAO,CAAC,OAAO,CAAuC;IACtD,OAAO,CAAC,SAAS,CAAyC;IAC1D,OAAO,CAAC,MAAM,CAA+B;IAC7C,OAAO,CAAC,MAAM,CAAgB;IAC9B,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,cAAc,CAAS;gBAEnB,MAAM,EAAE,4BAA4B,EAAE,MAAM,EAAE,aAAa;IAKjE,UAAU;YASF,OAAO;YAkEP,YAAY;YAaZ,cAAc;IAc5B,OAAO,CAAC,kBAAkB;IAQpB,WAAW,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IAyC3D;;;;OAIG;IACG,eAAe,CACnB,SAAS,EAAE,WAAW,EACtB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,kBAAkB,SAAqC,GACtD,OAAO,CAAC,IAAI,CAAC;IAoDV,eAAe,CAAC,SAAS,EAAE,WAAW,EAAE,cAAc,EAAE,CAAC,OAAO,EAAE,YAAY,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI;IA4B7G,iBAAiB,IAAI,OAAO;IAItB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAkF7B"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/services/activeMQArtemis/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAgC,YAAY,EAAE,MAAM,MAAM,CAAC;AAClE,OAAO,EAAE,aAAa,EAAE,MAAM,0CAA0C,CAAC;AAGzE,OAAO,EAAE,oCAAoC,EAAE,MAAM,qCAAqC,CAAC;AAE3F,MAAM,MAAM,WAAW,GACnB,YAAY,GACZ,eAAe,GACf,UAAU,GACV,OAAO,YAAY,GAAG,eAAe,GAAG,UAAU,EAAE,CAAC;AAEzD,KAAK,YAAY,GAAG;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,OAAO,EAAE,oCAAoC,CAAC;CAC/C,CAAC;AAEF,KAAK,OAAO,GAAG,YAAY,CAAC;AAE5B,KAAK,iBAAiB,GAAG;IACvB,SAAS,EAAE,WAAW,CAAA;IACtB,OAAO,EAAE,OAAO,CAAA;IAChB,QAAQ,EAAE,QAAQ,GAAG,MAAM,CAAA;IAC3B,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAA;CACjC,CAAA;AAED,MAAM,WAAW,4BAA4B;IAC3C,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,qBAAa,sBAAsB;IACjC,OAAO,CAAC,UAAU,CAA2B;IAC7C,OAAO,CAAC,OAAO,CAAuC;IACtD,OAAO,CAAC,SAAS,CAAyC;IAC1D,OAAO,CAAC,MAAM,CAA+B;IAC7C,OAAO,CAAC,MAAM,CAAgB;IAC9B,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,cAAc,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAiB;IAChD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAiB;IAC/C,OAAO,CAAC,gBAAgB,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAA4D;IAC3F,OAAO,CAAC,eAAe,CAAuB;IAC9C,OAAO,CAAC,iBAAiB,CAA+C;gBAE5D,MAAM,EAAE,4BAA4B,EAAE,MAAM,EAAE,aAAa;IAKjE,UAAU;YASF,OAAO;YAiKP,YAAY;YAaZ,cAAc;IAc5B,OAAO,CAAC,kBAAkB;IAQpB,WAAW,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IAyC3D;;;;OAIG;IACG,eAAe,CACnB,SAAS,EAAE,WAAW,EACtB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,kBAAkB,SAAqC,GACtD,OAAO,CAAC,IAAI,CAAC;IAoDV,eAAe,CAAC,SAAS,EAAE,WAAW,EAAE,cAAc,EAAE,CAAC,OAAO,EAAE,YAAY,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI;IA4B7G,iBAAiB,IAAI,OAAO;IAItB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAoG7B"}
@@ -23,6 +23,12 @@ class ActiveMQArtemisService {
23
23
  this.receivers = new Map();
24
24
  this.connecting = false;
25
25
  this.isShuttingDown = false;
26
+ this.tcpKeepAliveMs = 30000;
27
+ this.idleTimeOutMs = 60000;
28
+ this.keepAliveApplied = false;
29
+ this.enableAmqpPing = (process.env.ARTEMIS_DISABLE_AMQP_PING !== '1');
30
+ this.heartbeatSender = null;
31
+ this.heartbeatInterval = null;
26
32
  this.params = params;
27
33
  this.logger = logger;
28
34
  }
@@ -53,7 +59,7 @@ class ActiveMQArtemisService {
53
59
  this.connecting = false; // allow retry attempts later
54
60
  return reject(err);
55
61
  }
56
- const idleTimeOut = 30000;
62
+ const idleTimeOut = this.idleTimeOutMs;
57
63
  const baseId = process.env.CONTAINER_ID
58
64
  || process.env.HOSTNAME
59
65
  || `podverse-mq-${crypto_1.default.randomBytes(4).toString('hex')}`;
@@ -70,18 +76,123 @@ class ActiveMQArtemisService {
70
76
  reconnect: true,
71
77
  reconnect_limit: -1
72
78
  });
73
- connection.on('connection_open', () => {
79
+ connection.on('connection_open', (context) => {
74
80
  this.logger.info('Artemis AMQP connection established');
81
+ try {
82
+ // Log negotiated connection properties to help debug heartbeat/idle settings
83
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
84
+ const connAny = context && context.connection;
85
+ const local = connAny && connAny.options ? connAny.options : undefined;
86
+ const remote = connAny && connAny.remote ? connAny.remote : undefined;
87
+ this.logger.info('Artemis connection negotiation', { local, remote });
88
+ }
89
+ catch (err) {
90
+ this.logger.logError('Failed to log negotiated connection properties', err);
91
+ }
75
92
  this.connection = connection;
76
93
  this.connecting = false;
94
+ // Best-effort: enable Node TCP keepalive on the underlying socket so
95
+ // the OS detects dead peers even if AMQP-level heartbeats are missed.
96
+ try {
97
+ // rhea internal socket location varies between versions; probe common places.
98
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
99
+ const connAny = connection;
100
+ const sock = connAny.socket ||
101
+ connAny._socket ||
102
+ (connAny.transport && (connAny.transport.socket || connAny.transport._socket));
103
+ if (sock && typeof sock.setKeepAlive === 'function') {
104
+ if (!this.keepAliveApplied) {
105
+ sock.setKeepAlive(true, this.tcpKeepAliveMs);
106
+ this.keepAliveApplied = true;
107
+ this.logger.info(`Enabled TCP keepalive on Artemis socket (${this.tcpKeepAliveMs}ms)`);
108
+ }
109
+ else {
110
+ this.logger.info('TCP keepalive already applied to Artemis socket');
111
+ }
112
+ // Start optional AMQP-level pings if enabled
113
+ if (this.enableAmqpPing) {
114
+ try {
115
+ const hbSender = connection.open_sender({ target: { address: 'podverse.keepalive' } });
116
+ hbSender.on('sender_open', () => {
117
+ var _a;
118
+ this.heartbeatSender = hbSender;
119
+ if (this.heartbeatInterval)
120
+ clearInterval(this.heartbeatInterval);
121
+ const heartbeatMs = Number((_a = process.env.ARTEMIS_AMQP_PING_MS) !== null && _a !== void 0 ? _a : Math.max(1000, Math.floor(this.idleTimeOutMs / 2)));
122
+ this.logger.info('AMQP heartbeat interval (ms)', { heartbeatMs });
123
+ this.heartbeatInterval = setInterval(() => {
124
+ try {
125
+ if (this.heartbeatSender) {
126
+ this.heartbeatSender.send({ body: `ping:${Date.now()}` });
127
+ }
128
+ }
129
+ catch (err) {
130
+ this.logger.logError('AMQP keepalive ping failed', err);
131
+ }
132
+ }, heartbeatMs);
133
+ });
134
+ hbSender.on('sender_error', (ctx) => {
135
+ this.logger.logError('AMQP heartbeat sender_error', (ctx && (ctx.error || ctx)));
136
+ });
137
+ hbSender.on('sender_close', () => {
138
+ if (this.heartbeatInterval) {
139
+ clearInterval(this.heartbeatInterval);
140
+ this.heartbeatInterval = null;
141
+ }
142
+ this.heartbeatSender = null;
143
+ });
144
+ }
145
+ catch (err) {
146
+ this.logger.logError('Failed to start optional AMQP heartbeat sender', err);
147
+ }
148
+ }
149
+ }
150
+ else {
151
+ this.logger.info('Could not find underlying socket to enable TCP keepalive (non-fatal)');
152
+ }
153
+ }
154
+ catch (err) {
155
+ this.logger.logError('Failed to enable TCP keepalive on Artemis socket', err);
156
+ }
77
157
  resolve();
78
158
  });
79
159
  connection.on('connection_error', (context) => {
80
160
  this.logger.logError('Artemis connection error', context.error);
81
161
  });
82
- connection.on('disconnected', () => {
83
- this.logger.info('Artemis connection disconnected will attempt reconnect');
162
+ connection.on('disconnected', (context) => {
163
+ // Provide the disconnect reason if available — this helps determine whether
164
+ // the broker closed the AMQP link due to AMQP-level idle timeout or network issues.
165
+ try {
166
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
167
+ const reason = (context && (context.error || context.disconnect_reason)) || undefined;
168
+ this.logger.info('Artemis connection disconnected – will attempt reconnect', { reason });
169
+ }
170
+ catch (_a) {
171
+ this.logger.info('Artemis connection disconnected – will attempt reconnect');
172
+ }
84
173
  this.connection = null;
174
+ // keepAliveApplied resets so it can be re-applied on next open
175
+ this.keepAliveApplied = false;
176
+ if (this.enableAmqpPing) {
177
+ if (this.heartbeatInterval) {
178
+ clearInterval(this.heartbeatInterval);
179
+ this.heartbeatInterval = null;
180
+ }
181
+ try {
182
+ if (this.heartbeatSender) {
183
+ try {
184
+ this.heartbeatSender.close();
185
+ }
186
+ catch (_b) {
187
+ // swallow
188
+ }
189
+ this.heartbeatSender = null;
190
+ }
191
+ }
192
+ catch (err) {
193
+ this.logger.logError('Error closing heartbeatSender on disconnect', err);
194
+ }
195
+ }
85
196
  });
86
197
  }
87
198
  catch (err) {
@@ -333,6 +444,28 @@ class ActiveMQArtemisService {
333
444
  this.logger.logError('Error closing connection', error);
334
445
  }
335
446
  this.connection = null;
447
+ // reset keepalive flag
448
+ this.keepAliveApplied = false;
449
+ if (this.enableAmqpPing) {
450
+ if (this.heartbeatInterval) {
451
+ clearInterval(this.heartbeatInterval);
452
+ this.heartbeatInterval = null;
453
+ }
454
+ try {
455
+ if (this.heartbeatSender) {
456
+ try {
457
+ this.heartbeatSender.close();
458
+ }
459
+ catch (_d) {
460
+ // swallow
461
+ }
462
+ this.heartbeatSender = null;
463
+ }
464
+ }
465
+ catch (err) {
466
+ this.logger.logError('Error closing heartbeatSender on close', err);
467
+ }
468
+ }
336
469
  }
337
470
  });
338
471
  // race close against a timeout to avoid hanging shutdown; create timer before starting close
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "podverse-mq",
3
- "version": "5.1.12",
3
+ "version": "5.1.13-alpha.0",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -17,10 +17,10 @@
17
17
  "license": "AGPLv3",
18
18
  "dependencies": {
19
19
  "module-alias": "^2.2.3",
20
- "podverse-external-services": "^5.1.12",
21
- "podverse-helpers": "^5.1.12",
22
- "podverse-orm": "^5.1.12",
23
- "podverse-parser": "^5.1.12",
20
+ "podverse-external-services": "^5.1.13-alpha.1",
21
+ "podverse-helpers": "^5.1.13-alpha.0",
22
+ "podverse-orm": "^5.1.13-alpha.0",
23
+ "podverse-parser": "^5.1.12-alpha.2",
24
24
  "rhea": "^2.0.6",
25
25
  "ws": "^8.18.3"
26
26
  },