@autofleet/rabbit 3.2.2-2.beta-0 → 3.2.3
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/.nvmrc +1 -1
- package/dist/index.d.ts +18 -27
- package/dist/index.js +171 -241
- package/dist/lib/redis.d.ts +1 -1
- package/dist/lib/types.d.ts +8 -20
- package/dist/lib/types.js +1 -7
- package/dist/lib/utils.js +3 -6
- package/dist/logger.d.ts +1 -1
- package/dist/logger.js +2 -5
- package/package.json +13 -12
- package/src/index.ts +155 -241
- package/src/lib/types.ts +1 -16
- package/src/logger.ts +1 -1
- package/tsconfig.json +2 -5
package/src/index.ts
CHANGED
|
@@ -1,19 +1,16 @@
|
|
|
1
1
|
/* eslint-disable no-empty,consistent-return,no-async-promise-executor,@typescript-eslint/no-unused-vars */
|
|
2
2
|
|
|
3
|
-
import { EventEmitter
|
|
3
|
+
import { EventEmitter } from 'events';
|
|
4
4
|
import { promisify } from 'util';
|
|
5
5
|
import moment from 'moment';
|
|
6
6
|
import RedisLock from 'redis-lock';
|
|
7
|
-
import {
|
|
8
|
-
AmqpConnectionManager, ChannelWrapper, connect, CreateChannelOpts,
|
|
9
|
-
} from 'amqp-connection-manager';
|
|
7
|
+
import { AmqpConnectionManager, ChannelWrapper, connect } from 'amqp-connection-manager';
|
|
10
8
|
import {
|
|
11
9
|
ConfirmChannel, ConsumeMessage, Options, Replies,
|
|
12
10
|
} from 'amqplib';
|
|
13
11
|
import {
|
|
14
12
|
getCurrentPayload, newTrace, traceTypes, createOrSetRabbitTrace, outbreak,
|
|
15
13
|
} from '@autofleet/zehut';
|
|
16
|
-
import { randomUUID } from 'node:crypto';
|
|
17
14
|
import logger from './logger';
|
|
18
15
|
import RabbitError from './lib/rabbitError';
|
|
19
16
|
import getRedisInstance, { RedisConfig } from './lib/redis';
|
|
@@ -35,15 +32,14 @@ import {
|
|
|
35
32
|
CustomMessageHeaders,
|
|
36
33
|
QueuesCache,
|
|
37
34
|
RedisLockType,
|
|
38
|
-
ExchangesCache, CONSUMER_DEFAULT_OPTIONS,
|
|
39
|
-
ConnectionPurpose,
|
|
40
|
-
ConnectionData,
|
|
35
|
+
ExchangesCache, CONSUMER_DEFAULT_OPTIONS,
|
|
41
36
|
} from './lib/types';
|
|
42
37
|
|
|
43
38
|
// const debug = nodeDebug('af-rabbitmq')
|
|
44
|
-
const debug = logger.
|
|
39
|
+
const debug = logger.info;
|
|
45
40
|
|
|
46
41
|
const PUBLISH_TIMEOUT = 1000 * 10;
|
|
42
|
+
|
|
47
43
|
export interface IAfRabbitMq {
|
|
48
44
|
ack: any;
|
|
49
45
|
nack: any;
|
|
@@ -57,10 +53,6 @@ export interface IAfRabbitMq {
|
|
|
57
53
|
redisClient?: any;
|
|
58
54
|
}
|
|
59
55
|
|
|
60
|
-
interface NackOptions {
|
|
61
|
-
skipRetry?: boolean;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
56
|
export interface AfRabbitOptions {
|
|
65
57
|
disableReconnect?: boolean;
|
|
66
58
|
|
|
@@ -82,14 +74,11 @@ export interface AfRabbitOptions {
|
|
|
82
74
|
type newChannelOpts = {
|
|
83
75
|
name?: string;
|
|
84
76
|
onClose?: null | ((args: any | null) => void);
|
|
85
|
-
options?: CreateChannelOpts | undefined;
|
|
86
|
-
connectionPurpose: ConnectionPurpose,
|
|
87
77
|
};
|
|
88
78
|
|
|
89
79
|
type assertChannelOpts = {
|
|
90
80
|
channelName?: string;
|
|
91
81
|
force?: boolean;
|
|
92
|
-
connectionPurpose: ConnectionPurpose;
|
|
93
82
|
}
|
|
94
83
|
|
|
95
84
|
type AfConsumer = {
|
|
@@ -101,7 +90,7 @@ type AfConsumer = {
|
|
|
101
90
|
const HEARTBEAT = '60';
|
|
102
91
|
|
|
103
92
|
class RabbitMq implements IAfRabbitMq {
|
|
104
|
-
static parseMsg(msg: any): any {
|
|
93
|
+
static parseMsg(msg: any) : any {
|
|
105
94
|
let { content } = msg;
|
|
106
95
|
content = content.toString();
|
|
107
96
|
|
|
@@ -115,7 +104,7 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
115
104
|
};
|
|
116
105
|
}
|
|
117
106
|
|
|
118
|
-
static validateName(type: string, name: string)
|
|
107
|
+
static validateName(type: string, name: string) {
|
|
119
108
|
if (!name || name === '') {
|
|
120
109
|
throw new RabbitError(`error while using ${type} with no name`);
|
|
121
110
|
}
|
|
@@ -144,23 +133,18 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
144
133
|
|
|
145
134
|
channel: ChannelWrapper | null;
|
|
146
135
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
blockReconnect: boolean | null | undefined;
|
|
136
|
+
blockReconnect: boolean | null | undefined
|
|
150
137
|
|
|
151
|
-
|
|
152
|
-
[ConnectionPurpose.Consume]: ConnectionData;
|
|
153
|
-
[ConnectionPurpose.Publish]: ConnectionData;
|
|
154
|
-
};
|
|
138
|
+
connection: AmqpConnectionManager | null | undefined
|
|
155
139
|
|
|
156
140
|
em: EventEmitter;
|
|
157
141
|
|
|
142
|
+
creatingConnection: boolean;
|
|
143
|
+
|
|
158
144
|
exchanges: ExchangesCache;
|
|
159
145
|
|
|
160
146
|
queues: QueuesCache;
|
|
161
147
|
|
|
162
|
-
queueSetupPromises: QueueSetupPromisesDictionary;
|
|
163
|
-
|
|
164
148
|
options: AfRabbitOptions | undefined;
|
|
165
149
|
|
|
166
150
|
redisClient: any;
|
|
@@ -175,15 +159,10 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
175
159
|
constructor(options?: AfRabbitOptions, redisConfig?: RedisConfig) {
|
|
176
160
|
this.em = new EventEmitter();
|
|
177
161
|
this.channel = null;
|
|
178
|
-
this.
|
|
179
|
-
this.
|
|
180
|
-
[ConnectionPurpose.Consume]: { connection: null, creatingConnection: false },
|
|
181
|
-
[ConnectionPurpose.Publish]: { connection: null, creatingConnection: false },
|
|
182
|
-
};
|
|
162
|
+
this.connection = null;
|
|
163
|
+
this.creatingConnection = false;
|
|
183
164
|
this.exchanges = {};
|
|
184
165
|
this.queues = {};
|
|
185
|
-
this.queueSetupPromises = {};
|
|
186
|
-
this.consumers = [];
|
|
187
166
|
this.options = options;
|
|
188
167
|
this.redisClient = redisConfig && getRedisInstance(redisConfig);
|
|
189
168
|
if (this.redisClient) {
|
|
@@ -215,9 +194,8 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
215
194
|
return false;
|
|
216
195
|
}
|
|
217
196
|
|
|
218
|
-
public ack = (channel:
|
|
197
|
+
public ack = (channel: ChannelWrapper, msg: ConsumeMessageOrNull, shouldUpdateRedisTimestamp = false, releaseLock = null) => async (userMsg: ConsumeMessage) : Promise<any> => {
|
|
219
198
|
if (msg) {
|
|
220
|
-
debug('rabbit acking message', { deliveryTag: msg.fields.deliveryTag });
|
|
221
199
|
await channel.ack(msg);
|
|
222
200
|
const { properties: { headers } } = msg;
|
|
223
201
|
const timestamp = headers?.creationTimestamp;
|
|
@@ -231,7 +209,7 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
231
209
|
}
|
|
232
210
|
|
|
233
211
|
public nack = (
|
|
234
|
-
channel:
|
|
212
|
+
channel: ChannelWrapper,
|
|
235
213
|
queue: string,
|
|
236
214
|
options: any,
|
|
237
215
|
deadQueueOptions: Options.AssertQueue,
|
|
@@ -241,8 +219,8 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
241
219
|
userMsg: ConsumeMessageOrNull,
|
|
242
220
|
{
|
|
243
221
|
skipRetry = false,
|
|
244
|
-
}:
|
|
245
|
-
): Promise<any> => {
|
|
222
|
+
}: any = { },
|
|
223
|
+
) : Promise<any> => {
|
|
246
224
|
await this.unlockRedisIfNeeded(releaseLock);
|
|
247
225
|
if (channel && msg) {
|
|
248
226
|
if (
|
|
@@ -267,40 +245,39 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
267
245
|
: 1,
|
|
268
246
|
});
|
|
269
247
|
}
|
|
270
|
-
debug('rabbit nacking message', { deliveryTag: msg.fields.deliveryTag });
|
|
271
248
|
await channel.ack(msg);
|
|
272
249
|
} else {
|
|
273
250
|
logger.error('no channel or msg', {
|
|
251
|
+
channel: channel ? channel.name : '',
|
|
274
252
|
msg,
|
|
275
253
|
});
|
|
276
254
|
}
|
|
277
255
|
}
|
|
278
256
|
|
|
279
|
-
async getConnection(
|
|
280
|
-
return new Promise<AmqpConnectionManager
|
|
281
|
-
let { connection, creatingConnection } = this.connectionsMap[connectionPurpose];
|
|
257
|
+
async getConnection() {
|
|
258
|
+
return new Promise<AmqpConnectionManager>(async (resolve, reject) => {
|
|
282
259
|
if (this.blockReconnect) {
|
|
283
260
|
debug('rabbit: block reconnect');
|
|
284
261
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
285
262
|
// @ts-ignore
|
|
286
263
|
return resolve();
|
|
287
264
|
}
|
|
288
|
-
if (connection !== null) {
|
|
289
|
-
if (this.options?.disableReconnect || connection?.isConnected()) {
|
|
265
|
+
if (this.connection !== null) {
|
|
266
|
+
if (this.options?.disableReconnect || this.connection?.isConnected()) {
|
|
290
267
|
debug('rabbit: connection - is connected');
|
|
291
268
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
292
269
|
// @ts-ignore
|
|
293
|
-
return resolve(connection);
|
|
270
|
+
return resolve(this.connection);
|
|
294
271
|
}
|
|
295
272
|
debug('rabbit: connection - reconnecting');
|
|
296
273
|
}
|
|
297
|
-
if (creatingConnection) {
|
|
274
|
+
if (this.creatingConnection) {
|
|
298
275
|
debug('rabbit: creating connection emi');
|
|
299
276
|
this.em.once(CONNECTION_CREATED_CONST, resolve);
|
|
300
277
|
this.em.once(CONNECTION_FAILED_CONST, reject);
|
|
301
278
|
return;
|
|
302
279
|
}
|
|
303
|
-
this.
|
|
280
|
+
this.creatingConnection = true;
|
|
304
281
|
let isResolved = false;
|
|
305
282
|
|
|
306
283
|
// It is import to use it as a function and not as a variable
|
|
@@ -317,18 +294,12 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
317
294
|
};
|
|
318
295
|
|
|
319
296
|
const defaultUrls = findServers();
|
|
320
|
-
const
|
|
297
|
+
const connection: AmqpConnectionManager = await connect(defaultUrls, {
|
|
321
298
|
findServers,
|
|
322
299
|
});
|
|
323
300
|
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
return resolve(connection);
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
this.connectionsMap[connectionPurpose].connection = newConnection;
|
|
330
|
-
|
|
331
|
-
newConnection.on('error', (err) => {
|
|
301
|
+
this.connection = connection;
|
|
302
|
+
this.connection.on('error', (err) => {
|
|
332
303
|
logger.error('rabbit: connection error', { err });
|
|
333
304
|
if (!isResolved) {
|
|
334
305
|
isResolved = true;
|
|
@@ -337,8 +308,7 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
337
308
|
}
|
|
338
309
|
});
|
|
339
310
|
|
|
340
|
-
|
|
341
|
-
this.consumersTags = [];
|
|
311
|
+
this.connection.on('connectFailed', (err) => {
|
|
342
312
|
logger.error('rabbit: connection connectFailed', { err });
|
|
343
313
|
if (!isResolved) {
|
|
344
314
|
isResolved = true;
|
|
@@ -347,9 +317,10 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
347
317
|
}
|
|
348
318
|
});
|
|
349
319
|
|
|
350
|
-
|
|
351
|
-
this.consumersTags = [];
|
|
320
|
+
this.connection.on('disconnect', ({ err }) => {
|
|
352
321
|
debug('rabbit: connection closed');
|
|
322
|
+
this.exchanges = {};
|
|
323
|
+
this.queues = {};
|
|
353
324
|
if (this.options?.disableReconnect) {
|
|
354
325
|
logger.error(`${this.DISCONNECT_MSG}${err && ` - ${err}`}`);
|
|
355
326
|
this.blockReconnect = true;
|
|
@@ -357,68 +328,67 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
357
328
|
logger.error(`${this.RECONNECT_MSG}${err && ` - ${err}`}`);
|
|
358
329
|
}
|
|
359
330
|
});
|
|
360
|
-
|
|
331
|
+
this.connection.once('connect', async () => {
|
|
361
332
|
debug('rabbit: connection established');
|
|
362
|
-
this.
|
|
363
|
-
this.
|
|
333
|
+
await this.loadConsumers();
|
|
334
|
+
this.creatingConnection = false;
|
|
335
|
+
this.em.emit(CONNECTION_CREATED_CONST, connection);
|
|
364
336
|
isResolved = true;
|
|
365
|
-
resolve(
|
|
337
|
+
resolve(connection);
|
|
366
338
|
});
|
|
367
339
|
});
|
|
368
340
|
}
|
|
369
341
|
|
|
370
|
-
async getNewChannel({
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
connection = await this.getConnection(connectionPurpose);
|
|
376
|
-
} catch (e) {
|
|
377
|
-
logger.error(`rabbit: error on get connection for new channel ${name} `, { e });
|
|
378
|
-
throw e;
|
|
379
|
-
}
|
|
380
|
-
if (!connection) {
|
|
381
|
-
throw new Error(`rabbit: couldnt get connection for new channel ${name}`);
|
|
382
|
-
}
|
|
383
|
-
const channel = connection.createChannel({ ...options });
|
|
384
|
-
once(channel, 'close').then((args) => {
|
|
385
|
-
logger.error(`rabbit: channel ${name} closed`);
|
|
386
|
-
onClose?.(args);
|
|
387
|
-
});
|
|
388
|
-
try {
|
|
389
|
-
await once(channel, 'connect');
|
|
390
|
-
debug(`rabbit: channel ${name} CONNECTED`);
|
|
391
|
-
return channel;
|
|
392
|
-
} catch (err) {
|
|
393
|
-
logger.error(`rabbit: channel error ${name} error`, { err });
|
|
394
|
-
throw err;
|
|
395
|
-
}
|
|
396
|
-
}
|
|
342
|
+
async getNewChannel({ name = rand().toString(), onClose = null }: newChannelOpts = {}) {
|
|
343
|
+
return new Promise<ChannelWrapper>(async (resolve, reject) => {
|
|
344
|
+
const connection: AmqpConnectionManager = await this.getConnection();
|
|
345
|
+
const channel = connection.createChannel({
|
|
346
|
+
});
|
|
397
347
|
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
if (
|
|
402
|
-
|
|
348
|
+
let isResolved = false;
|
|
349
|
+
channel.on('error', (err) => {
|
|
350
|
+
logger.error(`rabbit: channel ${name} error`, { err });
|
|
351
|
+
if (!isResolved) {
|
|
352
|
+
isResolved = true;
|
|
353
|
+
reject(err);
|
|
403
354
|
}
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
});
|
|
410
|
-
this.channel = channel;
|
|
411
|
-
resolve(channel);
|
|
412
|
-
} catch (e) {
|
|
413
|
-
reject(e);
|
|
355
|
+
});
|
|
356
|
+
channel.on('close', (...args) => {
|
|
357
|
+
logger.error(`rabbit: channel ${name} closed`, { args });
|
|
358
|
+
if (onClose) {
|
|
359
|
+
onClose(args);
|
|
414
360
|
}
|
|
415
361
|
});
|
|
416
|
-
|
|
417
|
-
|
|
362
|
+
channel.once('connect', () => {
|
|
363
|
+
debug(`rabbit: channel ${name} CONNECTED`);
|
|
364
|
+
isResolved = true;
|
|
365
|
+
resolve(channel);
|
|
366
|
+
});
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
async assertChannel({ force = false } : assertChannelOpts = {}): Promise<ChannelWrapper> {
|
|
371
|
+
return new Promise<ChannelWrapper>(async (resolve, reject) => {
|
|
372
|
+
if (this.channel && !force) {
|
|
373
|
+
return resolve(this.channel);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
try {
|
|
377
|
+
const channel = await this.getNewChannel({
|
|
378
|
+
});
|
|
379
|
+
channel.on('error', (err) => {
|
|
380
|
+
logger.error('rabbit: channel error', { err });
|
|
381
|
+
});
|
|
382
|
+
this.channel = channel;
|
|
383
|
+
resolve(channel);
|
|
384
|
+
} catch (e) {
|
|
385
|
+
reject(e);
|
|
386
|
+
}
|
|
387
|
+
});
|
|
418
388
|
}
|
|
419
389
|
|
|
420
390
|
async assertExchange(exchangeName: string, options?: any) {
|
|
421
|
-
const channel: ChannelWrapper = await this.assertChannel(
|
|
391
|
+
const channel: ChannelWrapper = await this.assertChannel();
|
|
422
392
|
if (this.exchanges[exchangeName]) {
|
|
423
393
|
return this.exchanges[exchangeName];
|
|
424
394
|
}
|
|
@@ -427,36 +397,36 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
427
397
|
return exchange;
|
|
428
398
|
}
|
|
429
399
|
|
|
430
|
-
async getQueueLength(queue: string
|
|
400
|
+
async getQueueLength(queue: string) {
|
|
431
401
|
RabbitMq.validateName('queue', queue);
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
throw new Error('channel is not defined');
|
|
436
|
-
}
|
|
437
|
-
debug('rabbit: getting queue length', { queue, connected: connection?.isConnected() });
|
|
438
|
-
return channel?.checkQueue(queue);
|
|
402
|
+
const channel: ChannelWrapper = await this.assertChannel();
|
|
403
|
+
debug('rabbit: getting queue length', { queue, connected: this.connection?.isConnected() });
|
|
404
|
+
return channel.checkQueue(queue);
|
|
439
405
|
}
|
|
440
406
|
|
|
441
|
-
private async deleteQueue(queue: string
|
|
407
|
+
private async deleteQueue(queue: string) {
|
|
442
408
|
RabbitMq.validateName('queue', queue);
|
|
443
|
-
const channel: ChannelWrapper = await this.assertChannel(
|
|
409
|
+
const channel: ChannelWrapper = await this.assertChannel();
|
|
444
410
|
logger.info('rabbit: deleting queue', { queue });
|
|
445
411
|
const deleteQueueRes = await channel.deleteQueue(queue);
|
|
446
412
|
debug('queue deleted', deleteQueueRes);
|
|
447
413
|
return deleteQueueRes;
|
|
448
414
|
}
|
|
449
415
|
|
|
450
|
-
async bindQueue(queue: string, exchange: string
|
|
451
|
-
const channel: ChannelWrapper = await this.assertChannel(
|
|
416
|
+
async bindQueue(queue: string, exchange: string) {
|
|
417
|
+
const channel: ChannelWrapper = await this.assertChannel();
|
|
452
418
|
await channel.addSetup((setupChannel: ConfirmChannel) => setupChannel.bindQueue(queue, exchange, ''));
|
|
453
419
|
return channel.bindQueue(queue, exchange, '');
|
|
454
420
|
}
|
|
455
421
|
|
|
456
|
-
async
|
|
457
|
-
let queue: Replies.AssertQueue;
|
|
422
|
+
async assertQueue(queueName: string, options?: Options.AssertQueue) {
|
|
423
|
+
let queue: Replies.AssertQueue | null = null;
|
|
424
|
+
RabbitMq.validateName('queue', queueName);
|
|
425
|
+
if (this.queues[queueName]) {
|
|
426
|
+
return this.queues[queueName];
|
|
427
|
+
}
|
|
458
428
|
try {
|
|
459
|
-
const channel: ChannelWrapper = await this.assertChannel(
|
|
429
|
+
const channel: ChannelWrapper = await this.assertChannel();
|
|
460
430
|
debug('assertQueue->channel.addSetup', { queueName });
|
|
461
431
|
await channel.addSetup((setupChannel: ConfirmChannel) => setupChannel.assertQueue(queueName, options));
|
|
462
432
|
debug('assertQueue->channel.assertQueue', { queueName });
|
|
@@ -465,12 +435,12 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
465
435
|
logger.error('rabbit: assertQueue error', { queueName, options, error: e });
|
|
466
436
|
if (!this.options?.dontRetryAssert) {
|
|
467
437
|
debug('retrying assertQueue', { queueName });
|
|
468
|
-
const channel = await this.assertChannel({ force: true
|
|
469
|
-
await this.deleteQueue(queueName
|
|
438
|
+
const channel = await this.assertChannel({ force: true });
|
|
439
|
+
await this.deleteQueue(queueName);
|
|
470
440
|
|
|
471
|
-
debug('
|
|
441
|
+
debug('1assertQueue->channel.addSetup', { queueName });
|
|
472
442
|
await channel.addSetup((setupChannel: ConfirmChannel) => setupChannel.assertQueue(queueName, options));
|
|
473
|
-
debug('
|
|
443
|
+
debug('1assertQueue->channel.assertQueue', { queueName });
|
|
474
444
|
queue = await channel.assertQueue(queueName, options);
|
|
475
445
|
} else {
|
|
476
446
|
throw e;
|
|
@@ -481,35 +451,29 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
481
451
|
return queue;
|
|
482
452
|
}
|
|
483
453
|
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
}
|
|
490
|
-
|
|
491
|
-
if (this.queueSetupPromises[queueName]) {
|
|
492
|
-
return this.queueSetupPromises[queueName];
|
|
493
|
-
}
|
|
494
|
-
|
|
495
|
-
this.queueSetupPromises[queueName] = this.setupQueue(queueName, connectionPurpose, options);
|
|
496
|
-
return this.queueSetupPromises[queueName];
|
|
454
|
+
private saveConsumer(queue: string, callback: CallbackFunction, options: ConsumeOptions | undefined) {
|
|
455
|
+
this.consumers.push({
|
|
456
|
+
queue,
|
|
457
|
+
callback,
|
|
458
|
+
options,
|
|
459
|
+
});
|
|
497
460
|
}
|
|
498
461
|
|
|
499
|
-
private
|
|
500
|
-
|
|
501
|
-
if (
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
options,
|
|
507
|
-
});
|
|
462
|
+
private async loadConsumers() {
|
|
463
|
+
debug('rabbit: loading consumers', { consumers: this.consumers.length });
|
|
464
|
+
if (this.consumers.length > 0) {
|
|
465
|
+
await Promise.all(
|
|
466
|
+
this.consumers.map((consumer) => this
|
|
467
|
+
.consumeFromRabbit(consumer.queue, consumer.callback, consumer.options)),
|
|
468
|
+
);
|
|
508
469
|
}
|
|
509
470
|
}
|
|
510
471
|
|
|
511
472
|
async consume(queue: string, callback: CallbackFunction, options?: ConsumeOptions): Promise<any> {
|
|
512
|
-
|
|
473
|
+
if (this.connection && !this.creatingConnection) {
|
|
474
|
+
this.consumeFromRabbit(queue, callback, options);
|
|
475
|
+
}
|
|
476
|
+
return this.saveConsumer(queue, callback, options);
|
|
513
477
|
}
|
|
514
478
|
|
|
515
479
|
private async lockRedisIfNeeded(msg: any, options: any) {
|
|
@@ -535,8 +499,6 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
535
499
|
private async consumeFromRabbit(queue: string, callback: CallbackFunction, options?: ConsumeOptions): Promise<any> {
|
|
536
500
|
const optionsWithDefaults = { ...DEFAULT_OPTIONS, ...options };
|
|
537
501
|
RabbitMq.validateName('queue', queue);
|
|
538
|
-
this.saveConsumer(queue, callback, options);
|
|
539
|
-
const uniqueId = randomUUID();
|
|
540
502
|
const {
|
|
541
503
|
limit, deadMessageTtl, useConsumeWithLock, lockTimeout, auditContext, enableRabbitTrace,
|
|
542
504
|
} = optionsWithDefaults;
|
|
@@ -546,11 +508,11 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
546
508
|
}
|
|
547
509
|
logger.info(`rabbit: Consuming with lock from queue ${queue} with lockTimeout: ${lockTimeout}ms`);
|
|
548
510
|
}
|
|
549
|
-
const channel = await this.getNewChannel({
|
|
550
|
-
return channel.addSetup(async (
|
|
551
|
-
await
|
|
552
|
-
await
|
|
553
|
-
const { consumerTag } = await
|
|
511
|
+
const channel: ChannelWrapper = await this.getNewChannel({ name: `consume-queue-${queue}` });
|
|
512
|
+
return channel.addSetup(async (c: ConfirmChannel) => {
|
|
513
|
+
await c.assertQueue(queue, options ? { messageTtl: options.messageTtl } : undefined);
|
|
514
|
+
await c.prefetch(limit, true);
|
|
515
|
+
const { consumerTag } = await c.consume(
|
|
554
516
|
queue,
|
|
555
517
|
async (msg: ConsumeMessageOrNull) => {
|
|
556
518
|
if (!msg) {
|
|
@@ -574,7 +536,7 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
574
536
|
]);
|
|
575
537
|
} catch (e) {
|
|
576
538
|
logger.error('rabbit: failed to setRabbitTrace', { userId, e });
|
|
577
|
-
|
|
539
|
+
await this.nack(channel, queue, optionsWithDefaults, { messageTtl: deadMessageTtl }, msg, releaseLock)(msg);
|
|
578
540
|
}
|
|
579
541
|
}
|
|
580
542
|
|
|
@@ -589,37 +551,16 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
589
551
|
const shouldConsume = await this.shouldConsumeMessageByTimestamp(parsedMessage);
|
|
590
552
|
if (!shouldConsume) {
|
|
591
553
|
await this.unlockRedisIfNeeded(releaseLock);
|
|
592
|
-
return this.ack(
|
|
554
|
+
return this.ack(channel, msg)(msg);
|
|
593
555
|
}
|
|
594
|
-
|
|
595
|
-
let messageAcked = false;
|
|
596
|
-
// setting the localAck function to be used in the callback
|
|
597
|
-
|
|
598
|
-
const localAck = async () => {
|
|
599
|
-
if (messageAcked) {
|
|
600
|
-
return;
|
|
601
|
-
}
|
|
602
|
-
messageAcked = true;
|
|
603
|
-
return this.ack(confirmChannel, msg, true, releaseLock)(msg);
|
|
604
|
-
};
|
|
605
|
-
|
|
606
|
-
const localNack = async (_: ConsumeMessageOrNull, nackOptions: NackOptions = {}) => {
|
|
607
|
-
if (messageAcked) {
|
|
608
|
-
return;
|
|
609
|
-
}
|
|
610
|
-
debug('rabbit localNack', { messageAcked, uniqueId, deliveryTag: msg.fields.deliveryTag });
|
|
611
|
-
messageAcked = true;
|
|
612
|
-
return this.nack(confirmChannel, queue, optionsWithDefaults, { messageTtl: deadMessageTtl }, msg, releaseLock)(msg, nackOptions);
|
|
613
|
-
};
|
|
614
|
-
|
|
615
556
|
try {
|
|
616
557
|
await callback(
|
|
617
558
|
parsedMessage,
|
|
618
|
-
|
|
619
|
-
|
|
559
|
+
this.ack(channel, msg, true, releaseLock),
|
|
560
|
+
this.nack(channel, queue, optionsWithDefaults, { messageTtl: deadMessageTtl }, msg, releaseLock),
|
|
620
561
|
);
|
|
621
562
|
} catch (e) {
|
|
622
|
-
await
|
|
563
|
+
await this.nack(channel, queue, optionsWithDefaults, { messageTtl: deadMessageTtl }, msg, releaseLock)(msg);
|
|
623
564
|
}
|
|
624
565
|
}, CONSUMER_DEFAULT_OPTIONS,
|
|
625
566
|
);
|
|
@@ -627,18 +568,17 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
627
568
|
logger.error(`rabbit: failed to consume from queue ${queue}`);
|
|
628
569
|
} else {
|
|
629
570
|
logger.info(`rabbit: adding tag ${consumerTag} to the array.`);
|
|
630
|
-
this.consumersTags.push([
|
|
571
|
+
this.consumersTags.push([c, consumerTag]);
|
|
631
572
|
}
|
|
632
573
|
});
|
|
633
574
|
}
|
|
634
575
|
|
|
635
|
-
async consumeFromExchange(queue: string, exchange: string, callback: CallbackFunction, options?: ConsumeOptions): Promise<any> {
|
|
576
|
+
async consumeFromExchange(queue: string, exchange: string, callback: CallbackFunction, options?: ConsumeOptions) : Promise<any> {
|
|
636
577
|
const optionsWithDefaults = { ...DEFAULT_OPTIONS, ...options };
|
|
637
578
|
RabbitMq.validateName('exchange', exchange);
|
|
638
579
|
RabbitMq.validateName('queue', queue);
|
|
639
580
|
const { limit, deadMessageTtl } = optionsWithDefaults;
|
|
640
|
-
await this.
|
|
641
|
-
const channel: ChannelWrapper = await this.getNewChannel({ name: `consume - exchange - ${exchange} -queue - ${queue}`, connectionPurpose: ConnectionPurpose.Consume });
|
|
581
|
+
const channel: ChannelWrapper = await this.getNewChannel({ name: `consume-exchange-${exchange}-queue-${queue}` });
|
|
642
582
|
|
|
643
583
|
return channel.addSetup(async (c: ConfirmChannel) => {
|
|
644
584
|
const assertExchange = await assertExchangeFanout(c, exchange);
|
|
@@ -656,11 +596,11 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
656
596
|
});
|
|
657
597
|
}
|
|
658
598
|
|
|
659
|
-
async publish(exchange: string, content: any, customHeaders?: any): Promise<boolean> {
|
|
599
|
+
async publish(exchange: string, content: any, customHeaders?: any) : Promise<boolean> {
|
|
660
600
|
return wrapSetImmediate(async () => {
|
|
661
601
|
RabbitMq.validateName('exchange', exchange);
|
|
662
|
-
const channel: ChannelWrapper = await this.assertChannel(
|
|
663
|
-
await this.assertExchange(exchange
|
|
602
|
+
const channel: ChannelWrapper = await this.assertChannel();
|
|
603
|
+
await this.assertExchange(exchange);
|
|
664
604
|
await channel.publish(exchange, '',
|
|
665
605
|
Buffer.from(JSON.stringify(content)),
|
|
666
606
|
RabbitMq.getPublishOptions(customHeaders));
|
|
@@ -672,61 +612,35 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
672
612
|
content: any,
|
|
673
613
|
options?: any,
|
|
674
614
|
customHeaders?: any,
|
|
615
|
+
isBlocking?: boolean,
|
|
675
616
|
): Promise<boolean | undefined> {
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
Buffer.from(JSON.stringify(content)),
|
|
694
|
-
RabbitMq.getPublishOptions(customHeaders));
|
|
695
|
-
debug(`rabbit: sending to queue ${queue}`, { res });
|
|
696
|
-
return res;
|
|
697
|
-
} catch (e) {
|
|
698
|
-
const isConnected = await this.isConnected(ConnectionPurpose.Publish);
|
|
699
|
-
logger.error(`rabbit sendToQueue: failed to send to queue ${queue}, isConnected: ${isConnected}`, { e });
|
|
700
|
-
throw e;
|
|
617
|
+
const callback = async (): Promise<boolean | undefined> => {
|
|
618
|
+
try {
|
|
619
|
+
RabbitMq.validateName('queue', queue);
|
|
620
|
+
await this.assertChannel();
|
|
621
|
+
await this.assertQueue(queue, options);
|
|
622
|
+
const res = await this.channel?.sendToQueue(queue,
|
|
623
|
+
Buffer.from(JSON.stringify(content)),
|
|
624
|
+
RabbitMq.getPublishOptions(customHeaders));
|
|
625
|
+
debug(`rabbit: sending to queue ${queue}`, { res });
|
|
626
|
+
return res;
|
|
627
|
+
} catch (e) {
|
|
628
|
+
logger.error(`rabbit: failed to send to queue ${queue}`, { e });
|
|
629
|
+
throw e;
|
|
630
|
+
}
|
|
631
|
+
};
|
|
632
|
+
if (isBlocking) {
|
|
633
|
+
return callback();
|
|
701
634
|
}
|
|
635
|
+
return wrapSetImmediate(callback);
|
|
702
636
|
}
|
|
703
637
|
|
|
704
|
-
async isConnected(
|
|
705
|
-
const connection = await this.getConnection(
|
|
706
|
-
|
|
707
|
-
logger.error('rabbit: isConnected - false');
|
|
708
|
-
return false;
|
|
709
|
-
}
|
|
710
|
-
const isConnected = connection.isConnected();
|
|
711
|
-
if (!isConnected) {
|
|
712
|
-
logger.error('rabbit: isConnected - false');
|
|
713
|
-
return false;
|
|
714
|
-
}
|
|
715
|
-
const channel: any = await this.assertChannel({ connectionPurpose });
|
|
716
|
-
try {
|
|
717
|
-
await Promise.all([
|
|
718
|
-
channel.waitForConnect(),
|
|
719
|
-
...this.consumers.map((c: AfConsumer) => channel.checkQueue(c.queue)),
|
|
720
|
-
]);
|
|
721
|
-
} catch (e) {
|
|
722
|
-
logger.error('rabbit: isConnected - false');
|
|
723
|
-
return false;
|
|
724
|
-
}
|
|
725
|
-
logger.info('rabbit: isConnected - true');
|
|
726
|
-
return true;
|
|
638
|
+
async isConnected() : Promise<boolean> {
|
|
639
|
+
const connection = await this.getConnection();
|
|
640
|
+
return connection.isConnected();
|
|
727
641
|
}
|
|
728
642
|
|
|
729
|
-
async gracefulShutdown(signal: string): Promise<void> {
|
|
643
|
+
async gracefulShutdown(signal: string) : Promise<void> {
|
|
730
644
|
const tagsNumber = this.consumersTags.length;
|
|
731
645
|
logger.info(`rabbit: [gracefully-shutdown] received ${signal}! canceling #${tagsNumber} tags...`);
|
|
732
646
|
const cancelTagPromises = this.consumersTags.map(([channel, tag]) => channel.cancel(tag));
|