@autofleet/rabbit 3.2.23 → 3.2.24-beta.1
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 +77 -65
- 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 +101 -82
- 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,15 @@ 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 }) {
|
|
248
253
|
if (!this.publishChannelSetupPromise) {
|
|
249
254
|
this.publishChannelSetupPromise = new Promise(async (resolve, reject) => {
|
|
250
255
|
if (this.channel && !force) {
|
|
251
256
|
return resolve(this.channel);
|
|
252
257
|
}
|
|
253
258
|
try {
|
|
254
|
-
const channel = await this.getNewChannel({});
|
|
259
|
+
const channel = await this.getNewChannel({ connectionPurpose });
|
|
260
|
+
debug('rabbit: new channel got', { connectionPurpose, channel: this.channel });
|
|
255
261
|
channel.on('error', (err) => {
|
|
256
262
|
logger_1.default.error('rabbit: channel error', { err });
|
|
257
263
|
});
|
|
@@ -265,8 +271,8 @@ class RabbitMq {
|
|
|
265
271
|
}
|
|
266
272
|
return this.publishChannelSetupPromise;
|
|
267
273
|
}
|
|
268
|
-
async assertExchange(exchangeName, options) {
|
|
269
|
-
const channel = await this.assertChannel();
|
|
274
|
+
async assertExchange(exchangeName, options = { connectionPurpose: consts_1.ConnectionPurpose.Consume }) {
|
|
275
|
+
const channel = await this.assertChannel({ connectionPurpose: options.connectionPurpose });
|
|
270
276
|
if (this.exchanges[exchangeName]) {
|
|
271
277
|
delete this.assertExchangePromises[exchangeName];
|
|
272
278
|
return this.exchanges[exchangeName];
|
|
@@ -278,29 +284,30 @@ class RabbitMq {
|
|
|
278
284
|
this.exchanges[exchangeName] = await this.assertExchangePromises[exchangeName];
|
|
279
285
|
return this.exchanges[exchangeName];
|
|
280
286
|
}
|
|
281
|
-
async getQueueLength(queue) {
|
|
287
|
+
async getQueueLength(queue, connectionPurpose = consts_1.ConnectionPurpose.Consume) {
|
|
282
288
|
RabbitMq.validateName('queue', queue);
|
|
289
|
+
const { connection } = this.connectionsMap[connectionPurpose];
|
|
283
290
|
const { channel } = this;
|
|
284
291
|
if (!channel) {
|
|
285
292
|
throw new Error('channel is not defined');
|
|
286
293
|
}
|
|
287
|
-
debug('rabbit: getting queue length', { queue, connected:
|
|
294
|
+
debug('rabbit: getting queue length', { queue, connected: connection?.isConnected() });
|
|
288
295
|
return channel?.checkQueue(queue);
|
|
289
296
|
}
|
|
290
|
-
async deleteQueue(queue) {
|
|
297
|
+
async deleteQueue(queue, connectionPurpose) {
|
|
291
298
|
RabbitMq.validateName('queue', queue);
|
|
292
|
-
const channel = await this.assertChannel();
|
|
299
|
+
const channel = await this.assertChannel({ connectionPurpose });
|
|
293
300
|
logger_1.default.info('rabbit: deleting queue', { queue });
|
|
294
301
|
const deleteQueueRes = await channel.deleteQueue(queue);
|
|
295
302
|
debug('queue deleted', deleteQueueRes);
|
|
296
303
|
return deleteQueueRes;
|
|
297
304
|
}
|
|
298
|
-
async bindQueue(queue, exchange) {
|
|
299
|
-
const channel = await this.assertChannel();
|
|
305
|
+
async bindQueue(queue, exchange, connectionPurpose) {
|
|
306
|
+
const channel = await this.assertChannel({ connectionPurpose });
|
|
300
307
|
await channel.addSetup((setupChannel) => setupChannel.bindQueue(queue, exchange, ''));
|
|
301
308
|
return channel.bindQueue(queue, exchange, '');
|
|
302
309
|
}
|
|
303
|
-
async setupQueue(queueName, options) {
|
|
310
|
+
async setupQueue(queueName, connectionPurpose, options) {
|
|
304
311
|
let queue;
|
|
305
312
|
const shouldUseQuorum = RabbitMq.shouldUseQuorum(queueName);
|
|
306
313
|
const localeOptions = {
|
|
@@ -313,7 +320,7 @@ class RabbitMq {
|
|
|
313
320
|
},
|
|
314
321
|
};
|
|
315
322
|
try {
|
|
316
|
-
const channel = await this.assertChannel();
|
|
323
|
+
const channel = await this.assertChannel({ connectionPurpose });
|
|
317
324
|
debug('assertQueue->channel.addSetup', { queueName });
|
|
318
325
|
await channel.addSetup((setupChannel) => setupChannel.assertQueue(queueName, localeOptions));
|
|
319
326
|
debug('assertQueue->channel.assertQueue', { queueName });
|
|
@@ -323,8 +330,8 @@ class RabbitMq {
|
|
|
323
330
|
logger_1.default.error('rabbit: assertQueue error', { queueName, options, error: e });
|
|
324
331
|
if (!this.options?.dontRetryAssert) {
|
|
325
332
|
debug('retrying assertQueue', { queueName });
|
|
326
|
-
const channel = await this.assertChannel({ force: true });
|
|
327
|
-
await this.deleteQueue(queueName);
|
|
333
|
+
const channel = await this.assertChannel({ force: true, connectionPurpose });
|
|
334
|
+
await this.deleteQueue(queueName, connectionPurpose);
|
|
328
335
|
debug('retrying assertQueue->channel.addSetup', { queueName });
|
|
329
336
|
await channel.addSetup((setupChannel) => setupChannel.assertQueue(queueName, localeOptions));
|
|
330
337
|
debug('retrying assertQueue->channel.assertQueue', { queueName });
|
|
@@ -348,7 +355,7 @@ class RabbitMq {
|
|
|
348
355
|
}
|
|
349
356
|
return false;
|
|
350
357
|
}
|
|
351
|
-
async assertQueue(queueName, options) {
|
|
358
|
+
async assertQueue(queueName, connectionPurpose, options) {
|
|
352
359
|
RabbitMq.validateName('queue', queueName);
|
|
353
360
|
if (this.queues[queueName]) {
|
|
354
361
|
delete this.queueSetupPromises[queueName];
|
|
@@ -357,7 +364,7 @@ class RabbitMq {
|
|
|
357
364
|
if (this.queueSetupPromises[queueName]) {
|
|
358
365
|
return this.queueSetupPromises[queueName];
|
|
359
366
|
}
|
|
360
|
-
this.queueSetupPromises[queueName] = this.setupQueue(queueName, options);
|
|
367
|
+
this.queueSetupPromises[queueName] = this.setupQueue(queueName, connectionPurpose, options);
|
|
361
368
|
return this.queueSetupPromises[queueName];
|
|
362
369
|
}
|
|
363
370
|
saveConsumer(queue, callback, options) {
|
|
@@ -400,9 +407,9 @@ class RabbitMq {
|
|
|
400
407
|
}
|
|
401
408
|
logger_1.default.info(`rabbit: Consuming with lock from queue ${queue} with lockTimeout: ${lockTimeout}ms`);
|
|
402
409
|
}
|
|
403
|
-
const channel = await this.getNewChannel({});
|
|
410
|
+
const channel = await this.getNewChannel({ connectionPurpose: consts_1.ConnectionPurpose.Consume });
|
|
404
411
|
return channel.addSetup(async (confirmChannel) => {
|
|
405
|
-
const q = await this.assertQueue(queue, optionsWithDefaults);
|
|
412
|
+
const q = await this.assertQueue(queue, consts_1.ConnectionPurpose.Consume, optionsWithDefaults);
|
|
406
413
|
await confirmChannel.prefetch(limit, false);
|
|
407
414
|
const { consumerTag } = await confirmChannel.consume(queue, async (msg) => {
|
|
408
415
|
if (!msg) {
|
|
@@ -484,7 +491,7 @@ class RabbitMq {
|
|
|
484
491
|
RabbitMq.validateName('queue', queue);
|
|
485
492
|
const { limit, deadMessageTtl } = optionsWithDefaults;
|
|
486
493
|
await this.saveConsumer(queue, callback, options);
|
|
487
|
-
const channel = await this.getNewChannel({ name: `consume-exchange
|
|
494
|
+
const channel = await this.getNewChannel({ name: `consume - exchange - ${exchange} -queue - ${queue}` });
|
|
488
495
|
return channel.addSetup(async (c) => {
|
|
489
496
|
const assertExchange = await (0, utils_1.assertExchangeFanout)(c, exchange);
|
|
490
497
|
await c.assertQueue(queue);
|
|
@@ -499,14 +506,14 @@ class RabbitMq {
|
|
|
499
506
|
async publish(exchange, content, customHeaders) {
|
|
500
507
|
return (0, utils_1.wrapSetImmediate)(async () => {
|
|
501
508
|
RabbitMq.validateName('exchange', exchange);
|
|
502
|
-
const channel = await this.assertChannel();
|
|
503
|
-
await this.assertExchange(exchange);
|
|
509
|
+
const channel = await this.assertChannel({ connectionPurpose: consts_1.ConnectionPurpose.Publish });
|
|
510
|
+
await this.assertExchange(exchange, { connectionPurpose: consts_1.ConnectionPurpose.Publish });
|
|
504
511
|
await channel.publish(exchange, '', Buffer.from(JSON.stringify(content)), RabbitMq.getPublishOptions(customHeaders));
|
|
505
512
|
});
|
|
506
513
|
}
|
|
507
514
|
async sendToQueue(queue, content, options, customHeaders) {
|
|
508
515
|
try {
|
|
509
|
-
await this.assertChannel();
|
|
516
|
+
await this.assertChannel({ connectionPurpose: consts_1.ConnectionPurpose.Publish });
|
|
510
517
|
}
|
|
511
518
|
catch (e) {
|
|
512
519
|
logger_1.default.error(`rabbit sendToQueue: failed to send assert channel when sending to queue ${queue}`, { e });
|
|
@@ -514,7 +521,7 @@ class RabbitMq {
|
|
|
514
521
|
}
|
|
515
522
|
try {
|
|
516
523
|
RabbitMq.validateName('queue', queue);
|
|
517
|
-
await this.assertQueue(queue, options);
|
|
524
|
+
await this.assertQueue(queue, consts_1.ConnectionPurpose.Publish, options);
|
|
518
525
|
}
|
|
519
526
|
catch (e) {
|
|
520
527
|
logger_1.default.error(`rabbit sendToQueue: failed to assert queue ${queue}`, { e });
|
|
@@ -526,31 +533,36 @@ class RabbitMq {
|
|
|
526
533
|
return res;
|
|
527
534
|
}
|
|
528
535
|
catch (e) {
|
|
529
|
-
|
|
530
|
-
logger_1.default.error(`rabbit sendToQueue: failed to send to queue ${queue}, isConnected: ${isConnected}`, { e });
|
|
536
|
+
logger_1.default.error(`rabbit sendToQueue: failed to send to queue ${queue}`, { e });
|
|
531
537
|
throw e;
|
|
532
538
|
}
|
|
533
539
|
}
|
|
534
540
|
async isConnected() {
|
|
535
|
-
const
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
541
|
+
const isEachConnectionConnected = await Promise.all(Object.entries(this.connectionsMap).map(async ([connectionPurpose, connectionData]) => {
|
|
542
|
+
const { connection } = connectionData;
|
|
543
|
+
if (connectionPurpose === consts_1.ConnectionPurpose.Publish && !connection) {
|
|
544
|
+
return true; // The connection hasn't been initialized yet, as no messages have been sent through it
|
|
545
|
+
}
|
|
546
|
+
const isConnected = connection?.isConnected();
|
|
547
|
+
if (!isConnected) {
|
|
548
|
+
logger_1.default.error('rabbit: isConnected - false');
|
|
549
|
+
return false;
|
|
550
|
+
}
|
|
551
|
+
const channel = await this.assertChannel({ connectionPurpose: connectionPurpose });
|
|
552
|
+
try {
|
|
553
|
+
await Promise.all([
|
|
554
|
+
channel.waitForConnect(),
|
|
555
|
+
...this.consumers.map((c) => channel.checkQueue(c.queue)),
|
|
556
|
+
]);
|
|
557
|
+
}
|
|
558
|
+
catch (e) {
|
|
559
|
+
logger_1.default.error('rabbit: isConnected - false');
|
|
560
|
+
return false;
|
|
561
|
+
}
|
|
562
|
+
logger_1.default.info('rabbit: isConnected - true');
|
|
563
|
+
return true;
|
|
564
|
+
}));
|
|
565
|
+
return isEachConnectionConnected.every((isConnected) => isConnected === true);
|
|
554
566
|
}
|
|
555
567
|
async gracefulShutdown(signal) {
|
|
556
568
|
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,7 @@ 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> {
|
|
390
401
|
if (!this.publishChannelSetupPromise) {
|
|
391
402
|
this.publishChannelSetupPromise = new Promise<ChannelWrapper>(async (resolve, reject) => {
|
|
392
403
|
if (this.channel && !force) {
|
|
@@ -394,7 +405,8 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
394
405
|
}
|
|
395
406
|
|
|
396
407
|
try {
|
|
397
|
-
const channel = await this.getNewChannel({});
|
|
408
|
+
const channel = await this.getNewChannel({ connectionPurpose });
|
|
409
|
+
debug('rabbit: new channel got', { connectionPurpose, channel: this.channel });
|
|
398
410
|
channel.on('error', (err) => {
|
|
399
411
|
logger.error('rabbit: channel error', { err });
|
|
400
412
|
});
|
|
@@ -408,9 +420,8 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
408
420
|
return this.publishChannelSetupPromise;
|
|
409
421
|
}
|
|
410
422
|
|
|
411
|
-
async assertExchange(exchangeName: string, options
|
|
412
|
-
const channel: ChannelWrapper = await this.assertChannel();
|
|
413
|
-
|
|
423
|
+
async assertExchange(exchangeName: string, options: any = { connectionPurpose: ConnectionPurpose.Consume }) {
|
|
424
|
+
const channel: ChannelWrapper = await this.assertChannel({ connectionPurpose: options.connectionPurpose });
|
|
414
425
|
if (this.exchanges[exchangeName]) {
|
|
415
426
|
delete this.assertExchangePromises[exchangeName];
|
|
416
427
|
return this.exchanges[exchangeName];
|
|
@@ -425,32 +436,33 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
425
436
|
return this.exchanges[exchangeName];
|
|
426
437
|
}
|
|
427
438
|
|
|
428
|
-
async getQueueLength(queue: string) {
|
|
439
|
+
async getQueueLength(queue: string, connectionPurpose: ConnectionPurpose = ConnectionPurpose.Consume): Promise<Replies.AssertQueue> {
|
|
429
440
|
RabbitMq.validateName('queue', queue);
|
|
441
|
+
const { connection } = this.connectionsMap[connectionPurpose];
|
|
430
442
|
const { channel } = this;
|
|
431
443
|
if (!channel) {
|
|
432
444
|
throw new Error('channel is not defined');
|
|
433
445
|
}
|
|
434
|
-
debug('rabbit: getting queue length', { queue, connected:
|
|
446
|
+
debug('rabbit: getting queue length', { queue, connected: connection?.isConnected() });
|
|
435
447
|
return channel?.checkQueue(queue);
|
|
436
448
|
}
|
|
437
449
|
|
|
438
|
-
private async deleteQueue(queue: string) {
|
|
450
|
+
private async deleteQueue(queue: string, connectionPurpose: ConnectionPurpose) {
|
|
439
451
|
RabbitMq.validateName('queue', queue);
|
|
440
|
-
const channel: ChannelWrapper = await this.assertChannel();
|
|
452
|
+
const channel: ChannelWrapper = await this.assertChannel({ connectionPurpose });
|
|
441
453
|
logger.info('rabbit: deleting queue', { queue });
|
|
442
454
|
const deleteQueueRes = await channel.deleteQueue(queue);
|
|
443
455
|
debug('queue deleted', deleteQueueRes);
|
|
444
456
|
return deleteQueueRes;
|
|
445
457
|
}
|
|
446
458
|
|
|
447
|
-
async bindQueue(queue: string, exchange: string) {
|
|
448
|
-
const channel: ChannelWrapper = await this.assertChannel();
|
|
459
|
+
async bindQueue(queue: string, exchange: string, connectionPurpose: ConnectionPurpose) {
|
|
460
|
+
const channel: ChannelWrapper = await this.assertChannel({ connectionPurpose });
|
|
449
461
|
await channel.addSetup((setupChannel: ConfirmChannel) => setupChannel.bindQueue(queue, exchange, ''));
|
|
450
462
|
return channel.bindQueue(queue, exchange, '');
|
|
451
463
|
}
|
|
452
464
|
|
|
453
|
-
async setupQueue(queueName: string, options?: Options.AssertQueue): Promise<Replies.AssertQueue> {
|
|
465
|
+
async setupQueue(queueName: string, connectionPurpose: ConnectionPurpose, options?: Options.AssertQueue): Promise<Replies.AssertQueue> {
|
|
454
466
|
let queue: Replies.AssertQueue;
|
|
455
467
|
const shouldUseQuorum = RabbitMq.shouldUseQuorum(queueName);
|
|
456
468
|
const localeOptions = {
|
|
@@ -463,7 +475,7 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
463
475
|
},
|
|
464
476
|
};
|
|
465
477
|
try {
|
|
466
|
-
const channel: ChannelWrapper = await this.assertChannel();
|
|
478
|
+
const channel: ChannelWrapper = await this.assertChannel({ connectionPurpose });
|
|
467
479
|
debug('assertQueue->channel.addSetup', { queueName });
|
|
468
480
|
await channel.addSetup((setupChannel: ConfirmChannel) => setupChannel.assertQueue(queueName, localeOptions));
|
|
469
481
|
debug('assertQueue->channel.assertQueue', { queueName });
|
|
@@ -472,8 +484,8 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
472
484
|
logger.error('rabbit: assertQueue error', { queueName, options, error: e });
|
|
473
485
|
if (!this.options?.dontRetryAssert) {
|
|
474
486
|
debug('retrying assertQueue', { queueName });
|
|
475
|
-
const channel = await this.assertChannel({ force: true });
|
|
476
|
-
await this.deleteQueue(queueName);
|
|
487
|
+
const channel = await this.assertChannel({ force: true, connectionPurpose });
|
|
488
|
+
await this.deleteQueue(queueName, connectionPurpose);
|
|
477
489
|
|
|
478
490
|
debug('retrying assertQueue->channel.addSetup', { queueName });
|
|
479
491
|
await channel.addSetup((setupChannel: ConfirmChannel) => setupChannel.assertQueue(queueName, localeOptions));
|
|
@@ -503,7 +515,7 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
503
515
|
return false;
|
|
504
516
|
}
|
|
505
517
|
|
|
506
|
-
async assertQueue(queueName: string, options?: Options.AssertQueue)
|
|
518
|
+
async assertQueue(queueName: string, connectionPurpose: ConnectionPurpose, options?: Options.AssertQueue) {
|
|
507
519
|
RabbitMq.validateName('queue', queueName);
|
|
508
520
|
if (this.queues[queueName]) {
|
|
509
521
|
delete this.queueSetupPromises[queueName];
|
|
@@ -514,12 +526,12 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
514
526
|
return this.queueSetupPromises[queueName];
|
|
515
527
|
}
|
|
516
528
|
|
|
517
|
-
this.queueSetupPromises[queueName] = this.setupQueue(queueName, options);
|
|
529
|
+
this.queueSetupPromises[queueName] = this.setupQueue(queueName, connectionPurpose, options);
|
|
518
530
|
return this.queueSetupPromises[queueName];
|
|
519
531
|
}
|
|
520
532
|
|
|
521
533
|
private saveConsumer(queue: string, callback: CallbackFunction, options: ConsumeOptions | undefined) {
|
|
522
|
-
const isConsumerExist
|
|
534
|
+
const isConsumerExist: boolean = this.consumers.some((consumer) => consumer.queue === queue);
|
|
523
535
|
if (!isConsumerExist) {
|
|
524
536
|
logger.info(`rabbit: consumer: ${queue} saved in consumer array`);
|
|
525
537
|
this.consumers.push({
|
|
@@ -568,9 +580,9 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
568
580
|
}
|
|
569
581
|
logger.info(`rabbit: Consuming with lock from queue ${queue} with lockTimeout: ${lockTimeout}ms`);
|
|
570
582
|
}
|
|
571
|
-
const channel = await this.getNewChannel({});
|
|
583
|
+
const channel = await this.getNewChannel({ connectionPurpose: ConnectionPurpose.Consume });
|
|
572
584
|
return channel.addSetup(async (confirmChannel: ConfirmChannel) => {
|
|
573
|
-
const q = await this.assertQueue(queue, optionsWithDefaults);
|
|
585
|
+
const q = await this.assertQueue(queue, ConnectionPurpose.Consume, optionsWithDefaults);
|
|
574
586
|
await confirmChannel.prefetch(limit, false);
|
|
575
587
|
const { consumerTag } = await confirmChannel.consume(
|
|
576
588
|
queue,
|
|
@@ -658,13 +670,13 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
658
670
|
});
|
|
659
671
|
}
|
|
660
672
|
|
|
661
|
-
async consumeFromExchange(queue: string, exchange: string, callback: CallbackFunction, options?: ConsumeOptions)
|
|
673
|
+
async consumeFromExchange(queue: string, exchange: string, callback: CallbackFunction, options?: ConsumeOptions): Promise<any> {
|
|
662
674
|
const optionsWithDefaults = { ...DEFAULT_OPTIONS, ...options };
|
|
663
675
|
RabbitMq.validateName('exchange', exchange);
|
|
664
676
|
RabbitMq.validateName('queue', queue);
|
|
665
677
|
const { limit, deadMessageTtl } = optionsWithDefaults;
|
|
666
678
|
await this.saveConsumer(queue, callback, options);
|
|
667
|
-
const channel: ChannelWrapper = await this.getNewChannel({ name: `consume-exchange
|
|
679
|
+
const channel: ChannelWrapper = await this.getNewChannel({ name: `consume - exchange - ${exchange} -queue - ${queue}` });
|
|
668
680
|
|
|
669
681
|
return channel.addSetup(async (c: ConfirmChannel) => {
|
|
670
682
|
const assertExchange = await assertExchangeFanout(c, exchange);
|
|
@@ -682,11 +694,11 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
682
694
|
});
|
|
683
695
|
}
|
|
684
696
|
|
|
685
|
-
async publish(exchange: string, content: any, customHeaders?: any)
|
|
697
|
+
async publish(exchange: string, content: any, customHeaders?: any): Promise<boolean> {
|
|
686
698
|
return wrapSetImmediate(async () => {
|
|
687
699
|
RabbitMq.validateName('exchange', exchange);
|
|
688
|
-
const channel: ChannelWrapper = await this.assertChannel();
|
|
689
|
-
await this.assertExchange(exchange);
|
|
700
|
+
const channel: ChannelWrapper = await this.assertChannel({ connectionPurpose: ConnectionPurpose.Publish });
|
|
701
|
+
await this.assertExchange(exchange, { connectionPurpose: ConnectionPurpose.Publish });
|
|
690
702
|
await channel.publish(exchange, '',
|
|
691
703
|
Buffer.from(JSON.stringify(content)),
|
|
692
704
|
RabbitMq.getPublishOptions(customHeaders));
|
|
@@ -700,7 +712,7 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
700
712
|
customHeaders?: any,
|
|
701
713
|
): Promise<boolean | undefined> {
|
|
702
714
|
try {
|
|
703
|
-
await this.assertChannel();
|
|
715
|
+
await this.assertChannel({ connectionPurpose: ConnectionPurpose.Publish });
|
|
704
716
|
} catch (e) {
|
|
705
717
|
logger.error(`rabbit sendToQueue: failed to send assert channel when sending to queue ${queue}`, { e });
|
|
706
718
|
throw e;
|
|
@@ -708,7 +720,7 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
708
720
|
|
|
709
721
|
try {
|
|
710
722
|
RabbitMq.validateName('queue', queue);
|
|
711
|
-
await this.assertQueue(queue, options);
|
|
723
|
+
await this.assertQueue(queue, ConnectionPurpose.Publish, options);
|
|
712
724
|
} catch (e) {
|
|
713
725
|
logger.error(`rabbit sendToQueue: failed to assert queue ${queue}`, { e });
|
|
714
726
|
throw e;
|
|
@@ -721,34 +733,41 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
721
733
|
debug(`rabbit: sending to queue ${queue}`, { res });
|
|
722
734
|
return res;
|
|
723
735
|
} catch (e) {
|
|
724
|
-
|
|
725
|
-
logger.error(`rabbit sendToQueue: failed to send to queue ${queue}, isConnected: ${isConnected}`, { e });
|
|
736
|
+
logger.error(`rabbit sendToQueue: failed to send to queue ${queue}`, { e });
|
|
726
737
|
throw e;
|
|
727
738
|
}
|
|
728
739
|
}
|
|
729
740
|
|
|
730
|
-
async isConnected()
|
|
731
|
-
const
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
741
|
+
async isConnected(): Promise<boolean> {
|
|
742
|
+
const isEachConnectionConnected = await Promise.all(
|
|
743
|
+
Object.entries(this.connectionsMap).map(async ([connectionPurpose, connectionData]) => {
|
|
744
|
+
const { connection } = connectionData;
|
|
745
|
+
if (connectionPurpose === ConnectionPurpose.Publish && !connection) {
|
|
746
|
+
return true; // The connection hasn't been initialized yet, as no messages have been sent through it
|
|
747
|
+
}
|
|
748
|
+
const isConnected = connection?.isConnected();
|
|
749
|
+
if (!isConnected) {
|
|
750
|
+
logger.error('rabbit: isConnected - false');
|
|
751
|
+
return false;
|
|
752
|
+
}
|
|
753
|
+
const channel: any = await this.assertChannel({ connectionPurpose: connectionPurpose as ConnectionPurpose });
|
|
754
|
+
try {
|
|
755
|
+
await Promise.all([
|
|
756
|
+
channel.waitForConnect(),
|
|
757
|
+
...this.consumers.map((c: AfConsumer) => channel.checkQueue(c.queue)),
|
|
758
|
+
]);
|
|
759
|
+
} catch (e) {
|
|
760
|
+
logger.error('rabbit: isConnected - false');
|
|
761
|
+
return false;
|
|
762
|
+
}
|
|
763
|
+
logger.info('rabbit: isConnected - true');
|
|
764
|
+
return true;
|
|
765
|
+
}),
|
|
766
|
+
);
|
|
767
|
+
return isEachConnectionConnected.every((isConnected) => isConnected === true);
|
|
749
768
|
}
|
|
750
769
|
|
|
751
|
-
async gracefulShutdown(signal: string)
|
|
770
|
+
async gracefulShutdown(signal: string): Promise<void> {
|
|
752
771
|
const tagsNumber = this.consumersTags.length;
|
|
753
772
|
logger.info(`rabbit: [gracefully-shutdown] received ${signal}! canceling #${tagsNumber} tags...`);
|
|
754
773
|
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
|
+
};
|