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;
|
|
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 =
|
|
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
|
-
|
|
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.
|
|
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.
|
|
21
|
-
"podverse-helpers": "^5.1.
|
|
22
|
-
"podverse-orm": "^5.1.
|
|
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
|
},
|