@autofleet/rabbit 3.2.2-2.beta-0 → 3.2.2
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 -244
- 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 -244
- 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,45 +245,41 @@ 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
|
-
// It is import to use it as a function and not as a variable
|
|
307
|
-
// because of k8s changes the env variables
|
|
308
|
-
// and we want to use the new values
|
|
309
283
|
const findServers = () => {
|
|
310
284
|
const userName = process.env.RABBITMQ_USERNAME || 'guest';
|
|
311
285
|
const password = process.env.RABBITMQ_PASSWORD || 'guest';
|
|
@@ -317,18 +291,12 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
317
291
|
};
|
|
318
292
|
|
|
319
293
|
const defaultUrls = findServers();
|
|
320
|
-
const
|
|
294
|
+
const connection: AmqpConnectionManager = await connect(defaultUrls, {
|
|
321
295
|
findServers,
|
|
322
296
|
});
|
|
323
297
|
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
return resolve(connection);
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
this.connectionsMap[connectionPurpose].connection = newConnection;
|
|
330
|
-
|
|
331
|
-
newConnection.on('error', (err) => {
|
|
298
|
+
this.connection = connection;
|
|
299
|
+
this.connection.on('error', (err) => {
|
|
332
300
|
logger.error('rabbit: connection error', { err });
|
|
333
301
|
if (!isResolved) {
|
|
334
302
|
isResolved = true;
|
|
@@ -337,8 +305,7 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
337
305
|
}
|
|
338
306
|
});
|
|
339
307
|
|
|
340
|
-
|
|
341
|
-
this.consumersTags = [];
|
|
308
|
+
this.connection.on('connectFailed', (err) => {
|
|
342
309
|
logger.error('rabbit: connection connectFailed', { err });
|
|
343
310
|
if (!isResolved) {
|
|
344
311
|
isResolved = true;
|
|
@@ -347,9 +314,10 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
347
314
|
}
|
|
348
315
|
});
|
|
349
316
|
|
|
350
|
-
|
|
351
|
-
this.consumersTags = [];
|
|
317
|
+
this.connection.on('disconnect', ({ err }) => {
|
|
352
318
|
debug('rabbit: connection closed');
|
|
319
|
+
this.exchanges = {};
|
|
320
|
+
this.queues = {};
|
|
353
321
|
if (this.options?.disableReconnect) {
|
|
354
322
|
logger.error(`${this.DISCONNECT_MSG}${err && ` - ${err}`}`);
|
|
355
323
|
this.blockReconnect = true;
|
|
@@ -357,68 +325,67 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
357
325
|
logger.error(`${this.RECONNECT_MSG}${err && ` - ${err}`}`);
|
|
358
326
|
}
|
|
359
327
|
});
|
|
360
|
-
|
|
328
|
+
this.connection.once('connect', async () => {
|
|
361
329
|
debug('rabbit: connection established');
|
|
362
|
-
this.
|
|
363
|
-
this.
|
|
330
|
+
await this.loadConsumers();
|
|
331
|
+
this.creatingConnection = false;
|
|
332
|
+
this.em.emit(CONNECTION_CREATED_CONST, connection);
|
|
364
333
|
isResolved = true;
|
|
365
|
-
resolve(
|
|
334
|
+
resolve(connection);
|
|
366
335
|
});
|
|
367
336
|
});
|
|
368
337
|
}
|
|
369
338
|
|
|
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
|
-
}
|
|
339
|
+
async getNewChannel({ name = rand().toString(), onClose = null }: newChannelOpts = {}) {
|
|
340
|
+
return new Promise<ChannelWrapper>(async (resolve, reject) => {
|
|
341
|
+
const connection: AmqpConnectionManager = await this.getConnection();
|
|
342
|
+
const channel = connection.createChannel({
|
|
343
|
+
});
|
|
397
344
|
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
if (
|
|
402
|
-
|
|
345
|
+
let isResolved = false;
|
|
346
|
+
channel.on('error', (err) => {
|
|
347
|
+
logger.error(`rabbit: channel ${name} error`, { err });
|
|
348
|
+
if (!isResolved) {
|
|
349
|
+
isResolved = true;
|
|
350
|
+
reject(err);
|
|
403
351
|
}
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
});
|
|
410
|
-
this.channel = channel;
|
|
411
|
-
resolve(channel);
|
|
412
|
-
} catch (e) {
|
|
413
|
-
reject(e);
|
|
352
|
+
});
|
|
353
|
+
channel.on('close', (...args) => {
|
|
354
|
+
logger.error(`rabbit: channel ${name} closed`, { args });
|
|
355
|
+
if (onClose) {
|
|
356
|
+
onClose(args);
|
|
414
357
|
}
|
|
415
358
|
});
|
|
416
|
-
|
|
417
|
-
|
|
359
|
+
channel.once('connect', () => {
|
|
360
|
+
debug(`rabbit: channel ${name} CONNECTED`);
|
|
361
|
+
isResolved = true;
|
|
362
|
+
resolve(channel);
|
|
363
|
+
});
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
async assertChannel({ force = false } : assertChannelOpts = {}): Promise<ChannelWrapper> {
|
|
368
|
+
return new Promise<ChannelWrapper>(async (resolve, reject) => {
|
|
369
|
+
if (this.channel && !force) {
|
|
370
|
+
return resolve(this.channel);
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
try {
|
|
374
|
+
const channel = await this.getNewChannel({
|
|
375
|
+
});
|
|
376
|
+
channel.on('error', (err) => {
|
|
377
|
+
logger.error('rabbit: channel error', { err });
|
|
378
|
+
});
|
|
379
|
+
this.channel = channel;
|
|
380
|
+
resolve(channel);
|
|
381
|
+
} catch (e) {
|
|
382
|
+
reject(e);
|
|
383
|
+
}
|
|
384
|
+
});
|
|
418
385
|
}
|
|
419
386
|
|
|
420
387
|
async assertExchange(exchangeName: string, options?: any) {
|
|
421
|
-
const channel: ChannelWrapper = await this.assertChannel(
|
|
388
|
+
const channel: ChannelWrapper = await this.assertChannel();
|
|
422
389
|
if (this.exchanges[exchangeName]) {
|
|
423
390
|
return this.exchanges[exchangeName];
|
|
424
391
|
}
|
|
@@ -427,36 +394,36 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
427
394
|
return exchange;
|
|
428
395
|
}
|
|
429
396
|
|
|
430
|
-
async getQueueLength(queue: string
|
|
397
|
+
async getQueueLength(queue: string) {
|
|
431
398
|
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);
|
|
399
|
+
const channel: ChannelWrapper = await this.assertChannel();
|
|
400
|
+
debug('rabbit: getting queue length', { queue, connected: this.connection?.isConnected() });
|
|
401
|
+
return channel.checkQueue(queue);
|
|
439
402
|
}
|
|
440
403
|
|
|
441
|
-
private async deleteQueue(queue: string
|
|
404
|
+
private async deleteQueue(queue: string) {
|
|
442
405
|
RabbitMq.validateName('queue', queue);
|
|
443
|
-
const channel: ChannelWrapper = await this.assertChannel(
|
|
406
|
+
const channel: ChannelWrapper = await this.assertChannel();
|
|
444
407
|
logger.info('rabbit: deleting queue', { queue });
|
|
445
408
|
const deleteQueueRes = await channel.deleteQueue(queue);
|
|
446
409
|
debug('queue deleted', deleteQueueRes);
|
|
447
410
|
return deleteQueueRes;
|
|
448
411
|
}
|
|
449
412
|
|
|
450
|
-
async bindQueue(queue: string, exchange: string
|
|
451
|
-
const channel: ChannelWrapper = await this.assertChannel(
|
|
413
|
+
async bindQueue(queue: string, exchange: string) {
|
|
414
|
+
const channel: ChannelWrapper = await this.assertChannel();
|
|
452
415
|
await channel.addSetup((setupChannel: ConfirmChannel) => setupChannel.bindQueue(queue, exchange, ''));
|
|
453
416
|
return channel.bindQueue(queue, exchange, '');
|
|
454
417
|
}
|
|
455
418
|
|
|
456
|
-
async
|
|
457
|
-
let queue: Replies.AssertQueue;
|
|
419
|
+
async assertQueue(queueName: string, options?: Options.AssertQueue) {
|
|
420
|
+
let queue: Replies.AssertQueue | null = null;
|
|
421
|
+
RabbitMq.validateName('queue', queueName);
|
|
422
|
+
if (this.queues[queueName]) {
|
|
423
|
+
return this.queues[queueName];
|
|
424
|
+
}
|
|
458
425
|
try {
|
|
459
|
-
const channel: ChannelWrapper = await this.assertChannel(
|
|
426
|
+
const channel: ChannelWrapper = await this.assertChannel();
|
|
460
427
|
debug('assertQueue->channel.addSetup', { queueName });
|
|
461
428
|
await channel.addSetup((setupChannel: ConfirmChannel) => setupChannel.assertQueue(queueName, options));
|
|
462
429
|
debug('assertQueue->channel.assertQueue', { queueName });
|
|
@@ -465,12 +432,12 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
465
432
|
logger.error('rabbit: assertQueue error', { queueName, options, error: e });
|
|
466
433
|
if (!this.options?.dontRetryAssert) {
|
|
467
434
|
debug('retrying assertQueue', { queueName });
|
|
468
|
-
const channel = await this.assertChannel({ force: true
|
|
469
|
-
await this.deleteQueue(queueName
|
|
435
|
+
const channel = await this.assertChannel({ force: true });
|
|
436
|
+
await this.deleteQueue(queueName);
|
|
470
437
|
|
|
471
|
-
debug('
|
|
438
|
+
debug('1assertQueue->channel.addSetup', { queueName });
|
|
472
439
|
await channel.addSetup((setupChannel: ConfirmChannel) => setupChannel.assertQueue(queueName, options));
|
|
473
|
-
debug('
|
|
440
|
+
debug('1assertQueue->channel.assertQueue', { queueName });
|
|
474
441
|
queue = await channel.assertQueue(queueName, options);
|
|
475
442
|
} else {
|
|
476
443
|
throw e;
|
|
@@ -481,35 +448,29 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
481
448
|
return queue;
|
|
482
449
|
}
|
|
483
450
|
|
|
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];
|
|
451
|
+
private saveConsumer(queue: string, callback: CallbackFunction, options: ConsumeOptions | undefined) {
|
|
452
|
+
this.consumers.push({
|
|
453
|
+
queue,
|
|
454
|
+
callback,
|
|
455
|
+
options,
|
|
456
|
+
});
|
|
497
457
|
}
|
|
498
458
|
|
|
499
|
-
private
|
|
500
|
-
|
|
501
|
-
if (
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
options,
|
|
507
|
-
});
|
|
459
|
+
private async loadConsumers() {
|
|
460
|
+
debug('rabbit: loading consumers', { consumers: this.consumers.length });
|
|
461
|
+
if (this.consumers.length > 0) {
|
|
462
|
+
await Promise.all(
|
|
463
|
+
this.consumers.map((consumer) => this
|
|
464
|
+
.consumeFromRabbit(consumer.queue, consumer.callback, consumer.options)),
|
|
465
|
+
);
|
|
508
466
|
}
|
|
509
467
|
}
|
|
510
468
|
|
|
511
469
|
async consume(queue: string, callback: CallbackFunction, options?: ConsumeOptions): Promise<any> {
|
|
512
|
-
|
|
470
|
+
if (this.connection && !this.creatingConnection) {
|
|
471
|
+
this.consumeFromRabbit(queue, callback, options);
|
|
472
|
+
}
|
|
473
|
+
return this.saveConsumer(queue, callback, options);
|
|
513
474
|
}
|
|
514
475
|
|
|
515
476
|
private async lockRedisIfNeeded(msg: any, options: any) {
|
|
@@ -535,8 +496,6 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
535
496
|
private async consumeFromRabbit(queue: string, callback: CallbackFunction, options?: ConsumeOptions): Promise<any> {
|
|
536
497
|
const optionsWithDefaults = { ...DEFAULT_OPTIONS, ...options };
|
|
537
498
|
RabbitMq.validateName('queue', queue);
|
|
538
|
-
this.saveConsumer(queue, callback, options);
|
|
539
|
-
const uniqueId = randomUUID();
|
|
540
499
|
const {
|
|
541
500
|
limit, deadMessageTtl, useConsumeWithLock, lockTimeout, auditContext, enableRabbitTrace,
|
|
542
501
|
} = optionsWithDefaults;
|
|
@@ -546,11 +505,11 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
546
505
|
}
|
|
547
506
|
logger.info(`rabbit: Consuming with lock from queue ${queue} with lockTimeout: ${lockTimeout}ms`);
|
|
548
507
|
}
|
|
549
|
-
const channel = await this.getNewChannel({
|
|
550
|
-
return channel.addSetup(async (
|
|
551
|
-
await
|
|
552
|
-
await
|
|
553
|
-
const { consumerTag } = await
|
|
508
|
+
const channel: ChannelWrapper = await this.getNewChannel({ name: `consume-queue-${queue}` });
|
|
509
|
+
return channel.addSetup(async (c: ConfirmChannel) => {
|
|
510
|
+
await c.assertQueue(queue, options ? { messageTtl: options.messageTtl } : undefined);
|
|
511
|
+
await c.prefetch(limit, true);
|
|
512
|
+
const { consumerTag } = await c.consume(
|
|
554
513
|
queue,
|
|
555
514
|
async (msg: ConsumeMessageOrNull) => {
|
|
556
515
|
if (!msg) {
|
|
@@ -574,7 +533,7 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
574
533
|
]);
|
|
575
534
|
} catch (e) {
|
|
576
535
|
logger.error('rabbit: failed to setRabbitTrace', { userId, e });
|
|
577
|
-
|
|
536
|
+
await this.nack(channel, queue, optionsWithDefaults, { messageTtl: deadMessageTtl }, msg, releaseLock)(msg);
|
|
578
537
|
}
|
|
579
538
|
}
|
|
580
539
|
|
|
@@ -589,37 +548,16 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
589
548
|
const shouldConsume = await this.shouldConsumeMessageByTimestamp(parsedMessage);
|
|
590
549
|
if (!shouldConsume) {
|
|
591
550
|
await this.unlockRedisIfNeeded(releaseLock);
|
|
592
|
-
return this.ack(
|
|
551
|
+
return this.ack(channel, msg)(msg);
|
|
593
552
|
}
|
|
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
553
|
try {
|
|
616
554
|
await callback(
|
|
617
555
|
parsedMessage,
|
|
618
|
-
|
|
619
|
-
|
|
556
|
+
this.ack(channel, msg, true, releaseLock),
|
|
557
|
+
this.nack(channel, queue, optionsWithDefaults, { messageTtl: deadMessageTtl }, msg, releaseLock),
|
|
620
558
|
);
|
|
621
559
|
} catch (e) {
|
|
622
|
-
await
|
|
560
|
+
await this.nack(channel, queue, optionsWithDefaults, { messageTtl: deadMessageTtl }, msg, releaseLock)(msg);
|
|
623
561
|
}
|
|
624
562
|
}, CONSUMER_DEFAULT_OPTIONS,
|
|
625
563
|
);
|
|
@@ -627,18 +565,17 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
627
565
|
logger.error(`rabbit: failed to consume from queue ${queue}`);
|
|
628
566
|
} else {
|
|
629
567
|
logger.info(`rabbit: adding tag ${consumerTag} to the array.`);
|
|
630
|
-
this.consumersTags.push([
|
|
568
|
+
this.consumersTags.push([c, consumerTag]);
|
|
631
569
|
}
|
|
632
570
|
});
|
|
633
571
|
}
|
|
634
572
|
|
|
635
|
-
async consumeFromExchange(queue: string, exchange: string, callback: CallbackFunction, options?: ConsumeOptions): Promise<any> {
|
|
573
|
+
async consumeFromExchange(queue: string, exchange: string, callback: CallbackFunction, options?: ConsumeOptions) : Promise<any> {
|
|
636
574
|
const optionsWithDefaults = { ...DEFAULT_OPTIONS, ...options };
|
|
637
575
|
RabbitMq.validateName('exchange', exchange);
|
|
638
576
|
RabbitMq.validateName('queue', queue);
|
|
639
577
|
const { limit, deadMessageTtl } = optionsWithDefaults;
|
|
640
|
-
await this.
|
|
641
|
-
const channel: ChannelWrapper = await this.getNewChannel({ name: `consume - exchange - ${exchange} -queue - ${queue}`, connectionPurpose: ConnectionPurpose.Consume });
|
|
578
|
+
const channel: ChannelWrapper = await this.getNewChannel({ name: `consume-exchange-${exchange}-queue-${queue}` });
|
|
642
579
|
|
|
643
580
|
return channel.addSetup(async (c: ConfirmChannel) => {
|
|
644
581
|
const assertExchange = await assertExchangeFanout(c, exchange);
|
|
@@ -656,11 +593,11 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
656
593
|
});
|
|
657
594
|
}
|
|
658
595
|
|
|
659
|
-
async publish(exchange: string, content: any, customHeaders?: any): Promise<boolean> {
|
|
596
|
+
async publish(exchange: string, content: any, customHeaders?: any) : Promise<boolean> {
|
|
660
597
|
return wrapSetImmediate(async () => {
|
|
661
598
|
RabbitMq.validateName('exchange', exchange);
|
|
662
|
-
const channel: ChannelWrapper = await this.assertChannel(
|
|
663
|
-
await this.assertExchange(exchange
|
|
599
|
+
const channel: ChannelWrapper = await this.assertChannel();
|
|
600
|
+
await this.assertExchange(exchange);
|
|
664
601
|
await channel.publish(exchange, '',
|
|
665
602
|
Buffer.from(JSON.stringify(content)),
|
|
666
603
|
RabbitMq.getPublishOptions(customHeaders));
|
|
@@ -672,61 +609,35 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
672
609
|
content: any,
|
|
673
610
|
options?: any,
|
|
674
611
|
customHeaders?: any,
|
|
612
|
+
isBlocking?: boolean,
|
|
675
613
|
): 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;
|
|
614
|
+
const callback = async (): Promise<boolean | undefined> => {
|
|
615
|
+
try {
|
|
616
|
+
RabbitMq.validateName('queue', queue);
|
|
617
|
+
await this.assertChannel();
|
|
618
|
+
await this.assertQueue(queue, options);
|
|
619
|
+
const res = await this.channel?.sendToQueue(queue,
|
|
620
|
+
Buffer.from(JSON.stringify(content)),
|
|
621
|
+
RabbitMq.getPublishOptions(customHeaders));
|
|
622
|
+
debug(`rabbit: sending to queue ${queue}`, { res });
|
|
623
|
+
return res;
|
|
624
|
+
} catch (e) {
|
|
625
|
+
logger.error(`rabbit: failed to send to queue ${queue}`, { e });
|
|
626
|
+
throw e;
|
|
627
|
+
}
|
|
628
|
+
};
|
|
629
|
+
if (isBlocking) {
|
|
630
|
+
return callback();
|
|
701
631
|
}
|
|
632
|
+
return wrapSetImmediate(callback);
|
|
702
633
|
}
|
|
703
634
|
|
|
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;
|
|
635
|
+
async isConnected() : Promise<boolean> {
|
|
636
|
+
const connection = await this.getConnection();
|
|
637
|
+
return connection.isConnected();
|
|
727
638
|
}
|
|
728
639
|
|
|
729
|
-
async gracefulShutdown(signal: string): Promise<void> {
|
|
640
|
+
async gracefulShutdown(signal: string) : Promise<void> {
|
|
730
641
|
const tagsNumber = this.consumersTags.length;
|
|
731
642
|
logger.info(`rabbit: [gracefully-shutdown] received ${signal}! canceling #${tagsNumber} tags...`);
|
|
732
643
|
const cancelTagPromises = this.consumersTags.map(([channel, tag]) => channel.cancel(tag));
|