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