@autofleet/rabbit 3.2.21-beta.1 → 3.2.21
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 +16 -5
- package/dist/index.js +43 -19
- package/dist/lib/types.d.ts +3 -7
- package/dist/lib/utils.d.ts +2 -2
- package/package.json +2 -4
- package/src/index.ts +69 -26
- package/src/lib/types.ts +4 -9
- package/src/lib/utils.ts +6 -2
package/dist/index.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
2
|
import { EventEmitter } from 'events';
|
|
3
|
-
import { AmqpConnectionManager, ChannelWrapper } from 'amqp-connection-manager';
|
|
3
|
+
import { AmqpConnectionManager, ChannelWrapper, CreateChannelOpts } from 'amqp-connection-manager';
|
|
4
4
|
import { ConfirmChannel, ConsumeMessage, Options, Replies } from 'amqplib';
|
|
5
5
|
import { RedisConfig } from './lib/redis';
|
|
6
|
-
import { CallbackFunction, ConsumeMessageOrNull, ConsumeOptions, CustomMessageHeaders, QueuesCache, RedisLockType, ExchangesCache, QueueSetupPromisesDictionary,
|
|
6
|
+
import { CallbackFunction, ConsumeMessageOrNull, ConsumeOptions, CustomMessageHeaders, QueuesCache, RedisLockType, ExchangesCache, QueueSetupPromisesDictionary, AssertExchangePromisesDictionary } from './lib/types';
|
|
7
7
|
export interface IAfRabbitMq {
|
|
8
8
|
ack: any;
|
|
9
9
|
nack: any;
|
|
@@ -33,6 +33,15 @@ export interface AfRabbitOptions {
|
|
|
33
33
|
dontRetryAssert?: boolean;
|
|
34
34
|
rabbitHost?: string;
|
|
35
35
|
}
|
|
36
|
+
type newChannelOpts = {
|
|
37
|
+
name?: string;
|
|
38
|
+
onClose?: null | ((args: any | null) => void);
|
|
39
|
+
options?: CreateChannelOpts | undefined;
|
|
40
|
+
};
|
|
41
|
+
type assertChannelOpts = {
|
|
42
|
+
channelName?: string;
|
|
43
|
+
force?: boolean;
|
|
44
|
+
};
|
|
36
45
|
declare class RabbitMq implements IAfRabbitMq {
|
|
37
46
|
static parseMsg(msg: any): any;
|
|
38
47
|
static validateName(type: string, name: string): void;
|
|
@@ -57,6 +66,7 @@ declare class RabbitMq implements IAfRabbitMq {
|
|
|
57
66
|
exchanges: ExchangesCache;
|
|
58
67
|
queues: QueuesCache;
|
|
59
68
|
queueSetupPromises: QueueSetupPromisesDictionary;
|
|
69
|
+
assertExchangePromises: AssertExchangePromisesDictionary;
|
|
60
70
|
options: AfRabbitOptions | undefined;
|
|
61
71
|
redisClient: any;
|
|
62
72
|
redisLock?: RedisLockType;
|
|
@@ -69,12 +79,13 @@ declare class RabbitMq implements IAfRabbitMq {
|
|
|
69
79
|
nack: (channel: ConfirmChannel, queue: string, options: any, deadQueueOptions: Options.AssertQueue, msg: ConsumeMessageOrNull, releaseLock: any) => (userMsg: ConsumeMessageOrNull, { skipRetry, }?: NackOptions) => Promise<any>;
|
|
70
80
|
getConnection(): Promise<AmqpConnectionManager>;
|
|
71
81
|
getNewChannel({ name, onClose, options }?: newChannelOpts): Promise<ChannelWrapper>;
|
|
72
|
-
assertChannel({ force
|
|
73
|
-
assertExchange(exchangeName: string): Promise<any>;
|
|
82
|
+
assertChannel({ force }?: assertChannelOpts): Promise<ChannelWrapper>;
|
|
83
|
+
assertExchange(exchangeName: string, options?: any): Promise<any>;
|
|
74
84
|
getQueueLength(queue: string): Promise<Replies.AssertQueue>;
|
|
75
85
|
private deleteQueue;
|
|
76
86
|
bindQueue(queue: string, exchange: string): Promise<void>;
|
|
77
87
|
setupQueue(queueName: string, options?: Options.AssertQueue): Promise<Replies.AssertQueue>;
|
|
88
|
+
static shouldUseQuorum(queueName: string): boolean;
|
|
78
89
|
assertQueue(queueName: string, options?: Options.AssertQueue): Promise<any>;
|
|
79
90
|
private saveConsumer;
|
|
80
91
|
consume(queue: string, callback: CallbackFunction, options?: ConsumeOptions): Promise<any>;
|
|
@@ -82,7 +93,7 @@ declare class RabbitMq implements IAfRabbitMq {
|
|
|
82
93
|
private unlockRedisIfNeeded;
|
|
83
94
|
private consumeFromRabbit;
|
|
84
95
|
consumeFromExchange(queue: string, exchange: string, callback: CallbackFunction, options?: ConsumeOptions): Promise<any>;
|
|
85
|
-
publish(exchange: string, content: any, customHeaders?: any
|
|
96
|
+
publish(exchange: string, content: any, customHeaders?: any): Promise<boolean>;
|
|
86
97
|
sendToQueue(queue: string, content: any, options?: any, customHeaders?: any): Promise<boolean | undefined>;
|
|
87
98
|
isConnected(): Promise<boolean>;
|
|
88
99
|
gracefulShutdown(signal: string): Promise<void>;
|
package/dist/index.js
CHANGED
|
@@ -6,7 +6,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
7
|
const events_1 = require("events");
|
|
8
8
|
const util_1 = require("util");
|
|
9
|
-
const lodash_1 = require("lodash");
|
|
10
9
|
const moment_1 = __importDefault(require("moment"));
|
|
11
10
|
const redis_lock_1 = __importDefault(require("redis-lock"));
|
|
12
11
|
const amqp_connection_manager_1 = require("amqp-connection-manager");
|
|
@@ -124,6 +123,7 @@ class RabbitMq {
|
|
|
124
123
|
this.exchanges = {};
|
|
125
124
|
this.queues = {};
|
|
126
125
|
this.queueSetupPromises = {};
|
|
126
|
+
this.assertExchangePromises = {};
|
|
127
127
|
this.consumers = [];
|
|
128
128
|
this.options = options;
|
|
129
129
|
this.redisClient = redisConfig && (0, redis_1.default)(redisConfig);
|
|
@@ -243,14 +243,14 @@ class RabbitMq {
|
|
|
243
243
|
throw err;
|
|
244
244
|
}
|
|
245
245
|
}
|
|
246
|
-
async assertChannel({ force = false
|
|
246
|
+
async assertChannel({ force = false } = {}) {
|
|
247
247
|
if (!this.publishChannelSetupPromise) {
|
|
248
248
|
this.publishChannelSetupPromise = new Promise(async (resolve, reject) => {
|
|
249
249
|
if (this.channel && !force) {
|
|
250
250
|
return resolve(this.channel);
|
|
251
251
|
}
|
|
252
252
|
try {
|
|
253
|
-
const channel = await this.getNewChannel({
|
|
253
|
+
const channel = await this.getNewChannel({});
|
|
254
254
|
channel.on('error', (err) => {
|
|
255
255
|
logger_1.default.error('rabbit: channel error', { err });
|
|
256
256
|
});
|
|
@@ -264,14 +264,18 @@ class RabbitMq {
|
|
|
264
264
|
}
|
|
265
265
|
return this.publishChannelSetupPromise;
|
|
266
266
|
}
|
|
267
|
-
async assertExchange(exchangeName) {
|
|
267
|
+
async assertExchange(exchangeName, options) {
|
|
268
268
|
const channel = await this.assertChannel();
|
|
269
269
|
if (this.exchanges[exchangeName]) {
|
|
270
|
+
delete this.assertExchangePromises[exchangeName];
|
|
270
271
|
return this.exchanges[exchangeName];
|
|
271
272
|
}
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
273
|
+
if (this.assertExchangePromises[exchangeName]) {
|
|
274
|
+
return this.assertExchangePromises[exchangeName];
|
|
275
|
+
}
|
|
276
|
+
this.assertExchangePromises[exchangeName] = (0, utils_1.assertExchangeFanout)(channel, exchangeName);
|
|
277
|
+
this.exchanges[exchangeName] = await this.assertExchangePromises[exchangeName];
|
|
278
|
+
return this.exchanges[exchangeName];
|
|
275
279
|
}
|
|
276
280
|
async getQueueLength(queue) {
|
|
277
281
|
RabbitMq.validateName('queue', queue);
|
|
@@ -297,12 +301,22 @@ class RabbitMq {
|
|
|
297
301
|
}
|
|
298
302
|
async setupQueue(queueName, options) {
|
|
299
303
|
let queue;
|
|
304
|
+
const shouldUseQuorum = RabbitMq.shouldUseQuorum(queueName);
|
|
305
|
+
const localeOptions = {
|
|
306
|
+
...options,
|
|
307
|
+
durable: true,
|
|
308
|
+
arguments: {
|
|
309
|
+
...options?.arguments,
|
|
310
|
+
'x-consumer-timeout': 1000 * 60 * 60 * 24,
|
|
311
|
+
'x-queue-type': shouldUseQuorum ? 'quorum' : 'classic',
|
|
312
|
+
},
|
|
313
|
+
};
|
|
300
314
|
try {
|
|
301
315
|
const channel = await this.assertChannel();
|
|
302
316
|
debug('assertQueue->channel.addSetup', { queueName });
|
|
303
|
-
await channel.addSetup((setupChannel) => setupChannel.assertQueue(queueName,
|
|
317
|
+
await channel.addSetup((setupChannel) => setupChannel.assertQueue(queueName, localeOptions));
|
|
304
318
|
debug('assertQueue->channel.assertQueue', { queueName });
|
|
305
|
-
queue = await channel.assertQueue(queueName,
|
|
319
|
+
queue = await channel.assertQueue(queueName, localeOptions);
|
|
306
320
|
}
|
|
307
321
|
catch (e) {
|
|
308
322
|
logger_1.default.error('rabbit: assertQueue error', { queueName, options, error: e });
|
|
@@ -311,17 +325,28 @@ class RabbitMq {
|
|
|
311
325
|
const channel = await this.assertChannel({ force: true });
|
|
312
326
|
await this.deleteQueue(queueName);
|
|
313
327
|
debug('retrying assertQueue->channel.addSetup', { queueName });
|
|
314
|
-
await channel.addSetup((setupChannel) => setupChannel.assertQueue(queueName,
|
|
328
|
+
await channel.addSetup((setupChannel) => setupChannel.assertQueue(queueName, localeOptions));
|
|
315
329
|
debug('retrying assertQueue->channel.assertQueue', { queueName });
|
|
316
|
-
queue = await channel.assertQueue(queueName,
|
|
330
|
+
queue = await channel.assertQueue(queueName, localeOptions);
|
|
317
331
|
}
|
|
318
332
|
else {
|
|
319
333
|
throw e;
|
|
320
334
|
}
|
|
321
335
|
}
|
|
322
|
-
this.queues[queueName] =
|
|
336
|
+
this.queues[queueName] = queue;
|
|
323
337
|
return queue;
|
|
324
338
|
}
|
|
339
|
+
static shouldUseQuorum(queueName) {
|
|
340
|
+
const envQuorumQueuesWhitelist = process.env.QUORUM_QUEUES_WHITELIST;
|
|
341
|
+
if (envQuorumQueuesWhitelist === '*') {
|
|
342
|
+
return true;
|
|
343
|
+
}
|
|
344
|
+
if (envQuorumQueuesWhitelist) {
|
|
345
|
+
const whitelist = envQuorumQueuesWhitelist.split(',');
|
|
346
|
+
return whitelist.includes(queueName);
|
|
347
|
+
}
|
|
348
|
+
return false;
|
|
349
|
+
}
|
|
325
350
|
async assertQueue(queueName, options) {
|
|
326
351
|
RabbitMq.validateName('queue', queueName);
|
|
327
352
|
if (this.queues[queueName]) {
|
|
@@ -376,9 +401,8 @@ class RabbitMq {
|
|
|
376
401
|
}
|
|
377
402
|
const channel = await this.getNewChannel({});
|
|
378
403
|
return channel.addSetup(async (confirmChannel) => {
|
|
379
|
-
await
|
|
380
|
-
await confirmChannel.prefetch(limit,
|
|
381
|
-
const channelConsumerOptions = optionsWithDefaults.channelConsumeOptions ? (0, lodash_1.merge)(types_1.CONSUMER_DEFAULT_OPTIONS, optionsWithDefaults.channelConsumeOptions) : types_1.CONSUMER_DEFAULT_OPTIONS;
|
|
404
|
+
const q = await this.assertQueue(queue, optionsWithDefaults);
|
|
405
|
+
await confirmChannel.prefetch(limit, false);
|
|
382
406
|
const { consumerTag } = await confirmChannel.consume(queue, async (msg) => {
|
|
383
407
|
if (!msg) {
|
|
384
408
|
return null;
|
|
@@ -439,7 +463,7 @@ class RabbitMq {
|
|
|
439
463
|
catch (e) {
|
|
440
464
|
await localNack(msg);
|
|
441
465
|
}
|
|
442
|
-
},
|
|
466
|
+
}, types_1.CONSUMER_DEFAULT_OPTIONS);
|
|
443
467
|
if (!consumerTag) {
|
|
444
468
|
logger_1.default.error(`rabbit: failed to consume from queue ${queue}`);
|
|
445
469
|
}
|
|
@@ -467,17 +491,17 @@ class RabbitMq {
|
|
|
467
491
|
]);
|
|
468
492
|
});
|
|
469
493
|
}
|
|
470
|
-
async publish(exchange, content, customHeaders
|
|
494
|
+
async publish(exchange, content, customHeaders) {
|
|
471
495
|
return (0, utils_1.wrapSetImmediate)(async () => {
|
|
472
496
|
RabbitMq.validateName('exchange', exchange);
|
|
473
|
-
const channel = await this.assertChannel(
|
|
497
|
+
const channel = await this.assertChannel();
|
|
474
498
|
await this.assertExchange(exchange);
|
|
475
499
|
await channel.publish(exchange, '', Buffer.from(JSON.stringify(content)), RabbitMq.getPublishOptions(customHeaders));
|
|
476
500
|
});
|
|
477
501
|
}
|
|
478
502
|
async sendToQueue(queue, content, options, customHeaders) {
|
|
479
503
|
try {
|
|
480
|
-
await this.assertChannel(
|
|
504
|
+
await this.assertChannel();
|
|
481
505
|
}
|
|
482
506
|
catch (e) {
|
|
483
507
|
logger_1.default.error(`rabbit sendToQueue: failed to send assert channel when sending to queue ${queue}`, { e });
|
package/dist/lib/types.d.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { CreateChannelOpts } from 'amqp-connection-manager';
|
|
2
1
|
import { ConsumeMessage, Options, Replies } from 'amqplib';
|
|
3
2
|
export interface ExchangesCache {
|
|
4
3
|
[key: string]: any;
|
|
@@ -9,6 +8,9 @@ export interface QueuesCache {
|
|
|
9
8
|
export interface QueueSetupPromisesDictionary {
|
|
10
9
|
[key: string]: Promise<Replies.AssertQueue> | undefined;
|
|
11
10
|
}
|
|
11
|
+
export interface AssertExchangePromisesDictionary {
|
|
12
|
+
[key: string]: Promise<Replies.AssertExchange> | undefined;
|
|
13
|
+
}
|
|
12
14
|
export type CustomMessageHeaders = {
|
|
13
15
|
redisTimestampValidationKey?: string;
|
|
14
16
|
};
|
|
@@ -23,22 +25,16 @@ export interface ConsumeOptions {
|
|
|
23
25
|
useConsumeWithLock?: boolean;
|
|
24
26
|
auditContext?: any;
|
|
25
27
|
enableRabbitTrace?: boolean;
|
|
26
|
-
channelConsumeOptions?: Options.Consume;
|
|
27
28
|
}
|
|
28
29
|
export type CallbackFunction = (msg: ConsumeMessage, ack: any, nack: any) => Promise<any>;
|
|
29
30
|
export type newChannelOpts = {
|
|
30
31
|
name?: string;
|
|
31
32
|
onClose?: null | ((args: any | null) => void);
|
|
32
|
-
options?: CreateChannelOpts | undefined;
|
|
33
33
|
};
|
|
34
34
|
export type assertChannelOpts = {
|
|
35
35
|
channelName?: string;
|
|
36
36
|
force?: boolean;
|
|
37
|
-
newChannelOptions?: newChannelOpts;
|
|
38
37
|
};
|
|
39
|
-
export interface PublishOptions {
|
|
40
|
-
newChannelOptions?: newChannelOpts;
|
|
41
|
-
}
|
|
42
38
|
export type AfConsumer = {
|
|
43
39
|
queue: string;
|
|
44
40
|
callback: CallbackFunction;
|
package/dist/lib/utils.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ChannelWrapper } from 'amqp-connection-manager';
|
|
2
|
-
import { ConfirmChannel } from 'amqplib';
|
|
3
|
-
export declare const assertExchangeFanout: (c: ChannelWrapper | ConfirmChannel, exchangeName: string) => Promise<
|
|
2
|
+
import { ConfirmChannel, Replies } from 'amqplib';
|
|
3
|
+
export declare const assertExchangeFanout: (c: ChannelWrapper | ConfirmChannel, exchangeName: string) => Promise<Replies.AssertExchange>;
|
|
4
4
|
export declare const wrapSetImmediate: (callback: () => any) => Promise<any>;
|
|
5
5
|
export declare const rand: () => number;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@autofleet/rabbit",
|
|
3
|
-
"version": "3.2.21
|
|
3
|
+
"version": "3.2.21",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -19,7 +19,6 @@
|
|
|
19
19
|
"amqp-connection-manager": "4.1.9",
|
|
20
20
|
"amqplib": "0.10.3",
|
|
21
21
|
"bluebird": "^3.7.2",
|
|
22
|
-
"lodash": "^4.17.21",
|
|
23
22
|
"moment": "^2.29.1",
|
|
24
23
|
"redis": "^3.1.2",
|
|
25
24
|
"redis-lock": "^0.1.4"
|
|
@@ -31,7 +30,6 @@
|
|
|
31
30
|
"@autofleet/logger": "4.0.6",
|
|
32
31
|
"@types/amqplib": "0.8.2",
|
|
33
32
|
"@types/jest": "^29.5.12",
|
|
34
|
-
"@types/lodash": "^4.17.12",
|
|
35
33
|
"@types/node": "^20.14.11",
|
|
36
34
|
"@typescript-eslint/eslint-plugin": "^4.8.1",
|
|
37
35
|
"eslint": "^7.13.0",
|
|
@@ -45,4 +43,4 @@
|
|
|
45
43
|
},
|
|
46
44
|
"author": "",
|
|
47
45
|
"license": "ISC"
|
|
48
|
-
}
|
|
46
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -2,11 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
import { EventEmitter, once } from 'events';
|
|
4
4
|
import { promisify } from 'util';
|
|
5
|
-
import { merge } from 'lodash';
|
|
6
5
|
import moment from 'moment';
|
|
7
6
|
import RedisLock from 'redis-lock';
|
|
8
7
|
import {
|
|
9
|
-
AmqpConnectionManager, ChannelWrapper, connect,
|
|
8
|
+
AmqpConnectionManager, ChannelWrapper, connect, CreateChannelOpts,
|
|
10
9
|
} from 'amqp-connection-manager';
|
|
11
10
|
import {
|
|
12
11
|
ConfirmChannel, ConsumeMessage, Options, Replies,
|
|
@@ -36,10 +35,10 @@ import {
|
|
|
36
35
|
CustomMessageHeaders,
|
|
37
36
|
QueuesCache,
|
|
38
37
|
RedisLockType,
|
|
39
|
-
ExchangesCache,
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
38
|
+
ExchangesCache,
|
|
39
|
+
CONSUMER_DEFAULT_OPTIONS,
|
|
40
|
+
QueueSetupPromisesDictionary,
|
|
41
|
+
AssertExchangePromisesDictionary,
|
|
43
42
|
} from './lib/types';
|
|
44
43
|
|
|
45
44
|
// const debug = nodeDebug('af-rabbitmq')
|
|
@@ -82,6 +81,17 @@ export interface AfRabbitOptions {
|
|
|
82
81
|
rabbitHost?: string;
|
|
83
82
|
}
|
|
84
83
|
|
|
84
|
+
type newChannelOpts = {
|
|
85
|
+
name?: string;
|
|
86
|
+
onClose?: null | ((args: any | null) => void);
|
|
87
|
+
options?: CreateChannelOpts | undefined;
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
type assertChannelOpts = {
|
|
91
|
+
channelName?: string;
|
|
92
|
+
force?: boolean;
|
|
93
|
+
}
|
|
94
|
+
|
|
85
95
|
type AfConsumer = {
|
|
86
96
|
queue: string;
|
|
87
97
|
callback: CallbackFunction;
|
|
@@ -150,6 +160,8 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
150
160
|
|
|
151
161
|
queueSetupPromises: QueueSetupPromisesDictionary;
|
|
152
162
|
|
|
163
|
+
assertExchangePromises: AssertExchangePromisesDictionary;
|
|
164
|
+
|
|
153
165
|
options: AfRabbitOptions | undefined;
|
|
154
166
|
|
|
155
167
|
redisClient: any;
|
|
@@ -170,6 +182,7 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
170
182
|
this.exchanges = {};
|
|
171
183
|
this.queues = {};
|
|
172
184
|
this.queueSetupPromises = {};
|
|
185
|
+
this.assertExchangePromises = {};
|
|
173
186
|
this.consumers = [];
|
|
174
187
|
this.options = options;
|
|
175
188
|
this.redisClient = redisConfig && getRedisInstance(redisConfig);
|
|
@@ -372,7 +385,7 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
372
385
|
}
|
|
373
386
|
}
|
|
374
387
|
|
|
375
|
-
async assertChannel({ force = false
|
|
388
|
+
async assertChannel({ force = false } : assertChannelOpts = {}): Promise<ChannelWrapper> {
|
|
376
389
|
if (!this.publishChannelSetupPromise) {
|
|
377
390
|
this.publishChannelSetupPromise = new Promise<ChannelWrapper>(async (resolve, reject) => {
|
|
378
391
|
if (this.channel && !force) {
|
|
@@ -380,7 +393,7 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
380
393
|
}
|
|
381
394
|
|
|
382
395
|
try {
|
|
383
|
-
const channel = await this.getNewChannel({
|
|
396
|
+
const channel = await this.getNewChannel({});
|
|
384
397
|
channel.on('error', (err) => {
|
|
385
398
|
logger.error('rabbit: channel error', { err });
|
|
386
399
|
});
|
|
@@ -394,14 +407,21 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
394
407
|
return this.publishChannelSetupPromise;
|
|
395
408
|
}
|
|
396
409
|
|
|
397
|
-
async assertExchange(exchangeName: string) {
|
|
410
|
+
async assertExchange(exchangeName: string, options?: any) {
|
|
398
411
|
const channel: ChannelWrapper = await this.assertChannel();
|
|
412
|
+
|
|
399
413
|
if (this.exchanges[exchangeName]) {
|
|
414
|
+
delete this.assertExchangePromises[exchangeName];
|
|
400
415
|
return this.exchanges[exchangeName];
|
|
401
416
|
}
|
|
402
|
-
|
|
403
|
-
this.
|
|
404
|
-
|
|
417
|
+
|
|
418
|
+
if (this.assertExchangePromises[exchangeName]) {
|
|
419
|
+
return this.assertExchangePromises[exchangeName];
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
this.assertExchangePromises[exchangeName] = assertExchangeFanout(channel, exchangeName);
|
|
423
|
+
this.exchanges[exchangeName] = await this.assertExchangePromises[exchangeName];
|
|
424
|
+
return this.exchanges[exchangeName];
|
|
405
425
|
}
|
|
406
426
|
|
|
407
427
|
async getQueueLength(queue: string) {
|
|
@@ -431,12 +451,22 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
431
451
|
|
|
432
452
|
async setupQueue(queueName: string, options?: Options.AssertQueue): Promise<Replies.AssertQueue> {
|
|
433
453
|
let queue: Replies.AssertQueue;
|
|
454
|
+
const shouldUseQuorum = RabbitMq.shouldUseQuorum(queueName);
|
|
455
|
+
const localeOptions = {
|
|
456
|
+
...options,
|
|
457
|
+
durable: true,
|
|
458
|
+
arguments: {
|
|
459
|
+
...options?.arguments,
|
|
460
|
+
'x-consumer-timeout': 1000 * 60 * 60 * 24,
|
|
461
|
+
'x-queue-type': shouldUseQuorum ? 'quorum' : 'classic',
|
|
462
|
+
},
|
|
463
|
+
};
|
|
434
464
|
try {
|
|
435
465
|
const channel: ChannelWrapper = await this.assertChannel();
|
|
436
466
|
debug('assertQueue->channel.addSetup', { queueName });
|
|
437
|
-
await channel.addSetup((setupChannel: ConfirmChannel) => setupChannel.assertQueue(queueName,
|
|
467
|
+
await channel.addSetup((setupChannel: ConfirmChannel) => setupChannel.assertQueue(queueName, localeOptions));
|
|
438
468
|
debug('assertQueue->channel.assertQueue', { queueName });
|
|
439
|
-
queue = await channel.assertQueue(queueName,
|
|
469
|
+
queue = await channel.assertQueue(queueName, localeOptions);
|
|
440
470
|
} catch (e) {
|
|
441
471
|
logger.error('rabbit: assertQueue error', { queueName, options, error: e });
|
|
442
472
|
if (!this.options?.dontRetryAssert) {
|
|
@@ -445,19 +475,34 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
445
475
|
await this.deleteQueue(queueName);
|
|
446
476
|
|
|
447
477
|
debug('retrying assertQueue->channel.addSetup', { queueName });
|
|
448
|
-
await channel.addSetup((setupChannel: ConfirmChannel) => setupChannel.assertQueue(queueName,
|
|
478
|
+
await channel.addSetup((setupChannel: ConfirmChannel) => setupChannel.assertQueue(queueName, localeOptions));
|
|
449
479
|
debug('retrying assertQueue->channel.assertQueue', { queueName });
|
|
450
|
-
queue = await channel.assertQueue(queueName,
|
|
480
|
+
queue = await channel.assertQueue(queueName, localeOptions);
|
|
451
481
|
} else {
|
|
452
482
|
throw e;
|
|
453
483
|
}
|
|
454
484
|
}
|
|
455
485
|
|
|
456
|
-
this.queues[queueName] =
|
|
486
|
+
this.queues[queueName] = queue;
|
|
457
487
|
return queue;
|
|
458
488
|
}
|
|
459
489
|
|
|
460
|
-
|
|
490
|
+
static shouldUseQuorum(queueName: string): boolean {
|
|
491
|
+
const envQuorumQueuesWhitelist = process.env.QUORUM_QUEUES_WHITELIST;
|
|
492
|
+
|
|
493
|
+
if (envQuorumQueuesWhitelist === '*') {
|
|
494
|
+
return true;
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
if (envQuorumQueuesWhitelist) {
|
|
498
|
+
const whitelist = envQuorumQueuesWhitelist.split(',');
|
|
499
|
+
return whitelist.includes(queueName);
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
return false;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
async assertQueue(queueName: string, options?: Options.AssertQueue): Promise<any> {
|
|
461
506
|
RabbitMq.validateName('queue', queueName);
|
|
462
507
|
if (this.queues[queueName]) {
|
|
463
508
|
delete this.queueSetupPromises[queueName];
|
|
@@ -524,9 +569,8 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
524
569
|
}
|
|
525
570
|
const channel = await this.getNewChannel({});
|
|
526
571
|
return channel.addSetup(async (confirmChannel: ConfirmChannel) => {
|
|
527
|
-
await
|
|
528
|
-
await confirmChannel.prefetch(limit,
|
|
529
|
-
const channelConsumerOptions = optionsWithDefaults.channelConsumeOptions ? merge(CONSUMER_DEFAULT_OPTIONS, optionsWithDefaults.channelConsumeOptions) : CONSUMER_DEFAULT_OPTIONS;
|
|
572
|
+
const q = await this.assertQueue(queue, optionsWithDefaults);
|
|
573
|
+
await confirmChannel.prefetch(limit, false);
|
|
530
574
|
const { consumerTag } = await confirmChannel.consume(
|
|
531
575
|
queue,
|
|
532
576
|
async (msg: ConsumeMessageOrNull) => {
|
|
@@ -598,8 +642,7 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
598
642
|
} catch (e) {
|
|
599
643
|
await localNack(msg);
|
|
600
644
|
}
|
|
601
|
-
},
|
|
602
|
-
channelConsumerOptions,
|
|
645
|
+
}, CONSUMER_DEFAULT_OPTIONS,
|
|
603
646
|
);
|
|
604
647
|
if (!consumerTag) {
|
|
605
648
|
logger.error(`rabbit: failed to consume from queue ${queue}`);
|
|
@@ -634,10 +677,10 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
634
677
|
});
|
|
635
678
|
}
|
|
636
679
|
|
|
637
|
-
async publish(exchange: string, content: any, customHeaders?: any
|
|
680
|
+
async publish(exchange: string, content: any, customHeaders?: any) : Promise<boolean> {
|
|
638
681
|
return wrapSetImmediate(async () => {
|
|
639
682
|
RabbitMq.validateName('exchange', exchange);
|
|
640
|
-
const channel: ChannelWrapper = await this.assertChannel(
|
|
683
|
+
const channel: ChannelWrapper = await this.assertChannel();
|
|
641
684
|
await this.assertExchange(exchange);
|
|
642
685
|
await channel.publish(exchange, '',
|
|
643
686
|
Buffer.from(JSON.stringify(content)),
|
|
@@ -652,7 +695,7 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
652
695
|
customHeaders?: any,
|
|
653
696
|
): Promise<boolean | undefined> {
|
|
654
697
|
try {
|
|
655
|
-
await this.assertChannel(
|
|
698
|
+
await this.assertChannel();
|
|
656
699
|
} catch (e) {
|
|
657
700
|
logger.error(`rabbit sendToQueue: failed to send assert channel when sending to queue ${queue}`, { e });
|
|
658
701
|
throw e;
|
package/src/lib/types.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { CreateChannelOpts } from 'amqp-connection-manager';
|
|
2
1
|
import { ConsumeMessage, Options, Replies } from 'amqplib';
|
|
3
2
|
|
|
4
3
|
export interface ExchangesCache {
|
|
@@ -13,6 +12,10 @@ export interface QueueSetupPromisesDictionary {
|
|
|
13
12
|
[key: string]: Promise<Replies.AssertQueue> | undefined
|
|
14
13
|
}
|
|
15
14
|
|
|
15
|
+
export interface AssertExchangePromisesDictionary {
|
|
16
|
+
[key: string]: Promise<Replies.AssertExchange> | undefined
|
|
17
|
+
}
|
|
18
|
+
|
|
16
19
|
export type CustomMessageHeaders = {
|
|
17
20
|
redisTimestampValidationKey?: string;
|
|
18
21
|
}
|
|
@@ -28,7 +31,6 @@ export interface ConsumeOptions {
|
|
|
28
31
|
useConsumeWithLock?: boolean;
|
|
29
32
|
auditContext?: any;
|
|
30
33
|
enableRabbitTrace?: boolean;
|
|
31
|
-
channelConsumeOptions?: Options.Consume;
|
|
32
34
|
}
|
|
33
35
|
|
|
34
36
|
export type CallbackFunction = (msg: ConsumeMessage, ack: any, nack: any) => Promise<any>
|
|
@@ -36,18 +38,11 @@ export type CallbackFunction = (msg: ConsumeMessage, ack: any, nack: any) => Pro
|
|
|
36
38
|
export type newChannelOpts = {
|
|
37
39
|
name?: string;
|
|
38
40
|
onClose?: null | ((args: any | null) => void);
|
|
39
|
-
options?: CreateChannelOpts | undefined;
|
|
40
41
|
};
|
|
41
42
|
export type assertChannelOpts = {
|
|
42
43
|
channelName?: string;
|
|
43
44
|
force?: boolean;
|
|
44
|
-
newChannelOptions?: newChannelOpts;
|
|
45
45
|
}
|
|
46
|
-
|
|
47
|
-
export interface PublishOptions {
|
|
48
|
-
newChannelOptions?: newChannelOpts;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
46
|
export type AfConsumer = {
|
|
52
47
|
queue: string;
|
|
53
48
|
callback: CallbackFunction;
|
package/src/lib/utils.ts
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import { ChannelWrapper } from 'amqp-connection-manager';
|
|
2
|
-
import { ConfirmChannel } from 'amqplib';
|
|
2
|
+
import { ConfirmChannel, Replies } from 'amqplib';
|
|
3
|
+
|
|
4
|
+
export const assertExchangeFanout = async (
|
|
5
|
+
c: ChannelWrapper | ConfirmChannel,
|
|
6
|
+
exchangeName: string,
|
|
7
|
+
): Promise<Replies.AssertExchange> => c.assertExchange(exchangeName, 'fanout');
|
|
3
8
|
|
|
4
|
-
export const assertExchangeFanout = async (c: ChannelWrapper | ConfirmChannel, exchangeName: string) => c.assertExchange(exchangeName, 'fanout');
|
|
5
9
|
export const wrapSetImmediate = (callback: () => any) => new Promise<any>((resolve, reject) => {
|
|
6
10
|
setImmediate(async () => {
|
|
7
11
|
try {
|