@autofleet/rabbit 3.2.24-beta.10 → 3.2.24-beta.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/dist/index.d.ts +15 -10
- package/dist/index.js +81 -66
- package/dist/lib/consts.d.ts +4 -0
- package/dist/lib/consts.js +6 -1
- package/dist/lib/types.d.ts +5 -0
- package/package.json +1 -1
- package/src/index.ts +105 -83
- package/src/lib/consts.ts +5 -0
- package/src/lib/types.ts +6 -0
package/dist/index.d.ts
CHANGED
|
@@ -3,7 +3,8 @@ import { EventEmitter } from 'events';
|
|
|
3
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 {
|
|
6
|
+
import { ConnectionPurpose } from './lib/consts';
|
|
7
|
+
import { CallbackFunction, ConsumeMessageOrNull, ConsumeOptions, CustomMessageHeaders, QueuesCache, RedisLockType, ExchangesCache, QueueSetupPromisesDictionary, AssertExchangePromisesDictionary, ConnectionData } from './lib/types';
|
|
7
8
|
export interface IAfRabbitMq {
|
|
8
9
|
ack: any;
|
|
9
10
|
nack: any;
|
|
@@ -37,10 +38,12 @@ type newChannelOpts = {
|
|
|
37
38
|
name?: string;
|
|
38
39
|
onClose?: null | ((args: any | null) => void);
|
|
39
40
|
options?: CreateChannelOpts | undefined;
|
|
41
|
+
connectionPurpose?: ConnectionPurpose;
|
|
40
42
|
};
|
|
41
43
|
type assertChannelOpts = {
|
|
42
44
|
channelName?: string;
|
|
43
45
|
force?: boolean;
|
|
46
|
+
connectionPurpose?: ConnectionPurpose;
|
|
44
47
|
};
|
|
45
48
|
declare class RabbitMq implements IAfRabbitMq {
|
|
46
49
|
static parseMsg(msg: any): any;
|
|
@@ -60,9 +63,11 @@ declare class RabbitMq implements IAfRabbitMq {
|
|
|
60
63
|
channel: ChannelWrapper | null;
|
|
61
64
|
publishChannelSetupPromise: Promise<ChannelWrapper> | null;
|
|
62
65
|
blockReconnect: boolean | null | undefined;
|
|
63
|
-
|
|
66
|
+
connectionsMap: {
|
|
67
|
+
[ConnectionPurpose.Consume]: ConnectionData;
|
|
68
|
+
[ConnectionPurpose.Publish]: ConnectionData;
|
|
69
|
+
};
|
|
64
70
|
em: EventEmitter;
|
|
65
|
-
creatingConnection: boolean;
|
|
66
71
|
exchanges: ExchangesCache;
|
|
67
72
|
queues: QueuesCache;
|
|
68
73
|
queueSetupPromises: QueueSetupPromisesDictionary;
|
|
@@ -77,16 +82,16 @@ declare class RabbitMq implements IAfRabbitMq {
|
|
|
77
82
|
private shouldConsumeMessageByTimestamp;
|
|
78
83
|
ack: (channel: ConfirmChannel, msg: ConsumeMessageOrNull, shouldUpdateRedisTimestamp?: boolean, releaseLock?: null) => (userMsg: ConsumeMessage) => Promise<any>;
|
|
79
84
|
nack: (channel: ConfirmChannel, queue: string, options: any, deadQueueOptions: Options.AssertQueue, msg: ConsumeMessageOrNull, releaseLock: any) => (userMsg: ConsumeMessageOrNull, { skipRetry, }?: NackOptions) => Promise<any>;
|
|
80
|
-
getConnection(): Promise<AmqpConnectionManager>;
|
|
81
|
-
getNewChannel({ name, onClose, options }
|
|
82
|
-
assertChannel({ force }
|
|
85
|
+
getConnection(connectionPurpose: ConnectionPurpose): Promise<AmqpConnectionManager | null | undefined>;
|
|
86
|
+
getNewChannel({ name, onClose, options, connectionPurpose, }: newChannelOpts): Promise<ChannelWrapper>;
|
|
87
|
+
assertChannel({ force, connectionPurpose }: assertChannelOpts): Promise<ChannelWrapper>;
|
|
83
88
|
assertExchange(exchangeName: string, options?: any): Promise<any>;
|
|
84
|
-
getQueueLength(queue: string): Promise<Replies.AssertQueue>;
|
|
89
|
+
getQueueLength(queue: string, connectionPurpose?: ConnectionPurpose): Promise<Replies.AssertQueue>;
|
|
85
90
|
private deleteQueue;
|
|
86
|
-
bindQueue(queue: string, exchange: string): Promise<void>;
|
|
87
|
-
setupQueue(queueName: string, options?: Options.AssertQueue): Promise<Replies.AssertQueue>;
|
|
91
|
+
bindQueue(queue: string, exchange: string, connectionPurpose: ConnectionPurpose): Promise<void>;
|
|
92
|
+
setupQueue(queueName: string, connectionPurpose: ConnectionPurpose, options?: Options.AssertQueue): Promise<Replies.AssertQueue>;
|
|
88
93
|
static shouldUseQuorum(queueName: string): boolean;
|
|
89
|
-
assertQueue(queueName: string, options?: Options.AssertQueue): Promise<any>;
|
|
94
|
+
assertQueue(queueName: string, connectionPurpose: ConnectionPurpose, options?: Options.AssertQueue): Promise<any>;
|
|
90
95
|
private saveConsumer;
|
|
91
96
|
consume(queue: string, callback: CallbackFunction, options?: ConsumeOptions): Promise<any>;
|
|
92
97
|
private lockRedisIfNeeded;
|
package/dist/index.js
CHANGED
|
@@ -119,8 +119,10 @@ class RabbitMq {
|
|
|
119
119
|
this.em = new events_1.EventEmitter();
|
|
120
120
|
this.channel = null;
|
|
121
121
|
this.publishChannelSetupPromise = null;
|
|
122
|
-
this.
|
|
123
|
-
|
|
122
|
+
this.connectionsMap = {
|
|
123
|
+
[consts_1.ConnectionPurpose.Consume]: { connection: null, creatingConnection: false },
|
|
124
|
+
[consts_1.ConnectionPurpose.Publish]: { connection: null, creatingConnection: false },
|
|
125
|
+
};
|
|
124
126
|
this.exchanges = {};
|
|
125
127
|
this.queues = {};
|
|
126
128
|
this.queueSetupPromises = {};
|
|
@@ -142,30 +144,31 @@ class RabbitMq {
|
|
|
142
144
|
});
|
|
143
145
|
}
|
|
144
146
|
}
|
|
145
|
-
async getConnection() {
|
|
147
|
+
async getConnection(connectionPurpose) {
|
|
146
148
|
return new Promise(async (resolve, reject) => {
|
|
149
|
+
const { connection, creatingConnection } = this.connectionsMap[connectionPurpose];
|
|
147
150
|
if (this.blockReconnect) {
|
|
148
151
|
debug('rabbit: block reconnect');
|
|
149
152
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
150
153
|
// @ts-ignore
|
|
151
154
|
return resolve();
|
|
152
155
|
}
|
|
153
|
-
if (
|
|
154
|
-
if (this.options?.disableReconnect ||
|
|
156
|
+
if (connection !== null) {
|
|
157
|
+
if (this.options?.disableReconnect || connection?.isConnected()) {
|
|
155
158
|
debug('rabbit: connection - is connected');
|
|
156
159
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
157
160
|
// @ts-ignore
|
|
158
|
-
return resolve(
|
|
161
|
+
return resolve(connection);
|
|
159
162
|
}
|
|
160
163
|
debug('rabbit: connection - reconnecting');
|
|
161
164
|
}
|
|
162
|
-
if (
|
|
165
|
+
if (creatingConnection) {
|
|
163
166
|
debug('rabbit: creating connection emi');
|
|
164
167
|
this.em.once(consts_1.CONNECTION_CREATED_CONST, resolve);
|
|
165
168
|
this.em.once(consts_1.CONNECTION_FAILED_CONST, reject);
|
|
166
169
|
return;
|
|
167
170
|
}
|
|
168
|
-
this.creatingConnection = true;
|
|
171
|
+
this.connectionsMap[connectionPurpose].creatingConnection = true;
|
|
169
172
|
let isResolved = false;
|
|
170
173
|
// It is import to use it as a function and not as a variable
|
|
171
174
|
// because of k8s changes the env variables
|
|
@@ -178,11 +181,12 @@ class RabbitMq {
|
|
|
178
181
|
return [`amqp://${userName}:${password}@${host}?heartbeat=${HEARTBEAT}`];
|
|
179
182
|
};
|
|
180
183
|
const defaultUrls = findServers();
|
|
181
|
-
const
|
|
184
|
+
const newConnection = await (0, amqp_connection_manager_1.connect)(defaultUrls, {
|
|
182
185
|
findServers,
|
|
183
186
|
});
|
|
184
|
-
this.connection =
|
|
185
|
-
|
|
187
|
+
this.connectionsMap[connectionPurpose].connection = newConnection;
|
|
188
|
+
logger_1.default.info(`rabbit: created new connection ${connectionPurpose}`);
|
|
189
|
+
newConnection.on('error', (err) => {
|
|
186
190
|
logger_1.default.error('rabbit: connection error', { err });
|
|
187
191
|
if (!isResolved) {
|
|
188
192
|
isResolved = true;
|
|
@@ -190,7 +194,7 @@ class RabbitMq {
|
|
|
190
194
|
this.em.emit(consts_1.CONNECTION_FAILED_CONST, err);
|
|
191
195
|
}
|
|
192
196
|
});
|
|
193
|
-
|
|
197
|
+
newConnection.on('connectFailed', (err) => {
|
|
194
198
|
this.consumersTags = [];
|
|
195
199
|
logger_1.default.error('rabbit: connection connectFailed', { err });
|
|
196
200
|
if (!isResolved) {
|
|
@@ -199,8 +203,7 @@ class RabbitMq {
|
|
|
199
203
|
this.em.emit(consts_1.CONNECTION_FAILED_CONST, err);
|
|
200
204
|
}
|
|
201
205
|
});
|
|
202
|
-
|
|
203
|
-
// this.channel = null;
|
|
206
|
+
newConnection.on('disconnect', ({ err }) => {
|
|
204
207
|
this.consumersTags = [];
|
|
205
208
|
debug('rabbit: connection closed');
|
|
206
209
|
if (this.options?.disableReconnect) {
|
|
@@ -211,24 +214,26 @@ class RabbitMq {
|
|
|
211
214
|
logger_1.default.error(`${this.RECONNECT_MSG}${err && ` - ${err}`}`);
|
|
212
215
|
}
|
|
213
216
|
});
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
this.
|
|
217
|
-
this.em.emit(consts_1.CONNECTION_CREATED_CONST, connection);
|
|
217
|
+
newConnection.once('connect', async () => {
|
|
218
|
+
this.connectionsMap[connectionPurpose].creatingConnection = false;
|
|
219
|
+
this.em.emit(consts_1.CONNECTION_CREATED_CONST, newConnection);
|
|
218
220
|
isResolved = true;
|
|
219
|
-
resolve(
|
|
221
|
+
resolve(newConnection);
|
|
220
222
|
});
|
|
221
223
|
});
|
|
222
224
|
}
|
|
223
|
-
async getNewChannel({ name = (0, utils_1.rand)().toString(), onClose = null, options = {}
|
|
225
|
+
async getNewChannel({ name = (0, utils_1.rand)().toString(), onClose = null, options = {}, connectionPurpose = consts_1.ConnectionPurpose.Consume, }) {
|
|
224
226
|
let connection;
|
|
225
227
|
try {
|
|
226
|
-
connection = await this.getConnection();
|
|
228
|
+
connection = await this.getConnection(connectionPurpose);
|
|
227
229
|
}
|
|
228
230
|
catch (e) {
|
|
229
231
|
logger_1.default.error(`rabbit: error on get connection for new channel ${name} `, { e });
|
|
230
232
|
throw e;
|
|
231
233
|
}
|
|
234
|
+
if (!connection) {
|
|
235
|
+
throw new Error(`rabbit: couldnt get connection for new channel ${name}`);
|
|
236
|
+
}
|
|
232
237
|
const channel = connection.createChannel({ ...options });
|
|
233
238
|
(0, events_1.once)(channel, 'close').then((args) => {
|
|
234
239
|
logger_1.default.error(`rabbit: channel ${name} closed`);
|
|
@@ -244,14 +249,16 @@ class RabbitMq {
|
|
|
244
249
|
throw err;
|
|
245
250
|
}
|
|
246
251
|
}
|
|
247
|
-
async assertChannel({ force = false
|
|
252
|
+
async assertChannel({ force = false, connectionPurpose = consts_1.ConnectionPurpose.Consume }) {
|
|
253
|
+
debug('rabbit: start assert channel', { connectionPurpose, publishPromise: this.publishChannelSetupPromise, channel: this.channel });
|
|
248
254
|
if (!this.publishChannelSetupPromise) {
|
|
249
255
|
this.publishChannelSetupPromise = new Promise(async (resolve, reject) => {
|
|
250
256
|
if (this.channel && !force) {
|
|
251
257
|
return resolve(this.channel);
|
|
252
258
|
}
|
|
253
259
|
try {
|
|
254
|
-
const channel = await this.getNewChannel({});
|
|
260
|
+
const channel = await this.getNewChannel({ connectionPurpose });
|
|
261
|
+
debug('rabbit: new channel got', { connectionPurpose, channel: this.channel });
|
|
255
262
|
channel.on('error', (err) => {
|
|
256
263
|
logger_1.default.error('rabbit: channel error', { err });
|
|
257
264
|
});
|
|
@@ -265,8 +272,8 @@ class RabbitMq {
|
|
|
265
272
|
}
|
|
266
273
|
return this.publishChannelSetupPromise;
|
|
267
274
|
}
|
|
268
|
-
async assertExchange(exchangeName, options) {
|
|
269
|
-
const channel = await this.assertChannel();
|
|
275
|
+
async assertExchange(exchangeName, options = { connectionPurpose: consts_1.ConnectionPurpose.Consume }) {
|
|
276
|
+
const channel = await this.assertChannel({ connectionPurpose: options.connectionPurpose });
|
|
270
277
|
if (this.exchanges[exchangeName]) {
|
|
271
278
|
delete this.assertExchangePromises[exchangeName];
|
|
272
279
|
return this.exchanges[exchangeName];
|
|
@@ -278,29 +285,30 @@ class RabbitMq {
|
|
|
278
285
|
this.exchanges[exchangeName] = await this.assertExchangePromises[exchangeName];
|
|
279
286
|
return this.exchanges[exchangeName];
|
|
280
287
|
}
|
|
281
|
-
async getQueueLength(queue) {
|
|
288
|
+
async getQueueLength(queue, connectionPurpose = consts_1.ConnectionPurpose.Consume) {
|
|
282
289
|
RabbitMq.validateName('queue', queue);
|
|
290
|
+
const { connection } = this.connectionsMap[connectionPurpose];
|
|
283
291
|
const { channel } = this;
|
|
284
292
|
if (!channel) {
|
|
285
293
|
throw new Error('channel is not defined');
|
|
286
294
|
}
|
|
287
|
-
debug('rabbit: getting queue length', { queue, connected:
|
|
295
|
+
debug('rabbit: getting queue length', { queue, connected: connection?.isConnected() });
|
|
288
296
|
return channel?.checkQueue(queue);
|
|
289
297
|
}
|
|
290
|
-
async deleteQueue(queue) {
|
|
298
|
+
async deleteQueue(queue, connectionPurpose) {
|
|
291
299
|
RabbitMq.validateName('queue', queue);
|
|
292
|
-
const channel = await this.assertChannel();
|
|
300
|
+
const channel = await this.assertChannel({ connectionPurpose });
|
|
293
301
|
logger_1.default.info('rabbit: deleting queue', { queue });
|
|
294
302
|
const deleteQueueRes = await channel.deleteQueue(queue);
|
|
295
303
|
debug('queue deleted', deleteQueueRes);
|
|
296
304
|
return deleteQueueRes;
|
|
297
305
|
}
|
|
298
|
-
async bindQueue(queue, exchange) {
|
|
299
|
-
const channel = await this.assertChannel();
|
|
306
|
+
async bindQueue(queue, exchange, connectionPurpose) {
|
|
307
|
+
const channel = await this.assertChannel({ connectionPurpose });
|
|
300
308
|
await channel.addSetup((setupChannel) => setupChannel.bindQueue(queue, exchange, ''));
|
|
301
309
|
return channel.bindQueue(queue, exchange, '');
|
|
302
310
|
}
|
|
303
|
-
async setupQueue(queueName, options) {
|
|
311
|
+
async setupQueue(queueName, connectionPurpose, options) {
|
|
304
312
|
let queue;
|
|
305
313
|
const shouldUseQuorum = RabbitMq.shouldUseQuorum(queueName);
|
|
306
314
|
const localeOptions = {
|
|
@@ -313,7 +321,7 @@ class RabbitMq {
|
|
|
313
321
|
},
|
|
314
322
|
};
|
|
315
323
|
try {
|
|
316
|
-
const channel = await this.assertChannel();
|
|
324
|
+
const channel = await this.assertChannel({ connectionPurpose });
|
|
317
325
|
debug('assertQueue->channel.addSetup', { queueName });
|
|
318
326
|
await channel.addSetup((setupChannel) => setupChannel.assertQueue(queueName, localeOptions));
|
|
319
327
|
debug('assertQueue->channel.assertQueue', { queueName });
|
|
@@ -323,8 +331,8 @@ class RabbitMq {
|
|
|
323
331
|
logger_1.default.error('rabbit: assertQueue error', { queueName, options, error: e });
|
|
324
332
|
if (!this.options?.dontRetryAssert) {
|
|
325
333
|
debug('retrying assertQueue', { queueName });
|
|
326
|
-
const channel = await this.assertChannel({ force: true });
|
|
327
|
-
await this.deleteQueue(queueName);
|
|
334
|
+
const channel = await this.assertChannel({ force: true, connectionPurpose });
|
|
335
|
+
await this.deleteQueue(queueName, connectionPurpose);
|
|
328
336
|
debug('retrying assertQueue->channel.addSetup', { queueName });
|
|
329
337
|
await channel.addSetup((setupChannel) => setupChannel.assertQueue(queueName, localeOptions));
|
|
330
338
|
debug('retrying assertQueue->channel.assertQueue', { queueName });
|
|
@@ -348,7 +356,8 @@ class RabbitMq {
|
|
|
348
356
|
}
|
|
349
357
|
return false;
|
|
350
358
|
}
|
|
351
|
-
async assertQueue(queueName, options) {
|
|
359
|
+
async assertQueue(queueName, connectionPurpose, options) {
|
|
360
|
+
debug('rabbit: start assert queue', { connectionPurpose, queueName });
|
|
352
361
|
RabbitMq.validateName('queue', queueName);
|
|
353
362
|
if (this.queues[queueName]) {
|
|
354
363
|
delete this.queueSetupPromises[queueName];
|
|
@@ -357,7 +366,8 @@ class RabbitMq {
|
|
|
357
366
|
if (this.queueSetupPromises[queueName]) {
|
|
358
367
|
return this.queueSetupPromises[queueName];
|
|
359
368
|
}
|
|
360
|
-
this.queueSetupPromises[queueName] = this.setupQueue(queueName, options);
|
|
369
|
+
this.queueSetupPromises[queueName] = this.setupQueue(queueName, connectionPurpose, options);
|
|
370
|
+
debug('rabbit: done assert queue', { connectionPurpose, queueName });
|
|
361
371
|
return this.queueSetupPromises[queueName];
|
|
362
372
|
}
|
|
363
373
|
saveConsumer(queue, callback, options) {
|
|
@@ -400,9 +410,9 @@ class RabbitMq {
|
|
|
400
410
|
}
|
|
401
411
|
logger_1.default.info(`rabbit: Consuming with lock from queue ${queue} with lockTimeout: ${lockTimeout}ms`);
|
|
402
412
|
}
|
|
403
|
-
const channel = await this.getNewChannel({});
|
|
413
|
+
const channel = await this.getNewChannel({ connectionPurpose: consts_1.ConnectionPurpose.Consume });
|
|
404
414
|
return channel.addSetup(async (confirmChannel) => {
|
|
405
|
-
const q = await this.assertQueue(queue, optionsWithDefaults);
|
|
415
|
+
const q = await this.assertQueue(queue, consts_1.ConnectionPurpose.Consume, optionsWithDefaults);
|
|
406
416
|
await confirmChannel.prefetch(limit, false);
|
|
407
417
|
const { consumerTag } = await confirmChannel.consume(queue, async (msg) => {
|
|
408
418
|
if (!msg) {
|
|
@@ -484,12 +494,12 @@ class RabbitMq {
|
|
|
484
494
|
RabbitMq.validateName('queue', queue);
|
|
485
495
|
const { limit, deadMessageTtl } = optionsWithDefaults;
|
|
486
496
|
await this.saveConsumer(queue, callback, options);
|
|
487
|
-
const channel = await this.getNewChannel({ name: `consume-exchange
|
|
497
|
+
const channel = await this.getNewChannel({ name: `consume - exchange - ${exchange} -queue - ${queue}` });
|
|
488
498
|
return channel.addSetup(async (c) => {
|
|
489
499
|
const assertExchange = await (0, utils_1.assertExchangeFanout)(c, exchange);
|
|
490
500
|
await c.assertQueue(queue);
|
|
491
501
|
this.exchanges[exchange] = assertExchange;
|
|
492
|
-
await c.prefetch(limit,
|
|
502
|
+
await c.prefetch(limit, true);
|
|
493
503
|
return Promise.all([
|
|
494
504
|
c.bindQueue(queue, exchange, ''),
|
|
495
505
|
this.consume(queue, callback, options),
|
|
@@ -499,14 +509,14 @@ class RabbitMq {
|
|
|
499
509
|
async publish(exchange, content, customHeaders) {
|
|
500
510
|
return (0, utils_1.wrapSetImmediate)(async () => {
|
|
501
511
|
RabbitMq.validateName('exchange', exchange);
|
|
502
|
-
const channel = await this.assertChannel();
|
|
503
|
-
await this.assertExchange(exchange);
|
|
512
|
+
const channel = await this.assertChannel({ connectionPurpose: consts_1.ConnectionPurpose.Publish });
|
|
513
|
+
await this.assertExchange(exchange, { connectionPurpose: consts_1.ConnectionPurpose.Publish });
|
|
504
514
|
await channel.publish(exchange, '', Buffer.from(JSON.stringify(content)), RabbitMq.getPublishOptions(customHeaders));
|
|
505
515
|
});
|
|
506
516
|
}
|
|
507
517
|
async sendToQueue(queue, content, options, customHeaders) {
|
|
508
518
|
try {
|
|
509
|
-
await this.assertChannel();
|
|
519
|
+
await this.assertChannel({ connectionPurpose: consts_1.ConnectionPurpose.Publish });
|
|
510
520
|
}
|
|
511
521
|
catch (e) {
|
|
512
522
|
logger_1.default.error(`rabbit sendToQueue: failed to send assert channel when sending to queue ${queue}`, { e });
|
|
@@ -514,7 +524,7 @@ class RabbitMq {
|
|
|
514
524
|
}
|
|
515
525
|
try {
|
|
516
526
|
RabbitMq.validateName('queue', queue);
|
|
517
|
-
await this.assertQueue(queue, options);
|
|
527
|
+
await this.assertQueue(queue, consts_1.ConnectionPurpose.Publish, options);
|
|
518
528
|
}
|
|
519
529
|
catch (e) {
|
|
520
530
|
logger_1.default.error(`rabbit sendToQueue: failed to assert queue ${queue}`, { e });
|
|
@@ -526,31 +536,36 @@ class RabbitMq {
|
|
|
526
536
|
return res;
|
|
527
537
|
}
|
|
528
538
|
catch (e) {
|
|
529
|
-
|
|
530
|
-
logger_1.default.error(`rabbit sendToQueue: failed to send to queue ${queue}, isConnected: ${isConnected}`, { e });
|
|
539
|
+
logger_1.default.error(`rabbit sendToQueue: failed to send to queue ${queue}`, { e });
|
|
531
540
|
throw e;
|
|
532
541
|
}
|
|
533
542
|
}
|
|
534
543
|
async isConnected() {
|
|
535
|
-
const
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
544
|
+
const isEachConnectionConnected = await Promise.all(Object.entries(this.connectionsMap).map(async ([connectionPurpose, connectionData]) => {
|
|
545
|
+
const { connection } = connectionData;
|
|
546
|
+
if (connectionPurpose === consts_1.ConnectionPurpose.Publish && !connection) {
|
|
547
|
+
return true; // The connection hasn't been initialized yet, as no messages have been sent through it
|
|
548
|
+
}
|
|
549
|
+
const isConnected = connection?.isConnected();
|
|
550
|
+
if (!isConnected) {
|
|
551
|
+
logger_1.default.error('rabbit: isConnected - false');
|
|
552
|
+
return false;
|
|
553
|
+
}
|
|
554
|
+
const channel = await this.assertChannel({ connectionPurpose: connectionPurpose });
|
|
555
|
+
try {
|
|
556
|
+
await Promise.all([
|
|
557
|
+
channel.waitForConnect(),
|
|
558
|
+
...this.consumers.map((c) => channel.checkQueue(c.queue)),
|
|
559
|
+
]);
|
|
560
|
+
}
|
|
561
|
+
catch (e) {
|
|
562
|
+
logger_1.default.error('rabbit: isConnected - false');
|
|
563
|
+
return false;
|
|
564
|
+
}
|
|
565
|
+
logger_1.default.info('rabbit: isConnected - true');
|
|
566
|
+
return true;
|
|
567
|
+
}));
|
|
568
|
+
return isEachConnectionConnected.every((isConnected) => isConnected === true);
|
|
554
569
|
}
|
|
555
570
|
async gracefulShutdown(signal) {
|
|
556
571
|
const tagsNumber = this.consumersTags.length;
|
package/dist/lib/consts.d.ts
CHANGED
package/dist/lib/consts.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.DEFAULT_OPTIONS = exports.CONNECTION_FAILED_CONST = exports.CONNECTION_CREATED_CONST = exports.DEFAULT_USE_CONSUME_WITH_LOCK = exports.USER_OBJECT = exports.AUTOMATION_ID_HEADER = exports.USER_TRACING_HEADER = exports.TRACING_HEADER = exports.RETRY_HEADER = exports.DEFAULT_LOCK_TIMEOUT = exports.DEFAULT_DEAD_TTL_TWO_DAYS = void 0;
|
|
3
|
+
exports.ConnectionPurpose = exports.DEFAULT_OPTIONS = exports.CONNECTION_FAILED_CONST = exports.CONNECTION_CREATED_CONST = exports.DEFAULT_USE_CONSUME_WITH_LOCK = exports.USER_OBJECT = exports.AUTOMATION_ID_HEADER = exports.USER_TRACING_HEADER = exports.TRACING_HEADER = exports.RETRY_HEADER = exports.DEFAULT_LOCK_TIMEOUT = exports.DEFAULT_DEAD_TTL_TWO_DAYS = void 0;
|
|
4
4
|
exports.DEFAULT_DEAD_TTL_TWO_DAYS = 60000 * 60 * 12;
|
|
5
5
|
exports.DEFAULT_LOCK_TIMEOUT = 1000 * 5;
|
|
6
6
|
exports.RETRY_HEADER = 'x-retry-count';
|
|
@@ -20,3 +20,8 @@ exports.DEFAULT_OPTIONS = {
|
|
|
20
20
|
auditContext: null,
|
|
21
21
|
enableRabbitTrace: false,
|
|
22
22
|
};
|
|
23
|
+
var ConnectionPurpose;
|
|
24
|
+
(function (ConnectionPurpose) {
|
|
25
|
+
ConnectionPurpose["Consume"] = "consume";
|
|
26
|
+
ConnectionPurpose["Publish"] = "publish";
|
|
27
|
+
})(ConnectionPurpose = exports.ConnectionPurpose || (exports.ConnectionPurpose = {}));
|
package/dist/lib/types.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { AmqpConnectionManager } from 'amqp-connection-manager';
|
|
1
2
|
import { ConsumeMessage, Options, Replies } from 'amqplib';
|
|
2
3
|
export interface ExchangesCache {
|
|
3
4
|
[key: string]: any;
|
|
@@ -41,3 +42,7 @@ export type AfConsumer = {
|
|
|
41
42
|
options: ConsumeOptions | undefined;
|
|
42
43
|
};
|
|
43
44
|
export declare const CONSUMER_DEFAULT_OPTIONS: Options.Consume;
|
|
45
|
+
export type ConnectionData = {
|
|
46
|
+
connection: AmqpConnectionManager | null;
|
|
47
|
+
creatingConnection: boolean;
|
|
48
|
+
};
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
AUTOMATION_ID_HEADER,
|
|
23
23
|
CONNECTION_CREATED_CONST,
|
|
24
24
|
CONNECTION_FAILED_CONST,
|
|
25
|
+
ConnectionPurpose,
|
|
25
26
|
DEFAULT_LOCK_TIMEOUT,
|
|
26
27
|
DEFAULT_OPTIONS,
|
|
27
28
|
RETRY_HEADER,
|
|
@@ -40,13 +41,13 @@ import {
|
|
|
40
41
|
CONSUMER_DEFAULT_OPTIONS,
|
|
41
42
|
QueueSetupPromisesDictionary,
|
|
42
43
|
AssertExchangePromisesDictionary,
|
|
44
|
+
ConnectionData,
|
|
43
45
|
} from './lib/types';
|
|
44
46
|
|
|
45
47
|
// const debug = nodeDebug('af-rabbitmq')
|
|
46
48
|
const debug = logger.debug.bind(logger);
|
|
47
49
|
|
|
48
50
|
const PUBLISH_TIMEOUT = 1000 * 10;
|
|
49
|
-
|
|
50
51
|
export interface IAfRabbitMq {
|
|
51
52
|
ack: any;
|
|
52
53
|
nack: any;
|
|
@@ -86,11 +87,13 @@ type newChannelOpts = {
|
|
|
86
87
|
name?: string;
|
|
87
88
|
onClose?: null | ((args: any | null) => void);
|
|
88
89
|
options?: CreateChannelOpts | undefined;
|
|
90
|
+
connectionPurpose?: ConnectionPurpose,
|
|
89
91
|
};
|
|
90
92
|
|
|
91
93
|
type assertChannelOpts = {
|
|
92
94
|
channelName?: string;
|
|
93
95
|
force?: boolean;
|
|
96
|
+
connectionPurpose?: ConnectionPurpose;
|
|
94
97
|
}
|
|
95
98
|
|
|
96
99
|
type AfConsumer = {
|
|
@@ -102,7 +105,7 @@ type AfConsumer = {
|
|
|
102
105
|
const HEARTBEAT = '60';
|
|
103
106
|
|
|
104
107
|
class RabbitMq implements IAfRabbitMq {
|
|
105
|
-
static parseMsg(msg: any)
|
|
108
|
+
static parseMsg(msg: any): any {
|
|
106
109
|
let { content } = msg;
|
|
107
110
|
content = content.toString();
|
|
108
111
|
|
|
@@ -147,14 +150,15 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
147
150
|
|
|
148
151
|
publishChannelSetupPromise: Promise<ChannelWrapper> | null;
|
|
149
152
|
|
|
150
|
-
blockReconnect: boolean | null | undefined
|
|
153
|
+
blockReconnect: boolean | null | undefined;
|
|
151
154
|
|
|
152
|
-
|
|
155
|
+
connectionsMap: {
|
|
156
|
+
[ConnectionPurpose.Consume]: ConnectionData;
|
|
157
|
+
[ConnectionPurpose.Publish]: ConnectionData;
|
|
158
|
+
};
|
|
153
159
|
|
|
154
160
|
em: EventEmitter;
|
|
155
161
|
|
|
156
|
-
creatingConnection: boolean;
|
|
157
|
-
|
|
158
162
|
exchanges: ExchangesCache;
|
|
159
163
|
|
|
160
164
|
queues: QueuesCache;
|
|
@@ -178,8 +182,10 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
178
182
|
this.em = new EventEmitter();
|
|
179
183
|
this.channel = null;
|
|
180
184
|
this.publishChannelSetupPromise = null;
|
|
181
|
-
this.
|
|
182
|
-
|
|
185
|
+
this.connectionsMap = {
|
|
186
|
+
[ConnectionPurpose.Consume]: { connection: null, creatingConnection: false },
|
|
187
|
+
[ConnectionPurpose.Publish]: { connection: null, creatingConnection: false },
|
|
188
|
+
};
|
|
183
189
|
this.exchanges = {};
|
|
184
190
|
this.queues = {};
|
|
185
191
|
this.queueSetupPromises = {};
|
|
@@ -216,7 +222,7 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
216
222
|
return false;
|
|
217
223
|
}
|
|
218
224
|
|
|
219
|
-
public ack = (channel: ConfirmChannel, msg: ConsumeMessageOrNull, shouldUpdateRedisTimestamp = false, releaseLock = null) => async (userMsg: ConsumeMessage)
|
|
225
|
+
public ack = (channel: ConfirmChannel, msg: ConsumeMessageOrNull, shouldUpdateRedisTimestamp = false, releaseLock = null) => async (userMsg: ConsumeMessage): Promise<any> => {
|
|
220
226
|
if (msg) {
|
|
221
227
|
debug('rabbit acking message', { deliveryTag: msg.fields.deliveryTag });
|
|
222
228
|
await channel.ack(msg);
|
|
@@ -242,8 +248,8 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
242
248
|
userMsg: ConsumeMessageOrNull,
|
|
243
249
|
{
|
|
244
250
|
skipRetry = false,
|
|
245
|
-
}: NackOptions = {
|
|
246
|
-
)
|
|
251
|
+
}: NackOptions = {},
|
|
252
|
+
): Promise<any> => {
|
|
247
253
|
await this.unlockRedisIfNeeded(releaseLock);
|
|
248
254
|
if (channel && msg) {
|
|
249
255
|
if (
|
|
@@ -277,30 +283,31 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
277
283
|
}
|
|
278
284
|
}
|
|
279
285
|
|
|
280
|
-
async getConnection() {
|
|
281
|
-
return new Promise<AmqpConnectionManager>(async (resolve, reject) => {
|
|
286
|
+
async getConnection(connectionPurpose: ConnectionPurpose) {
|
|
287
|
+
return new Promise<AmqpConnectionManager | undefined | null>(async (resolve, reject) => {
|
|
288
|
+
const { connection, creatingConnection } = this.connectionsMap[connectionPurpose];
|
|
282
289
|
if (this.blockReconnect) {
|
|
283
290
|
debug('rabbit: block reconnect');
|
|
284
291
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
285
292
|
// @ts-ignore
|
|
286
293
|
return resolve();
|
|
287
294
|
}
|
|
288
|
-
if (
|
|
289
|
-
if (this.options?.disableReconnect ||
|
|
295
|
+
if (connection !== null) {
|
|
296
|
+
if (this.options?.disableReconnect || connection?.isConnected()) {
|
|
290
297
|
debug('rabbit: connection - is connected');
|
|
291
298
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
292
299
|
// @ts-ignore
|
|
293
|
-
return resolve(
|
|
300
|
+
return resolve(connection);
|
|
294
301
|
}
|
|
295
302
|
debug('rabbit: connection - reconnecting');
|
|
296
303
|
}
|
|
297
|
-
if (
|
|
304
|
+
if (creatingConnection) {
|
|
298
305
|
debug('rabbit: creating connection emi');
|
|
299
306
|
this.em.once(CONNECTION_CREATED_CONST, resolve);
|
|
300
307
|
this.em.once(CONNECTION_FAILED_CONST, reject);
|
|
301
308
|
return;
|
|
302
309
|
}
|
|
303
|
-
this.creatingConnection = true;
|
|
310
|
+
this.connectionsMap[connectionPurpose].creatingConnection = true;
|
|
304
311
|
let isResolved = false;
|
|
305
312
|
|
|
306
313
|
// It is import to use it as a function and not as a variable
|
|
@@ -317,12 +324,14 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
317
324
|
};
|
|
318
325
|
|
|
319
326
|
const defaultUrls = findServers();
|
|
320
|
-
const
|
|
327
|
+
const newConnection: AmqpConnectionManager = await connect(defaultUrls, {
|
|
321
328
|
findServers,
|
|
322
329
|
});
|
|
323
330
|
|
|
324
|
-
this.connection =
|
|
325
|
-
|
|
331
|
+
this.connectionsMap[connectionPurpose].connection = newConnection;
|
|
332
|
+
logger.info(`rabbit: created new connection ${connectionPurpose}`);
|
|
333
|
+
|
|
334
|
+
newConnection.on('error', (err) => {
|
|
326
335
|
logger.error('rabbit: connection error', { err });
|
|
327
336
|
if (!isResolved) {
|
|
328
337
|
isResolved = true;
|
|
@@ -331,7 +340,7 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
331
340
|
}
|
|
332
341
|
});
|
|
333
342
|
|
|
334
|
-
|
|
343
|
+
newConnection.on('connectFailed', (err) => {
|
|
335
344
|
this.consumersTags = [];
|
|
336
345
|
logger.error('rabbit: connection connectFailed', { err });
|
|
337
346
|
if (!isResolved) {
|
|
@@ -341,8 +350,7 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
341
350
|
}
|
|
342
351
|
});
|
|
343
352
|
|
|
344
|
-
|
|
345
|
-
// this.channel = null;
|
|
353
|
+
newConnection.on('disconnect', ({ err }) => {
|
|
346
354
|
this.consumersTags = [];
|
|
347
355
|
debug('rabbit: connection closed');
|
|
348
356
|
if (this.options?.disableReconnect) {
|
|
@@ -352,25 +360,28 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
352
360
|
logger.error(`${this.RECONNECT_MSG}${err && ` - ${err}`}`);
|
|
353
361
|
}
|
|
354
362
|
});
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
this.creatingConnection = false;
|
|
359
|
-
this.em.emit(CONNECTION_CREATED_CONST, connection);
|
|
363
|
+
newConnection.once('connect', async () => {
|
|
364
|
+
this.connectionsMap[connectionPurpose].creatingConnection = false;
|
|
365
|
+
this.em.emit(CONNECTION_CREATED_CONST, newConnection);
|
|
360
366
|
isResolved = true;
|
|
361
|
-
resolve(
|
|
367
|
+
resolve(newConnection);
|
|
362
368
|
});
|
|
363
369
|
});
|
|
364
370
|
}
|
|
365
371
|
|
|
366
|
-
async getNewChannel({
|
|
367
|
-
|
|
372
|
+
async getNewChannel({
|
|
373
|
+
name = rand().toString(), onClose = null, options = {}, connectionPurpose = ConnectionPurpose.Consume,
|
|
374
|
+
}: newChannelOpts): Promise<ChannelWrapper> {
|
|
375
|
+
let connection!: AmqpConnectionManager | undefined | null;
|
|
368
376
|
try {
|
|
369
|
-
connection = await this.getConnection();
|
|
377
|
+
connection = await this.getConnection(connectionPurpose);
|
|
370
378
|
} catch (e) {
|
|
371
379
|
logger.error(`rabbit: error on get connection for new channel ${name} `, { e });
|
|
372
380
|
throw e;
|
|
373
381
|
}
|
|
382
|
+
if (!connection) {
|
|
383
|
+
throw new Error(`rabbit: couldnt get connection for new channel ${name}`);
|
|
384
|
+
}
|
|
374
385
|
const channel = connection.createChannel({ ...options });
|
|
375
386
|
once(channel, 'close').then((args) => {
|
|
376
387
|
logger.error(`rabbit: channel ${name} closed`);
|
|
@@ -386,7 +397,8 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
386
397
|
}
|
|
387
398
|
}
|
|
388
399
|
|
|
389
|
-
async assertChannel({ force = false }
|
|
400
|
+
async assertChannel({ force = false, connectionPurpose = ConnectionPurpose.Consume }: assertChannelOpts): Promise<ChannelWrapper> {
|
|
401
|
+
debug('rabbit: start assert channel', { connectionPurpose, publishPromise: this.publishChannelSetupPromise, channel: this.channel });
|
|
390
402
|
if (!this.publishChannelSetupPromise) {
|
|
391
403
|
this.publishChannelSetupPromise = new Promise<ChannelWrapper>(async (resolve, reject) => {
|
|
392
404
|
if (this.channel && !force) {
|
|
@@ -394,7 +406,8 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
394
406
|
}
|
|
395
407
|
|
|
396
408
|
try {
|
|
397
|
-
const channel = await this.getNewChannel({});
|
|
409
|
+
const channel = await this.getNewChannel({ connectionPurpose });
|
|
410
|
+
debug('rabbit: new channel got', { connectionPurpose, channel: this.channel });
|
|
398
411
|
channel.on('error', (err) => {
|
|
399
412
|
logger.error('rabbit: channel error', { err });
|
|
400
413
|
});
|
|
@@ -408,9 +421,8 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
408
421
|
return this.publishChannelSetupPromise;
|
|
409
422
|
}
|
|
410
423
|
|
|
411
|
-
async assertExchange(exchangeName: string, options
|
|
412
|
-
const channel: ChannelWrapper = await this.assertChannel();
|
|
413
|
-
|
|
424
|
+
async assertExchange(exchangeName: string, options: any = { connectionPurpose: ConnectionPurpose.Consume }) {
|
|
425
|
+
const channel: ChannelWrapper = await this.assertChannel({ connectionPurpose: options.connectionPurpose });
|
|
414
426
|
if (this.exchanges[exchangeName]) {
|
|
415
427
|
delete this.assertExchangePromises[exchangeName];
|
|
416
428
|
return this.exchanges[exchangeName];
|
|
@@ -425,32 +437,33 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
425
437
|
return this.exchanges[exchangeName];
|
|
426
438
|
}
|
|
427
439
|
|
|
428
|
-
async getQueueLength(queue: string) {
|
|
440
|
+
async getQueueLength(queue: string, connectionPurpose: ConnectionPurpose = ConnectionPurpose.Consume): Promise<Replies.AssertQueue> {
|
|
429
441
|
RabbitMq.validateName('queue', queue);
|
|
442
|
+
const { connection } = this.connectionsMap[connectionPurpose];
|
|
430
443
|
const { channel } = this;
|
|
431
444
|
if (!channel) {
|
|
432
445
|
throw new Error('channel is not defined');
|
|
433
446
|
}
|
|
434
|
-
debug('rabbit: getting queue length', { queue, connected:
|
|
447
|
+
debug('rabbit: getting queue length', { queue, connected: connection?.isConnected() });
|
|
435
448
|
return channel?.checkQueue(queue);
|
|
436
449
|
}
|
|
437
450
|
|
|
438
|
-
private async deleteQueue(queue: string) {
|
|
451
|
+
private async deleteQueue(queue: string, connectionPurpose: ConnectionPurpose) {
|
|
439
452
|
RabbitMq.validateName('queue', queue);
|
|
440
|
-
const channel: ChannelWrapper = await this.assertChannel();
|
|
453
|
+
const channel: ChannelWrapper = await this.assertChannel({ connectionPurpose });
|
|
441
454
|
logger.info('rabbit: deleting queue', { queue });
|
|
442
455
|
const deleteQueueRes = await channel.deleteQueue(queue);
|
|
443
456
|
debug('queue deleted', deleteQueueRes);
|
|
444
457
|
return deleteQueueRes;
|
|
445
458
|
}
|
|
446
459
|
|
|
447
|
-
async bindQueue(queue: string, exchange: string) {
|
|
448
|
-
const channel: ChannelWrapper = await this.assertChannel();
|
|
460
|
+
async bindQueue(queue: string, exchange: string, connectionPurpose: ConnectionPurpose) {
|
|
461
|
+
const channel: ChannelWrapper = await this.assertChannel({ connectionPurpose });
|
|
449
462
|
await channel.addSetup((setupChannel: ConfirmChannel) => setupChannel.bindQueue(queue, exchange, ''));
|
|
450
463
|
return channel.bindQueue(queue, exchange, '');
|
|
451
464
|
}
|
|
452
465
|
|
|
453
|
-
async setupQueue(queueName: string, options?: Options.AssertQueue): Promise<Replies.AssertQueue> {
|
|
466
|
+
async setupQueue(queueName: string, connectionPurpose: ConnectionPurpose, options?: Options.AssertQueue): Promise<Replies.AssertQueue> {
|
|
454
467
|
let queue: Replies.AssertQueue;
|
|
455
468
|
const shouldUseQuorum = RabbitMq.shouldUseQuorum(queueName);
|
|
456
469
|
const localeOptions = {
|
|
@@ -463,7 +476,7 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
463
476
|
},
|
|
464
477
|
};
|
|
465
478
|
try {
|
|
466
|
-
const channel: ChannelWrapper = await this.assertChannel();
|
|
479
|
+
const channel: ChannelWrapper = await this.assertChannel({ connectionPurpose });
|
|
467
480
|
debug('assertQueue->channel.addSetup', { queueName });
|
|
468
481
|
await channel.addSetup((setupChannel: ConfirmChannel) => setupChannel.assertQueue(queueName, localeOptions));
|
|
469
482
|
debug('assertQueue->channel.assertQueue', { queueName });
|
|
@@ -472,8 +485,8 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
472
485
|
logger.error('rabbit: assertQueue error', { queueName, options, error: e });
|
|
473
486
|
if (!this.options?.dontRetryAssert) {
|
|
474
487
|
debug('retrying assertQueue', { queueName });
|
|
475
|
-
const channel = await this.assertChannel({ force: true });
|
|
476
|
-
await this.deleteQueue(queueName);
|
|
488
|
+
const channel = await this.assertChannel({ force: true, connectionPurpose });
|
|
489
|
+
await this.deleteQueue(queueName, connectionPurpose);
|
|
477
490
|
|
|
478
491
|
debug('retrying assertQueue->channel.addSetup', { queueName });
|
|
479
492
|
await channel.addSetup((setupChannel: ConfirmChannel) => setupChannel.assertQueue(queueName, localeOptions));
|
|
@@ -503,7 +516,8 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
503
516
|
return false;
|
|
504
517
|
}
|
|
505
518
|
|
|
506
|
-
async assertQueue(queueName: string, options?: Options.AssertQueue)
|
|
519
|
+
async assertQueue(queueName: string, connectionPurpose: ConnectionPurpose, options?: Options.AssertQueue) {
|
|
520
|
+
debug('rabbit: start assert queue', { connectionPurpose, queueName });
|
|
507
521
|
RabbitMq.validateName('queue', queueName);
|
|
508
522
|
if (this.queues[queueName]) {
|
|
509
523
|
delete this.queueSetupPromises[queueName];
|
|
@@ -514,12 +528,13 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
514
528
|
return this.queueSetupPromises[queueName];
|
|
515
529
|
}
|
|
516
530
|
|
|
517
|
-
this.queueSetupPromises[queueName] = this.setupQueue(queueName, options);
|
|
531
|
+
this.queueSetupPromises[queueName] = this.setupQueue(queueName, connectionPurpose, options);
|
|
532
|
+
debug('rabbit: done assert queue', { connectionPurpose, queueName });
|
|
518
533
|
return this.queueSetupPromises[queueName];
|
|
519
534
|
}
|
|
520
535
|
|
|
521
536
|
private saveConsumer(queue: string, callback: CallbackFunction, options: ConsumeOptions | undefined) {
|
|
522
|
-
const isConsumerExist
|
|
537
|
+
const isConsumerExist: boolean = this.consumers.some((consumer) => consumer.queue === queue);
|
|
523
538
|
if (!isConsumerExist) {
|
|
524
539
|
logger.info(`rabbit: consumer: ${queue} saved in consumer array`);
|
|
525
540
|
this.consumers.push({
|
|
@@ -568,9 +583,9 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
568
583
|
}
|
|
569
584
|
logger.info(`rabbit: Consuming with lock from queue ${queue} with lockTimeout: ${lockTimeout}ms`);
|
|
570
585
|
}
|
|
571
|
-
const channel = await this.getNewChannel({});
|
|
586
|
+
const channel = await this.getNewChannel({ connectionPurpose: ConnectionPurpose.Consume });
|
|
572
587
|
return channel.addSetup(async (confirmChannel: ConfirmChannel) => {
|
|
573
|
-
const q = await this.assertQueue(queue, optionsWithDefaults);
|
|
588
|
+
const q = await this.assertQueue(queue, ConnectionPurpose.Consume, optionsWithDefaults);
|
|
574
589
|
await confirmChannel.prefetch(limit, false);
|
|
575
590
|
const { consumerTag } = await confirmChannel.consume(
|
|
576
591
|
queue,
|
|
@@ -658,19 +673,19 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
658
673
|
});
|
|
659
674
|
}
|
|
660
675
|
|
|
661
|
-
async consumeFromExchange(queue: string, exchange: string, callback: CallbackFunction, options?: ConsumeOptions)
|
|
676
|
+
async consumeFromExchange(queue: string, exchange: string, callback: CallbackFunction, options?: ConsumeOptions): Promise<any> {
|
|
662
677
|
const optionsWithDefaults = { ...DEFAULT_OPTIONS, ...options };
|
|
663
678
|
RabbitMq.validateName('exchange', exchange);
|
|
664
679
|
RabbitMq.validateName('queue', queue);
|
|
665
680
|
const { limit, deadMessageTtl } = optionsWithDefaults;
|
|
666
681
|
await this.saveConsumer(queue, callback, options);
|
|
667
|
-
const channel: ChannelWrapper = await this.getNewChannel({ name: `consume-exchange
|
|
682
|
+
const channel: ChannelWrapper = await this.getNewChannel({ name: `consume - exchange - ${exchange} -queue - ${queue}` });
|
|
668
683
|
|
|
669
684
|
return channel.addSetup(async (c: ConfirmChannel) => {
|
|
670
685
|
const assertExchange = await assertExchangeFanout(c, exchange);
|
|
671
686
|
await c.assertQueue(queue);
|
|
672
687
|
this.exchanges[exchange] = assertExchange;
|
|
673
|
-
await c.prefetch(limit,
|
|
688
|
+
await c.prefetch(limit, true);
|
|
674
689
|
return Promise.all([
|
|
675
690
|
c.bindQueue(queue, exchange, ''),
|
|
676
691
|
this.consume(
|
|
@@ -682,11 +697,11 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
682
697
|
});
|
|
683
698
|
}
|
|
684
699
|
|
|
685
|
-
async publish(exchange: string, content: any, customHeaders?: any)
|
|
700
|
+
async publish(exchange: string, content: any, customHeaders?: any): Promise<boolean> {
|
|
686
701
|
return wrapSetImmediate(async () => {
|
|
687
702
|
RabbitMq.validateName('exchange', exchange);
|
|
688
|
-
const channel: ChannelWrapper = await this.assertChannel();
|
|
689
|
-
await this.assertExchange(exchange);
|
|
703
|
+
const channel: ChannelWrapper = await this.assertChannel({ connectionPurpose: ConnectionPurpose.Publish });
|
|
704
|
+
await this.assertExchange(exchange, { connectionPurpose: ConnectionPurpose.Publish });
|
|
690
705
|
await channel.publish(exchange, '',
|
|
691
706
|
Buffer.from(JSON.stringify(content)),
|
|
692
707
|
RabbitMq.getPublishOptions(customHeaders));
|
|
@@ -700,7 +715,7 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
700
715
|
customHeaders?: any,
|
|
701
716
|
): Promise<boolean | undefined> {
|
|
702
717
|
try {
|
|
703
|
-
await this.assertChannel();
|
|
718
|
+
await this.assertChannel({ connectionPurpose: ConnectionPurpose.Publish });
|
|
704
719
|
} catch (e) {
|
|
705
720
|
logger.error(`rabbit sendToQueue: failed to send assert channel when sending to queue ${queue}`, { e });
|
|
706
721
|
throw e;
|
|
@@ -708,7 +723,7 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
708
723
|
|
|
709
724
|
try {
|
|
710
725
|
RabbitMq.validateName('queue', queue);
|
|
711
|
-
await this.assertQueue(queue, options);
|
|
726
|
+
await this.assertQueue(queue, ConnectionPurpose.Publish, options);
|
|
712
727
|
} catch (e) {
|
|
713
728
|
logger.error(`rabbit sendToQueue: failed to assert queue ${queue}`, { e });
|
|
714
729
|
throw e;
|
|
@@ -721,34 +736,41 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
721
736
|
debug(`rabbit: sending to queue ${queue}`, { res });
|
|
722
737
|
return res;
|
|
723
738
|
} catch (e) {
|
|
724
|
-
|
|
725
|
-
logger.error(`rabbit sendToQueue: failed to send to queue ${queue}, isConnected: ${isConnected}`, { e });
|
|
739
|
+
logger.error(`rabbit sendToQueue: failed to send to queue ${queue}`, { e });
|
|
726
740
|
throw e;
|
|
727
741
|
}
|
|
728
742
|
}
|
|
729
743
|
|
|
730
|
-
async isConnected()
|
|
731
|
-
const
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
744
|
+
async isConnected(): Promise<boolean> {
|
|
745
|
+
const isEachConnectionConnected = await Promise.all(
|
|
746
|
+
Object.entries(this.connectionsMap).map(async ([connectionPurpose, connectionData]) => {
|
|
747
|
+
const { connection } = connectionData;
|
|
748
|
+
if (connectionPurpose === ConnectionPurpose.Publish && !connection) {
|
|
749
|
+
return true; // The connection hasn't been initialized yet, as no messages have been sent through it
|
|
750
|
+
}
|
|
751
|
+
const isConnected = connection?.isConnected();
|
|
752
|
+
if (!isConnected) {
|
|
753
|
+
logger.error('rabbit: isConnected - false');
|
|
754
|
+
return false;
|
|
755
|
+
}
|
|
756
|
+
const channel: any = await this.assertChannel({ connectionPurpose: connectionPurpose as ConnectionPurpose });
|
|
757
|
+
try {
|
|
758
|
+
await Promise.all([
|
|
759
|
+
channel.waitForConnect(),
|
|
760
|
+
...this.consumers.map((c: AfConsumer) => channel.checkQueue(c.queue)),
|
|
761
|
+
]);
|
|
762
|
+
} catch (e) {
|
|
763
|
+
logger.error('rabbit: isConnected - false');
|
|
764
|
+
return false;
|
|
765
|
+
}
|
|
766
|
+
logger.info('rabbit: isConnected - true');
|
|
767
|
+
return true;
|
|
768
|
+
}),
|
|
769
|
+
);
|
|
770
|
+
return isEachConnectionConnected.every((isConnected) => isConnected === true);
|
|
749
771
|
}
|
|
750
772
|
|
|
751
|
-
async gracefulShutdown(signal: string)
|
|
773
|
+
async gracefulShutdown(signal: string): Promise<void> {
|
|
752
774
|
const tagsNumber = this.consumersTags.length;
|
|
753
775
|
logger.info(`rabbit: [gracefully-shutdown] received ${signal}! canceling #${tagsNumber} tags...`);
|
|
754
776
|
const cancelTagPromises = this.consumersTags.map(([channel, tag]) => channel.cancel(tag));
|
package/src/lib/consts.ts
CHANGED
package/src/lib/types.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { AmqpConnectionManager } from 'amqp-connection-manager';
|
|
1
2
|
import { ConsumeMessage, Options, Replies } from 'amqplib';
|
|
2
3
|
|
|
3
4
|
export interface ExchangesCache {
|
|
@@ -59,3 +60,8 @@ export const CONSUMER_DEFAULT_OPTIONS: Options.Consume = {
|
|
|
59
60
|
[HA_PROMOTE_ON_SHUTDOWN]: 'always',
|
|
60
61
|
},
|
|
61
62
|
};
|
|
63
|
+
|
|
64
|
+
export type ConnectionData = {
|
|
65
|
+
connection: AmqpConnectionManager | null;
|
|
66
|
+
creatingConnection: boolean;
|
|
67
|
+
};
|