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