@autofleet/rabbit 4.0.0-beta.0 → 4.0.1
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.ts +24 -17
- package/dist/index.js +303 -180
- package/dist/index.js.map +1 -0
- package/dist/lib/celery.js +1 -0
- package/dist/lib/celery.js.map +1 -0
- package/dist/lib/consts.d.ts +0 -2
- package/dist/lib/consts.js +2 -3
- package/dist/lib/consts.js.map +1 -0
- package/dist/lib/rabbitError.js +1 -0
- package/dist/lib/rabbitError.js.map +1 -0
- package/dist/lib/redis.js +1 -0
- package/dist/lib/redis.js.map +1 -0
- package/dist/lib/types.d.ts +8 -0
- package/dist/lib/types.js +1 -0
- package/dist/lib/types.js.map +1 -0
- package/dist/lib/utils.js +1 -0
- package/dist/lib/utils.js.map +1 -0
- package/dist/logger.js +1 -0
- package/dist/logger.js.map +1 -0
- package/dist/{mock.d.ts → mock/index.d.ts} +1 -1
- package/dist/{mock.js → mock/index.js} +2 -1
- package/dist/mock/index.js.map +1 -0
- package/dist/mock/vitest.d.ts +13 -0
- package/dist/mock/vitest.js +18 -0
- package/dist/mock/vitest.js.map +1 -0
- package/package.json +22 -16
- package/src/index.ts +360 -203
- package/src/lib/consts.ts +0 -2
- package/src/lib/types.ts +10 -0
- package/src/{mock.ts → mock/index.ts} +2 -2
- package/src/mock/vitest.ts +24 -0
- package/tsconfig.build.json +5 -0
- package/tsconfig.json +2 -2
- package/vitest.config.ts +17 -0
- package/coverage/clover.xml +0 -669
- package/coverage/coverage-final.json +0 -9
- package/coverage/lcov-report/base.css +0 -224
- package/coverage/lcov-report/block-navigation.js +0 -87
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/index.html +0 -131
- package/coverage/lcov-report/prettify.css +0 -1
- package/coverage/lcov-report/prettify.js +0 -2
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +0 -196
- package/coverage/lcov-report/src/index.html +0 -131
- package/coverage/lcov-report/src/index.ts.html +0 -3826
- package/coverage/lcov-report/src/lib/celery.ts.html +0 -352
- package/coverage/lcov-report/src/lib/consts.ts.html +0 -142
- package/coverage/lcov-report/src/lib/index.html +0 -191
- package/coverage/lcov-report/src/lib/rabbitError.ts.html +0 -103
- package/coverage/lcov-report/src/lib/redis.ts.html +0 -133
- package/coverage/lcov-report/src/lib/types.ts.html +0 -268
- package/coverage/lcov-report/src/lib/utils.ts.html +0 -142
- package/coverage/lcov-report/src/logger.ts.html +0 -100
- package/coverage/lcov.info +0 -1076
package/src/index.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
/* eslint-disable no-empty,consistent-return,no-async-promise-executor,@typescript-eslint/no-unused-vars */
|
|
2
|
-
|
|
1
|
+
/* eslint-disable no-empty,consistent-return,no-async-promise-executor,@typescript-eslint/no-unused-vars,no-param-reassign */
|
|
3
2
|
import { EventEmitter, once } from 'events';
|
|
4
3
|
import { promisify } from 'util';
|
|
5
4
|
import moment from 'moment';
|
|
@@ -14,15 +13,12 @@ import {
|
|
|
14
13
|
getCurrentPayload, newTrace, traceTypes, createOrSetRabbitTrace, outbreak,
|
|
15
14
|
} from '@autofleet/zehut';
|
|
16
15
|
import { randomUUID } from 'node:crypto';
|
|
17
|
-
import axios from 'axios';
|
|
18
16
|
import logger from './logger';
|
|
19
17
|
import RabbitError from './lib/rabbitError';
|
|
20
18
|
import getRedisInstance, { RedisConfig } from './lib/redis';
|
|
21
19
|
import { assertExchangeFanout, rand, wrapSetImmediate } from './lib/utils';
|
|
22
20
|
import {
|
|
23
21
|
AUTOMATION_ID_HEADER,
|
|
24
|
-
CONNECTION_CREATED_CONST,
|
|
25
|
-
CONNECTION_FAILED_CONST,
|
|
26
22
|
DEFAULT_LOCK_TIMEOUT,
|
|
27
23
|
DEFAULT_OPTIONS,
|
|
28
24
|
RETRY_HEADER,
|
|
@@ -41,6 +37,7 @@ import {
|
|
|
41
37
|
CONSUMER_DEFAULT_OPTIONS,
|
|
42
38
|
QueueSetupPromisesDictionary,
|
|
43
39
|
AssertExchangePromisesDictionary,
|
|
40
|
+
ConnectionData,
|
|
44
41
|
} from './lib/types';
|
|
45
42
|
|
|
46
43
|
// const debug = nodeDebug('af-rabbitmq')
|
|
@@ -48,6 +45,12 @@ const debug = logger.debug.bind(logger);
|
|
|
48
45
|
|
|
49
46
|
const PUBLISH_TIMEOUT = 1000 * 10;
|
|
50
47
|
|
|
48
|
+
// TODO: [QUORUM-PHASE-3] Delete this env var
|
|
49
|
+
const {
|
|
50
|
+
DISABLE_QUORUM_QUEUES_CONSUME,
|
|
51
|
+
DISABLE_QUORUM_QUEUES_PUBLISH,
|
|
52
|
+
} = process.env;
|
|
53
|
+
|
|
51
54
|
export interface IAfRabbitMq {
|
|
52
55
|
ack: any;
|
|
53
56
|
nack: any;
|
|
@@ -89,11 +92,13 @@ type newChannelOpts = {
|
|
|
89
92
|
name?: string;
|
|
90
93
|
onClose?: null | ((args: any | null) => void);
|
|
91
94
|
options?: CreateChannelOpts | undefined;
|
|
95
|
+
connection: ConnectionData;
|
|
92
96
|
};
|
|
93
97
|
|
|
94
98
|
type assertChannelOpts = {
|
|
95
99
|
channelName?: string;
|
|
96
100
|
force?: boolean;
|
|
101
|
+
connection: ConnectionData;
|
|
97
102
|
}
|
|
98
103
|
|
|
99
104
|
type AfConsumer = {
|
|
@@ -146,13 +151,15 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
146
151
|
|
|
147
152
|
RECONNECT_MSG = 'rabbit: connection disconnect - reconnecting';
|
|
148
153
|
|
|
149
|
-
|
|
154
|
+
publishChannel: ChannelWrapper | null;
|
|
150
155
|
|
|
151
156
|
publishChannelSetupPromise: Promise<ChannelWrapper> | null;
|
|
152
157
|
|
|
153
158
|
blockReconnect: boolean | null | undefined
|
|
154
159
|
|
|
155
|
-
|
|
160
|
+
publishConnection: ConnectionData
|
|
161
|
+
|
|
162
|
+
consumeConnection: ConnectionData
|
|
156
163
|
|
|
157
164
|
em: EventEmitter;
|
|
158
165
|
|
|
@@ -166,7 +173,7 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
166
173
|
|
|
167
174
|
assertExchangePromises: AssertExchangePromisesDictionary;
|
|
168
175
|
|
|
169
|
-
options: AfRabbitOptions
|
|
176
|
+
options: AfRabbitOptions;
|
|
170
177
|
|
|
171
178
|
redisClient: any;
|
|
172
179
|
|
|
@@ -177,17 +184,20 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
177
184
|
|
|
178
185
|
private consumers: Array<AfConsumer> = [];
|
|
179
186
|
|
|
180
|
-
private
|
|
187
|
+
private doesVHostExist = false;
|
|
181
188
|
|
|
182
|
-
|
|
189
|
+
private vhost = 'quorum-vhost';
|
|
183
190
|
|
|
184
|
-
|
|
191
|
+
// TODO:[QUORUM-PHASE-3] Delete the old properties that we use for the old consumers and publishers
|
|
192
|
+
oldPublishChannel: ChannelWrapper | null;
|
|
185
193
|
|
|
186
194
|
oldPublishChannelSetupPromise: Promise<ChannelWrapper> | null;
|
|
187
195
|
|
|
188
196
|
oldBlockReconnect: boolean | null | undefined
|
|
189
197
|
|
|
190
|
-
|
|
198
|
+
oldPublishConnection: ConnectionData
|
|
199
|
+
|
|
200
|
+
oldConsumeConnection: ConnectionData
|
|
191
201
|
|
|
192
202
|
oldEm: EventEmitter;
|
|
193
203
|
|
|
@@ -207,9 +217,22 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
207
217
|
|
|
208
218
|
constructor(options: AfRabbitOptions = {}, redisConfig?: RedisConfig) {
|
|
209
219
|
this.em = new EventEmitter();
|
|
210
|
-
this.
|
|
220
|
+
this.publishChannel = null;
|
|
211
221
|
this.publishChannelSetupPromise = null;
|
|
212
|
-
this.
|
|
222
|
+
this.publishConnection = {
|
|
223
|
+
amqpConnection: null,
|
|
224
|
+
creatingConnection: false,
|
|
225
|
+
connectionCreatedEventName: 'publishConnectionCreated',
|
|
226
|
+
connectionFailedEventName: 'publishConnectionFailed',
|
|
227
|
+
blockReconnect: false,
|
|
228
|
+
};
|
|
229
|
+
this.consumeConnection = {
|
|
230
|
+
amqpConnection: null,
|
|
231
|
+
creatingConnection: false,
|
|
232
|
+
connectionCreatedEventName: 'consumeConnectionCreated',
|
|
233
|
+
connectionFailedEventName: 'consumeConnectionFailed',
|
|
234
|
+
blockReconnect: false,
|
|
235
|
+
};
|
|
213
236
|
this.creatingConnection = false;
|
|
214
237
|
this.exchanges = {};
|
|
215
238
|
this.queues = {};
|
|
@@ -218,10 +241,6 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
218
241
|
this.consumers = [];
|
|
219
242
|
this.options = options;
|
|
220
243
|
|
|
221
|
-
if (!options?.vhost) {
|
|
222
|
-
this.options.vhost = process.env.VHOST_NAME || 'quorum-vhost';
|
|
223
|
-
}
|
|
224
|
-
|
|
225
244
|
this.redisClient = redisConfig && getRedisInstance(redisConfig);
|
|
226
245
|
if (this.redisClient) {
|
|
227
246
|
this.redisLock = promisify(RedisLock(this.redisClient)) as RedisLockType;
|
|
@@ -237,11 +256,24 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
237
256
|
});
|
|
238
257
|
}
|
|
239
258
|
|
|
240
|
-
// TODO:
|
|
259
|
+
// TODO: [QUORUM-PHASE-3] Delete the old properties that we use for the old consumers and publishers
|
|
241
260
|
this.oldEm = new EventEmitter();
|
|
242
|
-
this.
|
|
261
|
+
this.oldPublishChannel = null;
|
|
243
262
|
this.oldPublishChannelSetupPromise = null;
|
|
244
|
-
this.
|
|
263
|
+
this.oldPublishConnection = {
|
|
264
|
+
amqpConnection: null,
|
|
265
|
+
creatingConnection: false,
|
|
266
|
+
connectionCreatedEventName: 'oldPublishConnectionCreated',
|
|
267
|
+
connectionFailedEventName: 'oldPublishConnectionFailed',
|
|
268
|
+
blockReconnect: false,
|
|
269
|
+
};
|
|
270
|
+
this.oldConsumeConnection = {
|
|
271
|
+
amqpConnection: null,
|
|
272
|
+
creatingConnection: false,
|
|
273
|
+
connectionCreatedEventName: 'oldConsumeConnectionCreated',
|
|
274
|
+
connectionFailedEventName: 'oldConsumeConnectionFailed',
|
|
275
|
+
blockReconnect: false,
|
|
276
|
+
};
|
|
245
277
|
this.oldCreatingConnection = false;
|
|
246
278
|
this.oldExchanges = {};
|
|
247
279
|
this.oldQueues = {};
|
|
@@ -252,57 +284,57 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
252
284
|
}
|
|
253
285
|
|
|
254
286
|
private assertVHost = async () => {
|
|
255
|
-
if (this.
|
|
287
|
+
if (this.doesVHostExist) {
|
|
256
288
|
return;
|
|
257
289
|
}
|
|
258
290
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
auth: {
|
|
266
|
-
username: process.env.RABBITMQ_USERNAME || 'guest',
|
|
267
|
-
password: process.env.RABBITMQ_PASSWORD || 'guest',
|
|
268
|
-
},
|
|
291
|
+
const username = process.env.RABBITMQ_USERNAME || 'guest';
|
|
292
|
+
const password = process.env.RABBITMQ_PASSWORD || 'guest';
|
|
293
|
+
const credentials = Buffer.from(`${username}:${password}`).toString('base64');
|
|
294
|
+
const headers = {
|
|
295
|
+
Authorization: `Basic ${credentials}`,
|
|
296
|
+
'Content-Type': 'application/json',
|
|
269
297
|
};
|
|
270
|
-
|
|
298
|
+
|
|
299
|
+
const rabbitHost = `http://${(this.options?.rabbitHost || process.env.RABBITMQ_SERVICE_HOST || 'localhost').split(':')[0]}:15672`;
|
|
300
|
+
|
|
301
|
+
const url = `${rabbitHost}/api/vhosts/${encodeURIComponent(this.vhost)}`;
|
|
271
302
|
|
|
272
303
|
try {
|
|
273
|
-
const response = await
|
|
304
|
+
const response = await fetch(url, {
|
|
305
|
+
method: 'GET',
|
|
306
|
+
headers,
|
|
307
|
+
});
|
|
274
308
|
|
|
275
|
-
if (response.status
|
|
276
|
-
|
|
309
|
+
if (response.status === 200) {
|
|
310
|
+
this.doesVHostExist = true;
|
|
311
|
+
logger.info('Vhost exists', { vhost: this.vhost });
|
|
312
|
+
return;
|
|
277
313
|
}
|
|
278
314
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
this.isVHostExist = true;
|
|
294
|
-
logger.info('Vhost created', { vhost: this.options?.vhost });
|
|
295
|
-
return;
|
|
296
|
-
} catch (createError) {
|
|
297
|
-
logger.error('Failed to create vhost', { error: createError });
|
|
298
|
-
throw createError;
|
|
299
|
-
}
|
|
315
|
+
if (response.status !== 404) {
|
|
316
|
+
logger.error('Failed to check vhost', { response });
|
|
317
|
+
throw new RabbitError('Failed to check vhost');
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
const createResponse = await fetch(url, {
|
|
321
|
+
method: 'PUT',
|
|
322
|
+
headers,
|
|
323
|
+
body: JSON.stringify({ default_queue_type: 'quorum' }),
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
if (!createResponse.ok) {
|
|
327
|
+
logger.error('Failed to create vhost', { response: createResponse });
|
|
328
|
+
throw new RabbitError('Failed to create vhost');
|
|
300
329
|
}
|
|
301
330
|
|
|
302
|
-
|
|
331
|
+
this.doesVHostExist = true;
|
|
332
|
+
logger.info('Vhost created', { vhost: this.vhost });
|
|
333
|
+
} catch (error) {
|
|
334
|
+
logger.error('Failed to check or create vhost', { error });
|
|
303
335
|
throw error;
|
|
304
336
|
}
|
|
305
|
-
}
|
|
337
|
+
};
|
|
306
338
|
|
|
307
339
|
private shouldConsumeMessageByTimestamp = async (msg: ConsumeMessageOrNull) => {
|
|
308
340
|
if (msg) {
|
|
@@ -351,13 +383,13 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
351
383
|
if (
|
|
352
384
|
!skipRetry
|
|
353
385
|
&& (
|
|
354
|
-
!msg.properties.headers[RETRY_HEADER]
|
|
386
|
+
!msg.properties.headers?.[RETRY_HEADER]
|
|
355
387
|
|| parseInt(msg.properties.headers[RETRY_HEADER], 10) < options.retries
|
|
356
388
|
)
|
|
357
389
|
) {
|
|
358
390
|
await this.sendToQueue(queue, RabbitMq.parseMsg(msg).content, options, {
|
|
359
391
|
...msg.properties.headers,
|
|
360
|
-
[RETRY_HEADER]: msg.properties.headers[RETRY_HEADER]
|
|
392
|
+
[RETRY_HEADER]: msg.properties.headers?.[RETRY_HEADER]
|
|
361
393
|
? msg.properties.headers[RETRY_HEADER] + 1
|
|
362
394
|
: 1,
|
|
363
395
|
});
|
|
@@ -365,7 +397,7 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
365
397
|
const deadQueue = `${queue}-dead`;
|
|
366
398
|
await this.sendToQueue(deadQueue, RabbitMq.parseMsg(msg).content, deadQueueOptions, {
|
|
367
399
|
...msg.properties.headers,
|
|
368
|
-
[RETRY_HEADER]: msg.properties.headers[RETRY_HEADER]
|
|
400
|
+
[RETRY_HEADER]: msg.properties.headers?.[RETRY_HEADER]
|
|
369
401
|
? msg.properties.headers[RETRY_HEADER] + 1
|
|
370
402
|
: 1,
|
|
371
403
|
});
|
|
@@ -379,30 +411,38 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
379
411
|
}
|
|
380
412
|
}
|
|
381
413
|
|
|
382
|
-
async getConnection() {
|
|
414
|
+
async getConnection(connection: ConnectionData) {
|
|
383
415
|
return new Promise<AmqpConnectionManager>(async (resolve, reject) => {
|
|
384
|
-
|
|
416
|
+
const {
|
|
417
|
+
amqpConnection: connectionLocal,
|
|
418
|
+
creatingConnection,
|
|
419
|
+
connectionCreatedEventName,
|
|
420
|
+
connectionFailedEventName,
|
|
421
|
+
blockReconnect,
|
|
422
|
+
} = connection;
|
|
423
|
+
|
|
424
|
+
if (blockReconnect) {
|
|
385
425
|
debug('rabbit: block reconnect');
|
|
386
426
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
387
427
|
// @ts-ignore
|
|
388
428
|
return resolve();
|
|
389
429
|
}
|
|
390
|
-
if (
|
|
391
|
-
if (this.options?.disableReconnect ||
|
|
430
|
+
if (connectionLocal !== null) {
|
|
431
|
+
if (this.options?.disableReconnect || connectionLocal?.isConnected()) {
|
|
392
432
|
debug('rabbit: connection - is connected');
|
|
393
433
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
394
434
|
// @ts-ignore
|
|
395
|
-
return resolve(
|
|
435
|
+
return resolve(connectionLocal);
|
|
396
436
|
}
|
|
397
437
|
debug('rabbit: connection - reconnecting');
|
|
398
438
|
}
|
|
399
|
-
if (
|
|
439
|
+
if (creatingConnection) {
|
|
400
440
|
debug('rabbit: creating connection emi');
|
|
401
|
-
this.em.once(
|
|
402
|
-
this.em.once(
|
|
441
|
+
this.em.once(connectionCreatedEventName, resolve);
|
|
442
|
+
this.em.once(connectionFailedEventName, reject);
|
|
403
443
|
return;
|
|
404
444
|
}
|
|
405
|
-
|
|
445
|
+
connection.creatingConnection = true;
|
|
406
446
|
let isResolved = false;
|
|
407
447
|
|
|
408
448
|
// It is import to use it as a function and not as a variable
|
|
@@ -414,65 +454,72 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
414
454
|
const host = this.options?.rabbitHost || process.env.RABBITMQ_SERVICE_HOST || 'localhost';
|
|
415
455
|
|
|
416
456
|
debug('rabbit: creating connection', { host, userName, HEARTBEAT });
|
|
417
|
-
return [`amqp://${userName}:${password}@${host}/${this.
|
|
457
|
+
return [`amqp://${userName}:${password}@${host}/${this.vhost}?heartbeat=${HEARTBEAT}`];
|
|
418
458
|
};
|
|
419
459
|
|
|
420
460
|
const defaultUrls = findServers();
|
|
421
|
-
const
|
|
461
|
+
const newConnection: AmqpConnectionManager = await connect(defaultUrls, {
|
|
422
462
|
findServers,
|
|
423
463
|
});
|
|
424
464
|
|
|
425
|
-
|
|
426
|
-
|
|
465
|
+
connection.amqpConnection = newConnection;
|
|
466
|
+
newConnection.on('error', (err) => {
|
|
427
467
|
logger.error('rabbit: connection error', { err });
|
|
428
468
|
if (!isResolved) {
|
|
429
469
|
isResolved = true;
|
|
430
470
|
reject(err);
|
|
431
|
-
this.em.emit(
|
|
471
|
+
this.em.emit(connectionFailedEventName, err);
|
|
432
472
|
}
|
|
433
473
|
});
|
|
434
474
|
|
|
435
|
-
|
|
475
|
+
newConnection.on('connectFailed', (err) => {
|
|
436
476
|
this.consumersTags = [];
|
|
437
|
-
|
|
477
|
+
if (typeof err.url === 'string') {
|
|
478
|
+
err.url = this.maskURL(err.url);
|
|
479
|
+
}
|
|
480
|
+
logger.error('rabbit: connection connectFailed', { err, advice: 'Check if the vhost exist', vhost: this.vhost });
|
|
438
481
|
if (!isResolved) {
|
|
439
482
|
isResolved = true;
|
|
440
483
|
reject(err);
|
|
441
|
-
this.em.emit(
|
|
484
|
+
this.em.emit(connectionFailedEventName, err);
|
|
442
485
|
}
|
|
443
486
|
});
|
|
444
487
|
|
|
445
|
-
|
|
446
|
-
// this.channel = null;
|
|
488
|
+
newConnection.on('disconnect', ({ err }) => {
|
|
447
489
|
this.consumersTags = [];
|
|
448
490
|
debug('rabbit: connection closed');
|
|
449
491
|
if (this.options?.disableReconnect) {
|
|
450
492
|
logger.error(`${this.DISCONNECT_MSG}${err && ` - ${err}`}`);
|
|
451
|
-
|
|
493
|
+
connection.blockReconnect = true;
|
|
452
494
|
} else {
|
|
453
495
|
logger.error(`${this.RECONNECT_MSG}${err && ` - ${err}`}`);
|
|
454
496
|
}
|
|
455
497
|
});
|
|
456
498
|
|
|
457
|
-
|
|
499
|
+
newConnection.once('connect', async () => {
|
|
458
500
|
debug('rabbit: connection established');
|
|
459
|
-
|
|
460
|
-
this.em.emit(
|
|
501
|
+
connection.creatingConnection = false;
|
|
502
|
+
this.em.emit(connectionCreatedEventName, newConnection);
|
|
461
503
|
isResolved = true;
|
|
462
|
-
resolve(
|
|
504
|
+
resolve(newConnection);
|
|
463
505
|
});
|
|
464
506
|
});
|
|
465
507
|
}
|
|
466
508
|
|
|
467
|
-
async getNewChannel({
|
|
468
|
-
|
|
509
|
+
async getNewChannel({
|
|
510
|
+
name = rand().toString(),
|
|
511
|
+
onClose = null,
|
|
512
|
+
options = {},
|
|
513
|
+
connection,
|
|
514
|
+
}: newChannelOpts) {
|
|
515
|
+
let localConnection!: AmqpConnectionManager;
|
|
469
516
|
try {
|
|
470
|
-
|
|
517
|
+
localConnection = await this.getConnection(connection);
|
|
471
518
|
} catch (e) {
|
|
472
519
|
logger.error(`rabbit: error on get connection for new channel ${name} `, { e });
|
|
473
520
|
throw e;
|
|
474
521
|
}
|
|
475
|
-
const channel =
|
|
522
|
+
const channel = localConnection?.createChannel({ ...options });
|
|
476
523
|
once(channel, 'close').then((args) => {
|
|
477
524
|
logger.error(`rabbit: channel ${name} closed`);
|
|
478
525
|
onClose?.(args);
|
|
@@ -487,19 +534,21 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
487
534
|
}
|
|
488
535
|
}
|
|
489
536
|
|
|
490
|
-
async assertChannel({ force = false }
|
|
537
|
+
async assertChannel({ force = false, connection }: assertChannelOpts): Promise<ChannelWrapper> {
|
|
491
538
|
if (!this.publishChannelSetupPromise) {
|
|
492
539
|
this.publishChannelSetupPromise = new Promise<ChannelWrapper>(async (resolve, reject) => {
|
|
493
|
-
if (this.
|
|
494
|
-
return resolve(this.
|
|
540
|
+
if (this.publishChannel && !force) {
|
|
541
|
+
return resolve(this.publishChannel);
|
|
495
542
|
}
|
|
496
543
|
|
|
497
544
|
try {
|
|
498
|
-
const channel = await this.getNewChannel({});
|
|
545
|
+
const channel = await this.getNewChannel({ connection });
|
|
499
546
|
channel.on('error', (err) => {
|
|
500
547
|
logger.error('rabbit: channel error', { err });
|
|
501
548
|
});
|
|
502
|
-
this.
|
|
549
|
+
if (this.publishConnection === connection) {
|
|
550
|
+
this.publishChannel = channel;
|
|
551
|
+
}
|
|
503
552
|
resolve(channel);
|
|
504
553
|
} catch (e) {
|
|
505
554
|
reject(e);
|
|
@@ -509,8 +558,8 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
509
558
|
return this.publishChannelSetupPromise;
|
|
510
559
|
}
|
|
511
560
|
|
|
512
|
-
async assertExchange(exchangeName: string,
|
|
513
|
-
const channel: ChannelWrapper = await this.assertChannel();
|
|
561
|
+
async assertExchange(exchangeName: string, connection: ConnectionData) {
|
|
562
|
+
const channel: ChannelWrapper = await this.assertChannel({ connection });
|
|
514
563
|
|
|
515
564
|
if (this.exchanges[exchangeName]) {
|
|
516
565
|
delete this.assertExchangePromises[exchangeName];
|
|
@@ -526,35 +575,34 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
526
575
|
return this.exchanges[exchangeName];
|
|
527
576
|
}
|
|
528
577
|
|
|
529
|
-
// TODO: Change the implementation to the new one
|
|
530
578
|
async getQueueLength(queue: string) {
|
|
531
579
|
RabbitMq.validateName('queue', queue);
|
|
532
|
-
const {
|
|
580
|
+
const { publishChannel: channel } = this;
|
|
533
581
|
if (!channel) {
|
|
534
|
-
throw new
|
|
582
|
+
throw new RabbitError('channel is not defined');
|
|
535
583
|
}
|
|
536
|
-
debug('rabbit: getting queue length', { queue, connected: this.
|
|
584
|
+
debug('rabbit: getting queue length', { queue, connected: this.publishConnection.amqpConnection?.isConnected() });
|
|
537
585
|
return channel?.checkQueue(queue);
|
|
538
586
|
}
|
|
539
587
|
|
|
540
|
-
private async deleteQueue(queue: string) {
|
|
588
|
+
private async deleteQueue(queue: string, connection: ConnectionData) {
|
|
541
589
|
RabbitMq.validateName('queue', queue);
|
|
542
|
-
const channel: ChannelWrapper = await this.assertChannel();
|
|
590
|
+
const channel: ChannelWrapper = await this.assertChannel({ connection });
|
|
543
591
|
logger.info('rabbit: deleting queue', { queue });
|
|
544
592
|
const deleteQueueRes = await channel.deleteQueue(queue);
|
|
545
593
|
debug('queue deleted', deleteQueueRes);
|
|
546
594
|
return deleteQueueRes;
|
|
547
595
|
}
|
|
548
596
|
|
|
549
|
-
// TODO: Change the implementation to the new one
|
|
550
597
|
async bindQueue(queue: string, exchange: string) {
|
|
551
|
-
const channel: ChannelWrapper = await this.
|
|
598
|
+
const channel: ChannelWrapper = await this.assertChannel({ connection: this.publishConnection });
|
|
552
599
|
await channel.addSetup((setupChannel: ConfirmChannel) => setupChannel.bindQueue(queue, exchange, ''));
|
|
553
600
|
return channel.bindQueue(queue, exchange, '');
|
|
554
601
|
}
|
|
555
602
|
|
|
556
603
|
async setupQueue(queueName: string, options?: Options.AssertQueue): Promise<Replies.AssertQueue> {
|
|
557
604
|
let queue: Replies.AssertQueue;
|
|
605
|
+
const connection = this.publishConnection;
|
|
558
606
|
const localeOptions = {
|
|
559
607
|
...options,
|
|
560
608
|
durable: true,
|
|
@@ -565,7 +613,7 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
565
613
|
},
|
|
566
614
|
};
|
|
567
615
|
try {
|
|
568
|
-
const channel: ChannelWrapper = await this.assertChannel();
|
|
616
|
+
const channel: ChannelWrapper = await this.assertChannel({ connection });
|
|
569
617
|
debug('assertQueue->channel.addSetup', { queueName });
|
|
570
618
|
await channel.addSetup(async (setupChannel: ConfirmChannel) => {
|
|
571
619
|
await setupChannel.assertQueue(queueName, localeOptions);
|
|
@@ -576,8 +624,8 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
576
624
|
logger.error('rabbit: assertQueue error', { queueName, options, error: e });
|
|
577
625
|
if (!this.options?.dontRetryAssert) {
|
|
578
626
|
debug('retrying assertQueue', { queueName });
|
|
579
|
-
const channel = await this.assertChannel({ force: true });
|
|
580
|
-
await this.deleteQueue(queueName);
|
|
627
|
+
const channel = await this.assertChannel({ force: true, connection });
|
|
628
|
+
await this.deleteQueue(queueName, connection);
|
|
581
629
|
|
|
582
630
|
debug('retrying assertQueue->channel.addSetup', { queueName });
|
|
583
631
|
await channel.addSetup((setupChannel: ConfirmChannel) => setupChannel.assertQueue(queueName, localeOptions));
|
|
@@ -592,7 +640,7 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
592
640
|
return queue;
|
|
593
641
|
}
|
|
594
642
|
|
|
595
|
-
// TODO: Can be deleted after deleting the old consumers
|
|
643
|
+
// TODO: [QUORUM-PHASE-3] Can be deleted after deleting the old consumers and publishers because all the queues are created us quorum queues
|
|
596
644
|
static shouldUseQuorum(queueName: string): boolean {
|
|
597
645
|
const envQuorumQueuesWhitelist = process.env.QUORUM_QUEUES_WHITELIST;
|
|
598
646
|
|
|
@@ -637,8 +685,8 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
637
685
|
|
|
638
686
|
// Used by the microservices to consume messages from the queue
|
|
639
687
|
async consume(queue: string, callback: CallbackFunction, options?: ConsumeOptions): Promise<any> {
|
|
640
|
-
// TODO:
|
|
641
|
-
if (options?.isQuorumQueue !== false) {
|
|
688
|
+
// TODO: [QUORUM-PHASE-3] Use only the implementation of consumeNew and delete consumeNew and consumeOld
|
|
689
|
+
if (options?.isQuorumQueue !== false && DISABLE_QUORUM_QUEUES_CONSUME !== 'true') {
|
|
642
690
|
await this.assertVHost();
|
|
643
691
|
await this.consumeNew(queue, callback, options);
|
|
644
692
|
}
|
|
@@ -646,12 +694,12 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
646
694
|
await this.consumeOld(queue, callback, options);
|
|
647
695
|
}
|
|
648
696
|
|
|
649
|
-
// TODO:
|
|
697
|
+
// TODO: [QUORUM-PHASE-3] Delete consumeNew we do not use it anymore
|
|
650
698
|
async consumeNew(queue: string, callback: CallbackFunction, options?: ConsumeOptions): Promise<any> {
|
|
651
699
|
await this.consumeFromRabbit(queue, callback, options);
|
|
652
700
|
}
|
|
653
701
|
|
|
654
|
-
// TODO:
|
|
702
|
+
// TODO: [QUORUM-PHASE-3] Delete consumeOld we do not use it anymore
|
|
655
703
|
async consumeOld(queue: string, callback: CallbackFunction, options?: ConsumeOptions): Promise<any> {
|
|
656
704
|
await this.consumeFromRabbitOld(queue, callback, options);
|
|
657
705
|
}
|
|
@@ -686,11 +734,11 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
686
734
|
} = optionsWithDefaults;
|
|
687
735
|
if (useConsumeWithLock) {
|
|
688
736
|
if (!this.redisLock) {
|
|
689
|
-
throw new
|
|
737
|
+
throw new RabbitError('Usage of consumeWithLock requires RedisInstance');
|
|
690
738
|
}
|
|
691
739
|
logger.info(`rabbit: Consuming with lock from queue ${queue} with lockTimeout: ${lockTimeout}ms`);
|
|
692
740
|
}
|
|
693
|
-
const channel = await this.getNewChannel({});
|
|
741
|
+
const channel = await this.getNewChannel({ connection: this.consumeConnection });
|
|
694
742
|
return channel.addSetup(async (confirmChannel: ConfirmChannel) => {
|
|
695
743
|
const q = await this.assertQueue(queue, optionsWithDefaults);
|
|
696
744
|
await confirmChannel.prefetch(limit, false);
|
|
@@ -701,9 +749,9 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
701
749
|
return null;
|
|
702
750
|
}
|
|
703
751
|
|
|
704
|
-
const traceId = msg.properties.headers[TRACING_HEADER];
|
|
705
|
-
const userId = msg.properties.headers[USER_TRACING_HEADER];
|
|
706
|
-
const automationId = msg.properties.headers[AUTOMATION_ID_HEADER];
|
|
752
|
+
const traceId = msg.properties.headers![TRACING_HEADER];
|
|
753
|
+
const userId = msg.properties.headers![USER_TRACING_HEADER];
|
|
754
|
+
const automationId = msg.properties.headers![AUTOMATION_ID_HEADER];
|
|
707
755
|
const parsedMessage = RabbitMq.parseMsg(msg);
|
|
708
756
|
const releaseLock = await this.lockRedisIfNeeded(parsedMessage, optionsWithDefaults);
|
|
709
757
|
const trace = newTrace(traceTypes.RABBIT);
|
|
@@ -786,10 +834,14 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
786
834
|
RabbitMq.validateName('exchange', exchange);
|
|
787
835
|
RabbitMq.validateName('queue', queue);
|
|
788
836
|
const { limit, deadMessageTtl } = optionsWithDefaults;
|
|
789
|
-
if
|
|
837
|
+
// TODO: [QUORUM-PHASE-3] Delete the if statement after all the queues are created as quorum queues
|
|
838
|
+
if (options?.isQuorumQueue !== false && DISABLE_QUORUM_QUEUES_CONSUME !== 'true') {
|
|
790
839
|
await this.assertVHost();
|
|
791
840
|
await this.saveConsumer(queue, callback, options);
|
|
792
|
-
const channel: ChannelWrapper = await this.getNewChannel({
|
|
841
|
+
const channel: ChannelWrapper = await this.getNewChannel({
|
|
842
|
+
name: `consume-exchange-${exchange}-queue-${queue}`,
|
|
843
|
+
connection: this.consumeConnection,
|
|
844
|
+
});
|
|
793
845
|
|
|
794
846
|
await channel.addSetup(async (c: ConfirmChannel) => {
|
|
795
847
|
const assertExchange = await assertExchangeFanout(c, exchange);
|
|
@@ -806,10 +858,12 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
806
858
|
]);
|
|
807
859
|
});
|
|
808
860
|
}
|
|
809
|
-
|
|
810
|
-
// TODO: DELETE OLD
|
|
861
|
+
// TODO: [QUORUM-PHASE-3] Delete the old implementation
|
|
811
862
|
await this.saveConsumerOld(queue, callback, options);
|
|
812
|
-
const channelOld: ChannelWrapper = await this.getNewChannelOld({
|
|
863
|
+
const channelOld: ChannelWrapper = await this.getNewChannelOld({
|
|
864
|
+
name: `consume-exchange-${exchange}-queue-${queue}-old`,
|
|
865
|
+
connection: this.oldConsumeConnection,
|
|
866
|
+
});
|
|
813
867
|
await channelOld.addSetup(async (c: ConfirmChannel) => {
|
|
814
868
|
const assertExchange = await assertExchangeFanout(c, exchange);
|
|
815
869
|
await c.assertQueue(queue);
|
|
@@ -827,12 +881,25 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
827
881
|
}
|
|
828
882
|
|
|
829
883
|
// Used by the microservices to publish messages to the exchange
|
|
830
|
-
// TODO:
|
|
831
|
-
async publish(exchange: string, content: any, customHeaders?: any) : Promise<boolean> {
|
|
884
|
+
// TODO: [QUORUM-PHASE-3] isQuorumQueue flag is not needed anymore because all the queues are created as quorum queues
|
|
885
|
+
async publish(exchange: string, content: any, customHeaders?: any, isQuorumQueue = true) : Promise<boolean> {
|
|
886
|
+
if (isQuorumQueue && DISABLE_QUORUM_QUEUES_PUBLISH !== 'true') {
|
|
887
|
+
return wrapSetImmediate(async () => {
|
|
888
|
+
await this.assertVHost();
|
|
889
|
+
RabbitMq.validateName('exchange', exchange);
|
|
890
|
+
const channel: ChannelWrapper = await this.assertChannel({ connection: this.publishConnection });
|
|
891
|
+
await this.assertExchange(exchange, this.publishConnection);
|
|
892
|
+
await channel.publish(exchange, '',
|
|
893
|
+
Buffer.from(JSON.stringify(content)),
|
|
894
|
+
RabbitMq.getPublishOptions(customHeaders));
|
|
895
|
+
});
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
// TODO: [QUORUM-PHASE-3] Delete the old implementation
|
|
832
899
|
return wrapSetImmediate(async () => {
|
|
833
900
|
RabbitMq.validateName('exchange', exchange);
|
|
834
|
-
const channel: ChannelWrapper = await this.assertChannelOld();
|
|
835
|
-
await this.assertExchangeOld(exchange);
|
|
901
|
+
const channel: ChannelWrapper = await this.assertChannelOld({ connection: this.oldPublishConnection });
|
|
902
|
+
await this.assertExchangeOld(exchange, this.oldPublishConnection);
|
|
836
903
|
await channel.publish(exchange, '',
|
|
837
904
|
Buffer.from(JSON.stringify(content)),
|
|
838
905
|
RabbitMq.getPublishOptions(customHeaders));
|
|
@@ -840,65 +907,97 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
840
907
|
}
|
|
841
908
|
|
|
842
909
|
// Used by the microservices to send messages to the queue
|
|
843
|
-
// TODO:
|
|
910
|
+
// TODO: [QUORUM-PHASE-3] isQuorumQueue flag is not needed anymore because all the queues are created as quorum queues
|
|
844
911
|
async sendToQueue(
|
|
845
912
|
queue: string,
|
|
846
913
|
content: any,
|
|
847
914
|
options?: any,
|
|
848
915
|
customHeaders?: any,
|
|
916
|
+
isQuorumQueue = true,
|
|
849
917
|
): Promise<boolean | undefined> {
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
918
|
+
if (isQuorumQueue && DISABLE_QUORUM_QUEUES_PUBLISH !== 'true') {
|
|
919
|
+
try {
|
|
920
|
+
await this.assertVHost();
|
|
921
|
+
await this.assertChannel({ connection: this.publishConnection });
|
|
922
|
+
} catch (e) {
|
|
923
|
+
logger.error(`rabbit sendToQueue: failed to send assert channel when sending to queue ${queue}`, { e });
|
|
924
|
+
throw e;
|
|
925
|
+
}
|
|
856
926
|
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
927
|
+
try {
|
|
928
|
+
RabbitMq.validateName('queue', queue);
|
|
929
|
+
await this.assertQueue(queue, options);
|
|
930
|
+
} catch (e) {
|
|
931
|
+
logger.error(`rabbit sendToQueue: failed to assert queue ${queue}`, { e });
|
|
932
|
+
throw e;
|
|
933
|
+
}
|
|
864
934
|
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
935
|
+
try {
|
|
936
|
+
const res = await this.publishChannel?.sendToQueue(queue,
|
|
937
|
+
Buffer.from(JSON.stringify(content)),
|
|
938
|
+
RabbitMq.getPublishOptions(customHeaders));
|
|
939
|
+
debug(`rabbit: sending to queue ${queue}`, { res });
|
|
940
|
+
return res;
|
|
941
|
+
} catch (e) {
|
|
942
|
+
const isConnected = await this.isConnected();
|
|
943
|
+
logger.error(`rabbit sendToQueue: failed to send to queue ${queue}, isConnected: ${isConnected}`, { e });
|
|
944
|
+
throw e;
|
|
945
|
+
}
|
|
946
|
+
} else {
|
|
947
|
+
try {
|
|
948
|
+
await this.assertChannelOld({ connection: this.oldPublishConnection });
|
|
949
|
+
} catch (e) {
|
|
950
|
+
logger.error(`rabbit sendToQueue: failed to send assert channel when sending to queue ${queue}`, { e });
|
|
951
|
+
throw e;
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
try {
|
|
955
|
+
RabbitMq.validateName('queue', queue);
|
|
956
|
+
await this.assertQueueOld(queue, options);
|
|
957
|
+
} catch (e) {
|
|
958
|
+
logger.error(`rabbit sendToQueue: failed to assert queue ${queue}`, { e });
|
|
959
|
+
throw e;
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
try {
|
|
963
|
+
const res = await this.oldPublishChannel?.sendToQueue(queue,
|
|
964
|
+
Buffer.from(JSON.stringify(content)),
|
|
965
|
+
RabbitMq.getPublishOptions(customHeaders));
|
|
966
|
+
debug(`rabbit: sending to queue ${queue}`, { res });
|
|
967
|
+
return res;
|
|
968
|
+
} catch (e) {
|
|
969
|
+
const isConnected = await this.isConnectedOld();
|
|
970
|
+
logger.error(`rabbit sendToQueue: failed to send to queue ${queue}, isConnected: ${isConnected}`, { e });
|
|
971
|
+
throw e;
|
|
972
|
+
}
|
|
875
973
|
}
|
|
876
974
|
}
|
|
877
975
|
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
const
|
|
881
|
-
const
|
|
976
|
+
async isConnected(): Promise<boolean> {
|
|
977
|
+
debug('rabbit: start old is connected');
|
|
978
|
+
const consumeConnection = await this.getConnection(this.consumeConnection);
|
|
979
|
+
const publishConnection = await this.getConnection(this.publishConnection);
|
|
980
|
+
const isConnected = consumeConnection.isConnected() && publishConnection.isConnected();
|
|
882
981
|
if (!isConnected) {
|
|
883
|
-
logger.error('rabbit: isConnected - false');
|
|
982
|
+
logger.error('rabbit: old isConnected - false');
|
|
884
983
|
return false;
|
|
885
984
|
}
|
|
886
|
-
const channel: any = await this.
|
|
985
|
+
const channel: any = await this.assertChannel({ connection: this.publishConnection });
|
|
887
986
|
try {
|
|
888
987
|
await Promise.all([
|
|
889
988
|
channel.waitForConnect(),
|
|
890
|
-
...this.
|
|
989
|
+
...this.consumers.map((c: AfConsumer) => channel.checkQueue(c.queue)),
|
|
891
990
|
]);
|
|
892
991
|
} catch (e) {
|
|
893
992
|
logger.error('rabbit: isConnected - false');
|
|
894
993
|
return false;
|
|
895
994
|
}
|
|
896
|
-
logger.
|
|
995
|
+
logger.debug('rabbit: isConnected - true');
|
|
897
996
|
return true;
|
|
898
997
|
}
|
|
899
998
|
|
|
900
999
|
async gracefulShutdown(signal: string) : Promise<void> {
|
|
901
|
-
// TODO:
|
|
1000
|
+
// TODO: [QUORUM-PHASE-3] Delete the old implementation they are not needed anymore
|
|
902
1001
|
const tagsNumber = this.consumersTags.length + this.oldConsumersTags.length;
|
|
903
1002
|
logger.info(`rabbit: [gracefully-shutdown] received ${signal}! canceling #${tagsNumber} tags...`);
|
|
904
1003
|
const cancelTagPromises = this.consumersTags.map(([channel, tag]) => channel.cancel(tag));
|
|
@@ -925,11 +1024,11 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
925
1024
|
} = optionsWithDefaults;
|
|
926
1025
|
if (useConsumeWithLock) {
|
|
927
1026
|
if (!this.redisLock) {
|
|
928
|
-
throw new
|
|
1027
|
+
throw new RabbitError('Usage of consumeWithLock requires RedisInstance');
|
|
929
1028
|
}
|
|
930
1029
|
logger.info(`rabbit: Consuming with lock from queue ${queue} with lockTimeout: ${lockTimeout}ms`);
|
|
931
1030
|
}
|
|
932
|
-
const channel = await this.getNewChannelOld({});
|
|
1031
|
+
const channel = await this.getNewChannelOld({ connection: this.oldConsumeConnection });
|
|
933
1032
|
return channel.addSetup(async (confirmChannel: ConfirmChannel) => {
|
|
934
1033
|
const q = await this.assertQueueOld(queue, optionsWithDefaults);
|
|
935
1034
|
await confirmChannel.prefetch(limit, false);
|
|
@@ -940,9 +1039,9 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
940
1039
|
return null;
|
|
941
1040
|
}
|
|
942
1041
|
|
|
943
|
-
const traceId = msg.properties.headers[TRACING_HEADER];
|
|
944
|
-
const userId = msg.properties.headers[USER_TRACING_HEADER];
|
|
945
|
-
const automationId = msg.properties.headers[AUTOMATION_ID_HEADER];
|
|
1042
|
+
const traceId = msg.properties.headers![TRACING_HEADER];
|
|
1043
|
+
const userId = msg.properties.headers![USER_TRACING_HEADER];
|
|
1044
|
+
const automationId = msg.properties.headers![AUTOMATION_ID_HEADER];
|
|
946
1045
|
const parsedMessage = RabbitMq.parseMsg(msg);
|
|
947
1046
|
const releaseLock = await this.lockRedisIfNeeded(parsedMessage, optionsWithDefaults);
|
|
948
1047
|
const trace = newTrace(traceTypes.RABBIT);
|
|
@@ -1019,16 +1118,26 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
1019
1118
|
});
|
|
1020
1119
|
}
|
|
1021
1120
|
|
|
1022
|
-
// TODO:
|
|
1023
|
-
async getNewChannelOld({
|
|
1024
|
-
|
|
1121
|
+
// TODO: [QUORUM-PHASE-3] Delete all the function under this line.
|
|
1122
|
+
async getNewChannelOld({
|
|
1123
|
+
name = rand().toString(),
|
|
1124
|
+
onClose = null,
|
|
1125
|
+
options = {},
|
|
1126
|
+
connection,
|
|
1127
|
+
}: newChannelOpts) {
|
|
1128
|
+
let localConnection!: AmqpConnectionManager;
|
|
1025
1129
|
try {
|
|
1026
|
-
|
|
1130
|
+
localConnection = await this.getConnectionOld(connection);
|
|
1027
1131
|
} catch (e) {
|
|
1028
1132
|
logger.error(`rabbit: error on get connection for new channel ${name} `, { e });
|
|
1029
1133
|
throw e;
|
|
1030
1134
|
}
|
|
1031
|
-
|
|
1135
|
+
|
|
1136
|
+
if (!localConnection) {
|
|
1137
|
+
throw new Error(`rabbit: couldnt get connection for new channel ${name}`);
|
|
1138
|
+
}
|
|
1139
|
+
|
|
1140
|
+
const channel = localConnection?.createChannel({ ...options });
|
|
1032
1141
|
once(channel, 'close').then((args) => {
|
|
1033
1142
|
logger.error(`rabbit: channel ${name} closed`);
|
|
1034
1143
|
onClose?.(args);
|
|
@@ -1043,30 +1152,38 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
1043
1152
|
}
|
|
1044
1153
|
}
|
|
1045
1154
|
|
|
1046
|
-
async getConnectionOld() {
|
|
1155
|
+
async getConnectionOld(connection: ConnectionData) {
|
|
1047
1156
|
return new Promise<AmqpConnectionManager>(async (resolve, reject) => {
|
|
1048
|
-
|
|
1157
|
+
const {
|
|
1158
|
+
amqpConnection: connectionLocal,
|
|
1159
|
+
creatingConnection,
|
|
1160
|
+
connectionCreatedEventName,
|
|
1161
|
+
connectionFailedEventName,
|
|
1162
|
+
blockReconnect,
|
|
1163
|
+
} = connection;
|
|
1164
|
+
|
|
1165
|
+
if (blockReconnect) {
|
|
1049
1166
|
debug('rabbit: block reconnect');
|
|
1050
1167
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
1051
1168
|
// @ts-ignore
|
|
1052
1169
|
return resolve();
|
|
1053
1170
|
}
|
|
1054
|
-
if (
|
|
1055
|
-
if (this.options?.disableReconnect ||
|
|
1171
|
+
if (connectionLocal !== null) {
|
|
1172
|
+
if (this.options?.disableReconnect || connectionLocal?.isConnected()) {
|
|
1056
1173
|
debug('rabbit: connection - is connected');
|
|
1057
1174
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
1058
1175
|
// @ts-ignore
|
|
1059
|
-
return resolve(
|
|
1176
|
+
return resolve(connectionLocal);
|
|
1060
1177
|
}
|
|
1061
1178
|
debug('rabbit: connection - reconnecting');
|
|
1062
1179
|
}
|
|
1063
|
-
if (
|
|
1180
|
+
if (creatingConnection) {
|
|
1064
1181
|
debug('rabbit: creating connection emi');
|
|
1065
|
-
this.oldEm.once(
|
|
1066
|
-
this.oldEm.once(
|
|
1182
|
+
this.oldEm.once(connectionCreatedEventName, resolve);
|
|
1183
|
+
this.oldEm.once(connectionFailedEventName, reject);
|
|
1067
1184
|
return;
|
|
1068
1185
|
}
|
|
1069
|
-
|
|
1186
|
+
connection.creatingConnection = true;
|
|
1070
1187
|
let isResolved = false;
|
|
1071
1188
|
|
|
1072
1189
|
// It is import to use it as a function and not as a variable
|
|
@@ -1083,47 +1200,50 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
1083
1200
|
};
|
|
1084
1201
|
|
|
1085
1202
|
const defaultUrls = findServers();
|
|
1086
|
-
const
|
|
1203
|
+
const newConnection: AmqpConnectionManager = await connect(defaultUrls, {
|
|
1087
1204
|
findServers,
|
|
1088
1205
|
});
|
|
1089
1206
|
|
|
1090
|
-
|
|
1091
|
-
|
|
1207
|
+
connection.amqpConnection = newConnection;
|
|
1208
|
+
newConnection.on('error', (err) => {
|
|
1092
1209
|
logger.error('rabbit: connection error', { err });
|
|
1093
1210
|
if (!isResolved) {
|
|
1094
1211
|
isResolved = true;
|
|
1095
1212
|
reject(err);
|
|
1096
|
-
this.oldEm.emit(
|
|
1213
|
+
this.oldEm.emit(connectionFailedEventName, err);
|
|
1097
1214
|
}
|
|
1098
1215
|
});
|
|
1099
1216
|
|
|
1100
|
-
|
|
1217
|
+
newConnection.on('connectFailed', (err) => {
|
|
1101
1218
|
this.oldConsumersTags = [];
|
|
1219
|
+
if (typeof err.url === 'string') {
|
|
1220
|
+
err.url = this.maskURL(err.url);
|
|
1221
|
+
}
|
|
1102
1222
|
logger.error('rabbit: connection connectFailed', { err });
|
|
1103
1223
|
if (!isResolved) {
|
|
1104
1224
|
isResolved = true;
|
|
1105
1225
|
reject(err);
|
|
1106
|
-
this.oldEm.emit(
|
|
1226
|
+
this.oldEm.emit(connectionFailedEventName, err);
|
|
1107
1227
|
}
|
|
1108
1228
|
});
|
|
1109
1229
|
|
|
1110
|
-
|
|
1230
|
+
newConnection.on('disconnect', ({ err }) => {
|
|
1111
1231
|
this.oldConsumersTags = [];
|
|
1112
1232
|
debug('rabbit: connection closed');
|
|
1113
1233
|
if (this.options?.disableReconnect) {
|
|
1114
1234
|
logger.error(`${this.DISCONNECT_MSG}${err && ` - ${err}`}`);
|
|
1115
|
-
|
|
1235
|
+
connection.blockReconnect = true;
|
|
1116
1236
|
} else {
|
|
1117
1237
|
logger.error(`${this.RECONNECT_MSG}${err && ` - ${err}`}`);
|
|
1118
1238
|
}
|
|
1119
1239
|
});
|
|
1120
1240
|
|
|
1121
|
-
|
|
1241
|
+
newConnection.once('connect', async () => {
|
|
1122
1242
|
debug('rabbit: connection established');
|
|
1123
|
-
|
|
1124
|
-
this.oldEm.emit(
|
|
1243
|
+
connection.creatingConnection = false;
|
|
1244
|
+
this.oldEm.emit(connectionCreatedEventName, newConnection);
|
|
1125
1245
|
isResolved = true;
|
|
1126
|
-
resolve(
|
|
1246
|
+
resolve(newConnection);
|
|
1127
1247
|
});
|
|
1128
1248
|
});
|
|
1129
1249
|
}
|
|
@@ -1157,6 +1277,7 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
1157
1277
|
|
|
1158
1278
|
async setupQueueOld(queueName: string, options?: Options.AssertQueue): Promise<Replies.AssertQueue> {
|
|
1159
1279
|
let queue: Replies.AssertQueue;
|
|
1280
|
+
const connection = this.oldPublishConnection;
|
|
1160
1281
|
const shouldUseQuorum = RabbitMq.shouldUseQuorum(queueName);
|
|
1161
1282
|
const localeOptions = {
|
|
1162
1283
|
...options,
|
|
@@ -1168,7 +1289,7 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
1168
1289
|
},
|
|
1169
1290
|
};
|
|
1170
1291
|
try {
|
|
1171
|
-
const channel: ChannelWrapper = await this.assertChannelOld();
|
|
1292
|
+
const channel: ChannelWrapper = await this.assertChannelOld({ connection });
|
|
1172
1293
|
debug('assertQueue->channel.addSetup', { queueName });
|
|
1173
1294
|
await channel.addSetup((setupChannel: ConfirmChannel) => setupChannel.assertQueue(queueName, localeOptions));
|
|
1174
1295
|
debug('assertQueue->channel.assertQueue', { queueName });
|
|
@@ -1177,8 +1298,8 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
1177
1298
|
logger.error('rabbit: assertQueue error', { queueName, options, error: e });
|
|
1178
1299
|
if (!this.options?.dontRetryAssert) {
|
|
1179
1300
|
debug('retrying assertQueue', { queueName });
|
|
1180
|
-
const channel = await this.assertChannelOld({ force: true });
|
|
1181
|
-
await this.deleteQueueOld(queueName);
|
|
1301
|
+
const channel = await this.assertChannelOld({ force: true, connection });
|
|
1302
|
+
await this.deleteQueueOld(queueName, connection);
|
|
1182
1303
|
|
|
1183
1304
|
debug('retrying assertQueue->channel.addSetup', { queueName });
|
|
1184
1305
|
await channel.addSetup((setupChannel: ConfirmChannel) => setupChannel.assertQueue(queueName, localeOptions));
|
|
@@ -1193,19 +1314,21 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
1193
1314
|
return queue;
|
|
1194
1315
|
}
|
|
1195
1316
|
|
|
1196
|
-
async assertChannelOld({ force = false }
|
|
1317
|
+
async assertChannelOld({ force = false, connection }: assertChannelOpts): Promise<ChannelWrapper> {
|
|
1197
1318
|
if (!this.oldPublishChannelSetupPromise) {
|
|
1198
1319
|
this.oldPublishChannelSetupPromise = new Promise<ChannelWrapper>(async (resolve, reject) => {
|
|
1199
|
-
if (this.
|
|
1200
|
-
return resolve(this.
|
|
1320
|
+
if (this.oldPublishChannel && !force) {
|
|
1321
|
+
return resolve(this.oldPublishChannel);
|
|
1201
1322
|
}
|
|
1202
1323
|
|
|
1203
1324
|
try {
|
|
1204
|
-
const channel = await this.getNewChannelOld({});
|
|
1325
|
+
const channel = await this.getNewChannelOld({ connection });
|
|
1205
1326
|
channel.on('error', (err) => {
|
|
1206
1327
|
logger.error('rabbit: channel error', { err });
|
|
1207
1328
|
});
|
|
1208
|
-
this.
|
|
1329
|
+
if (this.oldPublishConnection === connection) {
|
|
1330
|
+
this.oldPublishChannel = channel;
|
|
1331
|
+
}
|
|
1209
1332
|
resolve(channel);
|
|
1210
1333
|
} catch (e) {
|
|
1211
1334
|
reject(e);
|
|
@@ -1215,17 +1338,17 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
1215
1338
|
return this.oldPublishChannelSetupPromise;
|
|
1216
1339
|
}
|
|
1217
1340
|
|
|
1218
|
-
private async deleteQueueOld(queue: string) {
|
|
1341
|
+
private async deleteQueueOld(queue: string, connection: ConnectionData) {
|
|
1219
1342
|
RabbitMq.validateName('queue', queue);
|
|
1220
|
-
const channel: ChannelWrapper = await this.assertChannelOld();
|
|
1343
|
+
const channel: ChannelWrapper = await this.assertChannelOld({ connection });
|
|
1221
1344
|
logger.info('rabbit: deleting queue', { queue });
|
|
1222
1345
|
const deleteQueueRes = await channel.deleteQueue(queue);
|
|
1223
1346
|
debug('queue deleted', deleteQueueRes);
|
|
1224
1347
|
return deleteQueueRes;
|
|
1225
1348
|
}
|
|
1226
1349
|
|
|
1227
|
-
async assertExchangeOld(exchangeName: string,
|
|
1228
|
-
const channel: ChannelWrapper = await this.assertChannelOld();
|
|
1350
|
+
async assertExchangeOld(exchangeName: string, connection: ConnectionData) {
|
|
1351
|
+
const channel: ChannelWrapper = await this.assertChannelOld({ connection });
|
|
1229
1352
|
|
|
1230
1353
|
if (this.oldExchanges[exchangeName]) {
|
|
1231
1354
|
delete this.oldAssertExchangePromises[exchangeName];
|
|
@@ -1240,6 +1363,40 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
1240
1363
|
this.oldExchanges[exchangeName] = await this.oldAssertExchangePromises[exchangeName];
|
|
1241
1364
|
return this.oldExchanges[exchangeName];
|
|
1242
1365
|
}
|
|
1366
|
+
|
|
1367
|
+
async isConnectedOld() : Promise<boolean> {
|
|
1368
|
+
debug('rabbit: start old is connected');
|
|
1369
|
+
const oldConsumeConnection = await this.getConnectionOld(this.oldConsumeConnection);
|
|
1370
|
+
const oldPublishConnection = await this.getConnectionOld(this.oldPublishConnection);
|
|
1371
|
+
const oldIsConnected = oldConsumeConnection.isConnected() && oldPublishConnection.isConnected();
|
|
1372
|
+
if (!oldIsConnected) {
|
|
1373
|
+
logger.error('rabbit: old isConnected - false');
|
|
1374
|
+
return false;
|
|
1375
|
+
}
|
|
1376
|
+
const oldChannel: any = await this.assertChannelOld({ connection: this.oldPublishConnection });
|
|
1377
|
+
try {
|
|
1378
|
+
await Promise.all([
|
|
1379
|
+
oldChannel.waitForConnect(),
|
|
1380
|
+
...this.oldConsumers.map((c: AfConsumer) => oldChannel.checkQueue(c.queue)),
|
|
1381
|
+
]);
|
|
1382
|
+
} catch (e) {
|
|
1383
|
+
logger.error('rabbit: old isConnected - false');
|
|
1384
|
+
return false;
|
|
1385
|
+
}
|
|
1386
|
+
logger.debug('rabbit: old isConnected - true');
|
|
1387
|
+
return true;
|
|
1388
|
+
}
|
|
1389
|
+
|
|
1390
|
+
private maskURL = (url: string): string => {
|
|
1391
|
+
try {
|
|
1392
|
+
const urlObj = new URL(url);
|
|
1393
|
+
urlObj.username = '***';
|
|
1394
|
+
urlObj.password = '***';
|
|
1395
|
+
return urlObj.toString();
|
|
1396
|
+
} catch {
|
|
1397
|
+
return url;
|
|
1398
|
+
}
|
|
1399
|
+
}
|
|
1243
1400
|
}
|
|
1244
1401
|
|
|
1245
1402
|
export default RabbitMq;
|