@autofleet/rabbit 3.3.0-beta.1 → 3.3.0-beta.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.nvmrc +1 -1
- package/dist/index.d.ts +34 -13
- package/dist/index.js +498 -129
- package/dist/lib/consts.d.ts +2 -4
- package/dist/lib/consts.js +3 -6
- package/dist/lib/types.d.ts +1 -7
- package/package.json +1 -1
- package/src/index.ts +596 -161
- package/src/lib/consts.ts +2 -5
- package/src/lib/types.ts +2 -8
package/dist/index.js
CHANGED
|
@@ -56,10 +56,57 @@ class RabbitMq {
|
|
|
56
56
|
},
|
|
57
57
|
};
|
|
58
58
|
}
|
|
59
|
-
constructor(options, redisConfig) {
|
|
59
|
+
constructor(options = {}, redisConfig) {
|
|
60
60
|
this.DISCONNECT_MSG = 'rabbit: connection disconnect';
|
|
61
61
|
this.RECONNECT_MSG = 'rabbit: connection disconnect - reconnecting';
|
|
62
62
|
this.consumers = [];
|
|
63
|
+
this.doesVHostExist = false;
|
|
64
|
+
this.vhost = 'quorum-vhost';
|
|
65
|
+
this.oldConsumers = [];
|
|
66
|
+
this.assertVHost = async () => {
|
|
67
|
+
if (this.doesVHostExist) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
const username = process.env.RABBITMQ_USERNAME || 'guest';
|
|
71
|
+
const password = process.env.RABBITMQ_PASSWORD || 'guest';
|
|
72
|
+
const credentials = Buffer.from(`${username}:${password}`).toString('base64');
|
|
73
|
+
const headers = {
|
|
74
|
+
Authorization: `Basic ${credentials}`,
|
|
75
|
+
'Content-Type': 'application/json',
|
|
76
|
+
};
|
|
77
|
+
const rabbitHost = `http://${(this.options?.rabbitHost || process.env.RABBITMQ_SERVICE_HOST || 'localhost').split(':')[0]}:15672`;
|
|
78
|
+
const url = `${rabbitHost}/api/vhosts/${encodeURIComponent(this.vhost)}`;
|
|
79
|
+
try {
|
|
80
|
+
const response = await fetch(url, {
|
|
81
|
+
method: 'GET',
|
|
82
|
+
headers,
|
|
83
|
+
});
|
|
84
|
+
if (response.status === 200) {
|
|
85
|
+
this.doesVHostExist = true;
|
|
86
|
+
logger_1.default.info('Vhost exists', { vhost: this.vhost });
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
if (response.status !== 404) {
|
|
90
|
+
logger_1.default.error('Failed to check vhost', { response });
|
|
91
|
+
throw new rabbitError_1.default('Failed to check vhost');
|
|
92
|
+
}
|
|
93
|
+
const createResponse = await fetch(url, {
|
|
94
|
+
method: 'PUT',
|
|
95
|
+
headers,
|
|
96
|
+
body: JSON.stringify({ default_queue_type: 'quorum' }),
|
|
97
|
+
});
|
|
98
|
+
if (!createResponse.ok) {
|
|
99
|
+
logger_1.default.error('Failed to create vhost', { response: createResponse });
|
|
100
|
+
throw new rabbitError_1.default('Failed to create vhost');
|
|
101
|
+
}
|
|
102
|
+
this.doesVHostExist = true;
|
|
103
|
+
logger_1.default.info('Vhost created', { vhost: this.vhost });
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
logger_1.default.error('Failed to check or create vhost', { error });
|
|
107
|
+
throw error;
|
|
108
|
+
}
|
|
109
|
+
};
|
|
63
110
|
this.shouldConsumeMessageByTimestamp = async (msg) => {
|
|
64
111
|
if (msg) {
|
|
65
112
|
const { properties: { headers } } = msg;
|
|
@@ -89,11 +136,11 @@ class RabbitMq {
|
|
|
89
136
|
await this.unlockRedisIfNeeded(releaseLock);
|
|
90
137
|
if (channel && msg) {
|
|
91
138
|
if (!skipRetry
|
|
92
|
-
&& (!msg.properties.headers[consts_1.RETRY_HEADER]
|
|
93
|
-
|| parseInt(msg.properties.headers[consts_1.RETRY_HEADER], 10) < options.retries)) {
|
|
139
|
+
&& (!msg.properties.headers?.[consts_1.RETRY_HEADER]
|
|
140
|
+
|| parseInt(msg.properties.headers?.[consts_1.RETRY_HEADER], 10) < options.retries)) {
|
|
94
141
|
await this.sendToQueue(queue, RabbitMq.parseMsg(msg).content, options, {
|
|
95
142
|
...msg.properties.headers,
|
|
96
|
-
[consts_1.RETRY_HEADER]: msg.properties.headers[consts_1.RETRY_HEADER]
|
|
143
|
+
[consts_1.RETRY_HEADER]: msg.properties.headers?.[consts_1.RETRY_HEADER]
|
|
97
144
|
? msg.properties.headers[consts_1.RETRY_HEADER] + 1
|
|
98
145
|
: 1,
|
|
99
146
|
});
|
|
@@ -102,7 +149,7 @@ class RabbitMq {
|
|
|
102
149
|
const deadQueue = `${queue}-dead`;
|
|
103
150
|
await this.sendToQueue(deadQueue, RabbitMq.parseMsg(msg).content, deadQueueOptions, {
|
|
104
151
|
...msg.properties.headers,
|
|
105
|
-
[consts_1.RETRY_HEADER]: msg.properties.headers[consts_1.RETRY_HEADER]
|
|
152
|
+
[consts_1.RETRY_HEADER]: msg.properties.headers?.[consts_1.RETRY_HEADER]
|
|
106
153
|
? msg.properties.headers[consts_1.RETRY_HEADER] + 1
|
|
107
154
|
: 1,
|
|
108
155
|
});
|
|
@@ -117,22 +164,10 @@ class RabbitMq {
|
|
|
117
164
|
}
|
|
118
165
|
};
|
|
119
166
|
this.em = new events_1.EventEmitter();
|
|
120
|
-
this.
|
|
167
|
+
this.channel = null;
|
|
121
168
|
this.publishChannelSetupPromise = null;
|
|
122
|
-
this.
|
|
123
|
-
|
|
124
|
-
connection: null,
|
|
125
|
-
creatingConnection: false,
|
|
126
|
-
connectionCreatedEventName: 'consumeConnectionCreated',
|
|
127
|
-
connectionFailedEventName: 'consumeConnectionFailed',
|
|
128
|
-
},
|
|
129
|
-
[consts_1.ConnectionPurpose.Publish]: {
|
|
130
|
-
connection: null,
|
|
131
|
-
creatingConnection: false,
|
|
132
|
-
connectionCreatedEventName: 'publishConnectionCreated',
|
|
133
|
-
connectionFailedEventName: 'publishConnectionFailed',
|
|
134
|
-
},
|
|
135
|
-
};
|
|
169
|
+
this.connection = null;
|
|
170
|
+
this.creatingConnection = false;
|
|
136
171
|
this.exchanges = {};
|
|
137
172
|
this.queues = {};
|
|
138
173
|
this.queueSetupPromises = {};
|
|
@@ -153,32 +188,43 @@ class RabbitMq {
|
|
|
153
188
|
await this.gracefulShutdown('SIGINT');
|
|
154
189
|
});
|
|
155
190
|
}
|
|
191
|
+
// TODO: [QUORUM-PHASE-3] Delete the old properties that we use for the old consumers and publishers
|
|
192
|
+
this.oldEm = new events_1.EventEmitter();
|
|
193
|
+
this.oldChannel = null;
|
|
194
|
+
this.oldPublishChannelSetupPromise = null;
|
|
195
|
+
this.oldConnection = null;
|
|
196
|
+
this.oldCreatingConnection = false;
|
|
197
|
+
this.oldExchanges = {};
|
|
198
|
+
this.oldQueues = {};
|
|
199
|
+
this.oldQueueSetupPromises = {};
|
|
200
|
+
this.oldAssertExchangePromises = {};
|
|
201
|
+
this.oldConsumers = [];
|
|
202
|
+
this.oldConsumersTags = [];
|
|
156
203
|
}
|
|
157
|
-
async getConnection(
|
|
204
|
+
async getConnection() {
|
|
158
205
|
return new Promise(async (resolve, reject) => {
|
|
159
|
-
const { connection, creatingConnection, connectionCreatedEventName, connectionFailedEventName, } = this.connectionsMap[connectionPurpose];
|
|
160
206
|
if (this.blockReconnect) {
|
|
161
207
|
debug('rabbit: block reconnect');
|
|
162
208
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
163
209
|
// @ts-ignore
|
|
164
210
|
return resolve();
|
|
165
211
|
}
|
|
166
|
-
if (connection !== null) {
|
|
167
|
-
if (this.options?.disableReconnect || connection?.isConnected()) {
|
|
212
|
+
if (this.connection !== null) {
|
|
213
|
+
if (this.options?.disableReconnect || this.connection?.isConnected()) {
|
|
168
214
|
debug('rabbit: connection - is connected');
|
|
169
215
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
170
216
|
// @ts-ignore
|
|
171
|
-
return resolve(connection);
|
|
217
|
+
return resolve(this.connection);
|
|
172
218
|
}
|
|
173
219
|
debug('rabbit: connection - reconnecting');
|
|
174
220
|
}
|
|
175
|
-
if (creatingConnection) {
|
|
221
|
+
if (this.creatingConnection) {
|
|
176
222
|
debug('rabbit: creating connection emi');
|
|
177
|
-
this.em.once(
|
|
178
|
-
this.em.once(
|
|
223
|
+
this.em.once(consts_1.CONNECTION_CREATED_CONST, resolve);
|
|
224
|
+
this.em.once(consts_1.CONNECTION_FAILED_CONST, reject);
|
|
179
225
|
return;
|
|
180
226
|
}
|
|
181
|
-
this.
|
|
227
|
+
this.creatingConnection = true;
|
|
182
228
|
let isResolved = false;
|
|
183
229
|
// It is import to use it as a function and not as a variable
|
|
184
230
|
// because of k8s changes the env variables
|
|
@@ -188,32 +234,32 @@ class RabbitMq {
|
|
|
188
234
|
const password = process.env.RABBITMQ_PASSWORD || 'guest';
|
|
189
235
|
const host = this.options?.rabbitHost || process.env.RABBITMQ_SERVICE_HOST || 'localhost';
|
|
190
236
|
debug('rabbit: creating connection', { host, userName, HEARTBEAT });
|
|
191
|
-
return [`amqp://${userName}:${password}@${host}?heartbeat=${HEARTBEAT}`];
|
|
237
|
+
return [`amqp://${userName}:${password}@${host}/${this.vhost}?heartbeat=${HEARTBEAT}`];
|
|
192
238
|
};
|
|
193
239
|
const defaultUrls = findServers();
|
|
194
|
-
const
|
|
240
|
+
const connection = await (0, amqp_connection_manager_1.connect)(defaultUrls, {
|
|
195
241
|
findServers,
|
|
196
242
|
});
|
|
197
|
-
this.
|
|
198
|
-
|
|
199
|
-
newConnection.on('error', (err) => {
|
|
243
|
+
this.connection = connection;
|
|
244
|
+
this.connection.on('error', (err) => {
|
|
200
245
|
logger_1.default.error('rabbit: connection error', { err });
|
|
201
246
|
if (!isResolved) {
|
|
202
247
|
isResolved = true;
|
|
203
248
|
reject(err);
|
|
204
|
-
this.em.emit(
|
|
249
|
+
this.em.emit(consts_1.CONNECTION_FAILED_CONST, err);
|
|
205
250
|
}
|
|
206
251
|
});
|
|
207
|
-
|
|
252
|
+
this.connection.on('connectFailed', (err) => {
|
|
208
253
|
this.consumersTags = [];
|
|
209
|
-
logger_1.default.error('rabbit: connection connectFailed', { err });
|
|
254
|
+
logger_1.default.error('rabbit: connection connectFailed', { err, advice: 'Check if the vhost exist', vhost: this.vhost });
|
|
210
255
|
if (!isResolved) {
|
|
211
256
|
isResolved = true;
|
|
212
257
|
reject(err);
|
|
213
|
-
this.em.emit(
|
|
258
|
+
this.em.emit(consts_1.CONNECTION_FAILED_CONST, err);
|
|
214
259
|
}
|
|
215
260
|
});
|
|
216
|
-
|
|
261
|
+
this.connection.on('disconnect', ({ err }) => {
|
|
262
|
+
// this.channel = null;
|
|
217
263
|
this.consumersTags = [];
|
|
218
264
|
debug('rabbit: connection closed');
|
|
219
265
|
if (this.options?.disableReconnect) {
|
|
@@ -224,26 +270,24 @@ class RabbitMq {
|
|
|
224
270
|
logger_1.default.error(`${this.RECONNECT_MSG}${err && ` - ${err}`}`);
|
|
225
271
|
}
|
|
226
272
|
});
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
this.
|
|
273
|
+
this.connection.once('connect', async () => {
|
|
274
|
+
debug('rabbit: connection established');
|
|
275
|
+
this.creatingConnection = false;
|
|
276
|
+
this.em.emit(consts_1.CONNECTION_CREATED_CONST, connection);
|
|
230
277
|
isResolved = true;
|
|
231
|
-
resolve(
|
|
278
|
+
resolve(connection);
|
|
232
279
|
});
|
|
233
280
|
});
|
|
234
281
|
}
|
|
235
|
-
async getNewChannel({ name = (0, utils_1.rand)().toString(), onClose = null, options = {}
|
|
282
|
+
async getNewChannel({ name = (0, utils_1.rand)().toString(), onClose = null, options = {} } = {}) {
|
|
236
283
|
let connection;
|
|
237
284
|
try {
|
|
238
|
-
connection = await this.getConnection(
|
|
285
|
+
connection = await this.getConnection();
|
|
239
286
|
}
|
|
240
287
|
catch (e) {
|
|
241
288
|
logger_1.default.error(`rabbit: error on get connection for new channel ${name} `, { e });
|
|
242
289
|
throw e;
|
|
243
290
|
}
|
|
244
|
-
if (!connection) {
|
|
245
|
-
throw new Error(`rabbit: couldnt get connection for new channel ${name}`);
|
|
246
|
-
}
|
|
247
291
|
const channel = connection.createChannel({ ...options });
|
|
248
292
|
(0, events_1.once)(channel, 'close').then((args) => {
|
|
249
293
|
logger_1.default.error(`rabbit: channel ${name} closed`);
|
|
@@ -259,22 +303,18 @@ class RabbitMq {
|
|
|
259
303
|
throw err;
|
|
260
304
|
}
|
|
261
305
|
}
|
|
262
|
-
async assertChannel({ force = false
|
|
263
|
-
debug('rabbit: start assert channel', { connectionPurpose, publishPromise: this.publishChannelSetupPromise, channel: this.publishChannel });
|
|
306
|
+
async assertChannel({ force = false } = {}) {
|
|
264
307
|
if (!this.publishChannelSetupPromise) {
|
|
265
308
|
this.publishChannelSetupPromise = new Promise(async (resolve, reject) => {
|
|
266
|
-
if (this.
|
|
267
|
-
return resolve(this.
|
|
309
|
+
if (this.channel && !force) {
|
|
310
|
+
return resolve(this.channel);
|
|
268
311
|
}
|
|
269
312
|
try {
|
|
270
|
-
const channel = await this.getNewChannel({
|
|
271
|
-
debug('rabbit: new channel got', { connectionPurpose, channel: this.publishChannel });
|
|
313
|
+
const channel = await this.getNewChannel({});
|
|
272
314
|
channel.on('error', (err) => {
|
|
273
315
|
logger_1.default.error('rabbit: channel error', { err });
|
|
274
316
|
});
|
|
275
|
-
|
|
276
|
-
this.publishChannel = channel;
|
|
277
|
-
}
|
|
317
|
+
this.channel = channel;
|
|
278
318
|
resolve(channel);
|
|
279
319
|
}
|
|
280
320
|
catch (e) {
|
|
@@ -284,8 +324,8 @@ class RabbitMq {
|
|
|
284
324
|
}
|
|
285
325
|
return this.publishChannelSetupPromise;
|
|
286
326
|
}
|
|
287
|
-
async assertExchange(exchangeName, options
|
|
288
|
-
const channel = await this.assertChannel(
|
|
327
|
+
async assertExchange(exchangeName, options) {
|
|
328
|
+
const channel = await this.assertChannel();
|
|
289
329
|
if (this.exchanges[exchangeName]) {
|
|
290
330
|
delete this.assertExchangePromises[exchangeName];
|
|
291
331
|
return this.exchanges[exchangeName];
|
|
@@ -297,46 +337,47 @@ class RabbitMq {
|
|
|
297
337
|
this.exchanges[exchangeName] = await this.assertExchangePromises[exchangeName];
|
|
298
338
|
return this.exchanges[exchangeName];
|
|
299
339
|
}
|
|
300
|
-
|
|
340
|
+
// TODO: [QUORUM-PHASE-2] Change the implementation to the new one
|
|
341
|
+
async getQueueLength(queue) {
|
|
301
342
|
RabbitMq.validateName('queue', queue);
|
|
302
|
-
const {
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
throw new Error('channel is not defined');
|
|
343
|
+
const { oldChannel: channel } = this;
|
|
344
|
+
if (!channel) {
|
|
345
|
+
throw new rabbitError_1.default('channel is not defined');
|
|
306
346
|
}
|
|
307
|
-
debug('rabbit: getting queue length', { queue, connected: connection?.isConnected() });
|
|
308
|
-
return
|
|
347
|
+
debug('rabbit: getting queue length', { queue, connected: this.connection?.isConnected() });
|
|
348
|
+
return channel?.checkQueue(queue);
|
|
309
349
|
}
|
|
310
|
-
async deleteQueue(queue
|
|
350
|
+
async deleteQueue(queue) {
|
|
311
351
|
RabbitMq.validateName('queue', queue);
|
|
312
|
-
const channel = await this.assertChannel(
|
|
352
|
+
const channel = await this.assertChannel();
|
|
313
353
|
logger_1.default.info('rabbit: deleting queue', { queue });
|
|
314
354
|
const deleteQueueRes = await channel.deleteQueue(queue);
|
|
315
355
|
debug('queue deleted', deleteQueueRes);
|
|
316
356
|
return deleteQueueRes;
|
|
317
357
|
}
|
|
358
|
+
// TODO: [QUORUM-PHASE-2] Change the implementation to the new one
|
|
318
359
|
async bindQueue(queue, exchange) {
|
|
319
|
-
const channel = await this.
|
|
360
|
+
const channel = await this.assertChannelOld();
|
|
320
361
|
await channel.addSetup((setupChannel) => setupChannel.bindQueue(queue, exchange, ''));
|
|
321
362
|
return channel.bindQueue(queue, exchange, '');
|
|
322
363
|
}
|
|
323
364
|
async setupQueue(queueName, options) {
|
|
324
365
|
let queue;
|
|
325
|
-
const connectionPurpose = consts_1.ConnectionPurpose.Publish;
|
|
326
|
-
const shouldUseQuorum = RabbitMq.shouldUseQuorum(queueName);
|
|
327
366
|
const localeOptions = {
|
|
328
367
|
...options,
|
|
329
368
|
durable: true,
|
|
330
369
|
arguments: {
|
|
331
370
|
...options?.arguments,
|
|
332
371
|
'x-consumer-timeout': 1000 * 60 * 60 * 24,
|
|
333
|
-
'x-queue-type':
|
|
372
|
+
'x-queue-type': 'quorum',
|
|
334
373
|
},
|
|
335
374
|
};
|
|
336
375
|
try {
|
|
337
|
-
const channel = await this.assertChannel(
|
|
376
|
+
const channel = await this.assertChannel();
|
|
338
377
|
debug('assertQueue->channel.addSetup', { queueName });
|
|
339
|
-
await channel.addSetup((setupChannel) =>
|
|
378
|
+
await channel.addSetup(async (setupChannel) => {
|
|
379
|
+
await setupChannel.assertQueue(queueName, localeOptions);
|
|
380
|
+
});
|
|
340
381
|
debug('assertQueue->channel.assertQueue', { queueName });
|
|
341
382
|
queue = await channel.assertQueue(queueName, localeOptions);
|
|
342
383
|
}
|
|
@@ -344,8 +385,8 @@ class RabbitMq {
|
|
|
344
385
|
logger_1.default.error('rabbit: assertQueue error', { queueName, options, error: e });
|
|
345
386
|
if (!this.options?.dontRetryAssert) {
|
|
346
387
|
debug('retrying assertQueue', { queueName });
|
|
347
|
-
const channel = await this.assertChannel({ force: true
|
|
348
|
-
await this.deleteQueue(queueName
|
|
388
|
+
const channel = await this.assertChannel({ force: true });
|
|
389
|
+
await this.deleteQueue(queueName);
|
|
349
390
|
debug('retrying assertQueue->channel.addSetup', { queueName });
|
|
350
391
|
await channel.addSetup((setupChannel) => setupChannel.assertQueue(queueName, localeOptions));
|
|
351
392
|
debug('retrying assertQueue->channel.assertQueue', { queueName });
|
|
@@ -358,6 +399,7 @@ class RabbitMq {
|
|
|
358
399
|
this.queues[queueName] = queue;
|
|
359
400
|
return queue;
|
|
360
401
|
}
|
|
402
|
+
// TODO: [QUORUM-PHASE-3] Can be deleted after deleting the old consumers and publishers because all the queues are created us quorum queues
|
|
361
403
|
static shouldUseQuorum(queueName) {
|
|
362
404
|
const envQuorumQueuesWhitelist = process.env.QUORUM_QUEUES_WHITELIST;
|
|
363
405
|
if (envQuorumQueuesWhitelist === '*') {
|
|
@@ -370,7 +412,6 @@ class RabbitMq {
|
|
|
370
412
|
return false;
|
|
371
413
|
}
|
|
372
414
|
async assertQueue(queueName, options) {
|
|
373
|
-
debug('rabbit: start assert queue', { queueName });
|
|
374
415
|
RabbitMq.validateName('queue', queueName);
|
|
375
416
|
if (this.queues[queueName]) {
|
|
376
417
|
delete this.queueSetupPromises[queueName];
|
|
@@ -380,7 +421,6 @@ class RabbitMq {
|
|
|
380
421
|
return this.queueSetupPromises[queueName];
|
|
381
422
|
}
|
|
382
423
|
this.queueSetupPromises[queueName] = this.setupQueue(queueName, options);
|
|
383
|
-
debug('rabbit: done assert queue', { queueName });
|
|
384
424
|
return this.queueSetupPromises[queueName];
|
|
385
425
|
}
|
|
386
426
|
saveConsumer(queue, callback, options) {
|
|
@@ -394,9 +434,23 @@ class RabbitMq {
|
|
|
394
434
|
});
|
|
395
435
|
}
|
|
396
436
|
}
|
|
437
|
+
// Used by the microservices to consume messages from the queue
|
|
397
438
|
async consume(queue, callback, options) {
|
|
439
|
+
// TODO: [QUORUM-PHASE-3] Use only the implementation of consumeNew and delete consumeNew and consumeOld
|
|
440
|
+
if (options?.isQuorumQueue !== false) {
|
|
441
|
+
await this.assertVHost();
|
|
442
|
+
await this.consumeNew(queue, callback, options);
|
|
443
|
+
}
|
|
444
|
+
await this.consumeOld(queue, callback, options);
|
|
445
|
+
}
|
|
446
|
+
// TODO: [QUORUM-PHASE-3] Delete consumeNew we do not use it anymore
|
|
447
|
+
async consumeNew(queue, callback, options) {
|
|
398
448
|
await this.consumeFromRabbit(queue, callback, options);
|
|
399
449
|
}
|
|
450
|
+
// TODO: [QUORUM-PHASE-3] Delete consumeOld we do not use it anymore
|
|
451
|
+
async consumeOld(queue, callback, options) {
|
|
452
|
+
await this.consumeFromRabbitOld(queue, callback, options);
|
|
453
|
+
}
|
|
400
454
|
async lockRedisIfNeeded(msg, options) {
|
|
401
455
|
const { properties: { headers } } = msg;
|
|
402
456
|
const timestamp = headers?.creationTimestamp;
|
|
@@ -419,21 +473,22 @@ class RabbitMq {
|
|
|
419
473
|
const { limit, deadMessageTtl, useConsumeWithLock, lockTimeout, auditContext, enableRabbitTrace, } = optionsWithDefaults;
|
|
420
474
|
if (useConsumeWithLock) {
|
|
421
475
|
if (!this.redisLock) {
|
|
422
|
-
throw new
|
|
476
|
+
throw new rabbitError_1.default('Usage of consumeWithLock requires RedisInstance');
|
|
423
477
|
}
|
|
424
478
|
logger_1.default.info(`rabbit: Consuming with lock from queue ${queue} with lockTimeout: ${lockTimeout}ms`);
|
|
425
479
|
}
|
|
426
|
-
const channel = await this.getNewChannel({
|
|
480
|
+
const channel = await this.getNewChannel({});
|
|
427
481
|
return channel.addSetup(async (confirmChannel) => {
|
|
482
|
+
logger_1.default.info(`rabbit: channel.addSetup before ${queue} assertQueueOld`);
|
|
428
483
|
const q = await this.assertQueue(queue, optionsWithDefaults);
|
|
429
484
|
await confirmChannel.prefetch(limit, false);
|
|
430
485
|
const { consumerTag } = await confirmChannel.consume(queue, async (msg) => {
|
|
431
486
|
if (!msg) {
|
|
432
487
|
return null;
|
|
433
488
|
}
|
|
434
|
-
const traceId = msg.properties.headers[consts_1.TRACING_HEADER];
|
|
435
|
-
const userId = msg.properties.headers[consts_1.USER_TRACING_HEADER];
|
|
436
|
-
const automationId = msg.properties.headers[consts_1.AUTOMATION_ID_HEADER];
|
|
489
|
+
const traceId = msg.properties.headers?.[consts_1.TRACING_HEADER];
|
|
490
|
+
const userId = msg.properties.headers?.[consts_1.USER_TRACING_HEADER];
|
|
491
|
+
const automationId = msg.properties.headers?.[consts_1.AUTOMATION_ID_HEADER];
|
|
437
492
|
const parsedMessage = RabbitMq.parseMsg(msg);
|
|
438
493
|
const releaseLock = await this.lockRedisIfNeeded(parsedMessage, optionsWithDefaults);
|
|
439
494
|
const trace = (0, zehut_1.newTrace)(zehut_1.traceTypes.RABBIT);
|
|
@@ -501,36 +556,57 @@ class RabbitMq {
|
|
|
501
556
|
}
|
|
502
557
|
});
|
|
503
558
|
}
|
|
559
|
+
// Used by the microservices to consume messages from the exchange
|
|
504
560
|
async consumeFromExchange(queue, exchange, callback, options) {
|
|
505
561
|
const optionsWithDefaults = { ...consts_1.DEFAULT_OPTIONS, ...options };
|
|
506
562
|
RabbitMq.validateName('exchange', exchange);
|
|
507
563
|
RabbitMq.validateName('queue', queue);
|
|
508
564
|
const { limit, deadMessageTtl } = optionsWithDefaults;
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
565
|
+
// TODO: [QUORUM-PHASE-3] Delete the if statement after all the queues are created as quorum queues
|
|
566
|
+
if (options?.isQuorumQueue !== false) {
|
|
567
|
+
await this.assertVHost();
|
|
568
|
+
await this.saveConsumer(queue, callback, options);
|
|
569
|
+
const channel = await this.getNewChannel({ name: `consume-exchange-${exchange}-queue-${queue}` });
|
|
570
|
+
await channel.addSetup(async (c) => {
|
|
571
|
+
const assertExchange = await (0, utils_1.assertExchangeFanout)(c, exchange);
|
|
572
|
+
await c.assertQueue(queue);
|
|
573
|
+
this.exchanges[exchange] = assertExchange;
|
|
574
|
+
await c.prefetch(limit, false);
|
|
575
|
+
return Promise.all([
|
|
576
|
+
c.bindQueue(queue, exchange, ''),
|
|
577
|
+
this.consumeNew(queue, callback, options),
|
|
578
|
+
]);
|
|
579
|
+
});
|
|
580
|
+
}
|
|
581
|
+
// TODO: [QUORUM-PHASE-3] Delete the old implementation
|
|
582
|
+
await this.saveConsumerOld(queue, callback, options);
|
|
583
|
+
const channelOld = await this.getNewChannelOld({ name: `consume-exchange-${exchange}-queue-${queue}-old` });
|
|
584
|
+
await channelOld.addSetup(async (c) => {
|
|
512
585
|
const assertExchange = await (0, utils_1.assertExchangeFanout)(c, exchange);
|
|
513
|
-
await c.assertQueue(queue);
|
|
514
|
-
this.
|
|
586
|
+
// await c.assertQueue(queue);
|
|
587
|
+
this.oldExchanges[exchange] = assertExchange;
|
|
515
588
|
await c.prefetch(limit, false);
|
|
516
589
|
return Promise.all([
|
|
517
|
-
c.bindQueue(queue, exchange, ''),
|
|
518
|
-
this.
|
|
590
|
+
// c.bindQueue(queue, exchange, ''),
|
|
591
|
+
this.consumeOld(queue, callback, options),
|
|
519
592
|
]);
|
|
520
593
|
});
|
|
521
594
|
}
|
|
595
|
+
// Used by the microservices to publish messages to the exchange
|
|
596
|
+
// TODO: [QUORUM-PHASE-2] Change the implementation to the new one
|
|
522
597
|
async publish(exchange, content, customHeaders) {
|
|
523
|
-
debug('rabbit: start publish msg');
|
|
524
598
|
return (0, utils_1.wrapSetImmediate)(async () => {
|
|
525
599
|
RabbitMq.validateName('exchange', exchange);
|
|
526
|
-
const channel = await this.
|
|
527
|
-
await this.
|
|
600
|
+
const channel = await this.assertChannelOld();
|
|
601
|
+
await this.assertExchangeOld(exchange);
|
|
528
602
|
await channel.publish(exchange, '', Buffer.from(JSON.stringify(content)), RabbitMq.getPublishOptions(customHeaders));
|
|
529
603
|
});
|
|
530
604
|
}
|
|
605
|
+
// Used by the microservices to send messages to the queue
|
|
606
|
+
// TODO: [QUORUM-PHASE-2] Change the implementation to the new one
|
|
531
607
|
async sendToQueue(queue, content, options, customHeaders) {
|
|
532
608
|
try {
|
|
533
|
-
await this.
|
|
609
|
+
await this.assertChannelOld();
|
|
534
610
|
}
|
|
535
611
|
catch (e) {
|
|
536
612
|
logger_1.default.error(`rabbit sendToQueue: failed to send assert channel when sending to queue ${queue}`, { e });
|
|
@@ -538,61 +614,55 @@ class RabbitMq {
|
|
|
538
614
|
}
|
|
539
615
|
try {
|
|
540
616
|
RabbitMq.validateName('queue', queue);
|
|
541
|
-
await this.
|
|
617
|
+
await this.assertQueueOld(queue, options);
|
|
542
618
|
}
|
|
543
619
|
catch (e) {
|
|
544
620
|
logger_1.default.error(`rabbit sendToQueue: failed to assert queue ${queue}`, { e });
|
|
545
621
|
throw e;
|
|
546
622
|
}
|
|
547
623
|
try {
|
|
548
|
-
const res = await this.
|
|
624
|
+
const res = await this.oldChannel?.sendToQueue(queue, Buffer.from(JSON.stringify(content)), RabbitMq.getPublishOptions(customHeaders));
|
|
549
625
|
debug(`rabbit: sending to queue ${queue}`, { res });
|
|
550
626
|
return res;
|
|
551
627
|
}
|
|
552
628
|
catch (e) {
|
|
553
|
-
|
|
629
|
+
const isConnected = await this.isConnected();
|
|
630
|
+
logger_1.default.error(`rabbit sendToQueue: failed to send to queue ${queue}, isConnected: ${isConnected}`, { e });
|
|
554
631
|
throw e;
|
|
555
632
|
}
|
|
556
633
|
}
|
|
634
|
+
// TODO: [QUORUM-PHASE-2] After changing the publish from old to new change from the old to the new implementation
|
|
557
635
|
async isConnected() {
|
|
558
|
-
|
|
559
|
-
const
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
logger_1.default.error('rabbit: isConnected - false');
|
|
578
|
-
return false;
|
|
579
|
-
}
|
|
580
|
-
}
|
|
581
|
-
}
|
|
582
|
-
else {
|
|
583
|
-
logger_1.default.info('rabbit: connection hasnt initialized yet', { connectionPurpose });
|
|
584
|
-
}
|
|
585
|
-
return true;
|
|
586
|
-
}));
|
|
587
|
-
return isEachConnectionConnected.every((isConnected) => isConnected === true);
|
|
636
|
+
const connection = await this.getConnectionOld();
|
|
637
|
+
const isConnected = connection.isConnected();
|
|
638
|
+
if (!isConnected) {
|
|
639
|
+
logger_1.default.error('rabbit: isConnected - false');
|
|
640
|
+
return false;
|
|
641
|
+
}
|
|
642
|
+
const channel = await this.assertChannelOld();
|
|
643
|
+
try {
|
|
644
|
+
await Promise.all([
|
|
645
|
+
channel.waitForConnect(),
|
|
646
|
+
...this.oldConsumers.map((c) => channel.checkQueue(c.queue)),
|
|
647
|
+
]);
|
|
648
|
+
}
|
|
649
|
+
catch (e) {
|
|
650
|
+
logger_1.default.error('rabbit: isConnected - false');
|
|
651
|
+
return false;
|
|
652
|
+
}
|
|
653
|
+
logger_1.default.info('rabbit: isConnected - true');
|
|
654
|
+
return true;
|
|
588
655
|
}
|
|
589
656
|
async gracefulShutdown(signal) {
|
|
590
|
-
|
|
657
|
+
// TODO: [QUORUM-PHASE-2] After changing the publish from old to new change from the old to the new implementation
|
|
658
|
+
const tagsNumber = this.consumersTags.length + this.oldConsumersTags.length;
|
|
591
659
|
logger_1.default.info(`rabbit: [gracefully-shutdown] received ${signal}! canceling #${tagsNumber} tags...`);
|
|
592
660
|
const cancelTagPromises = this.consumersTags.map(([channel, tag]) => channel.cancel(tag));
|
|
661
|
+
const cancelTagPromisesOld = this.oldConsumersTags.map(([channel, tag]) => channel.cancel(tag));
|
|
593
662
|
// Clean the array to avoid race
|
|
594
663
|
this.consumersTags = [];
|
|
595
|
-
|
|
664
|
+
this.oldConsumersTags = [];
|
|
665
|
+
const results = await Promise.allSettled([...cancelTagPromises, ...cancelTagPromisesOld]);
|
|
596
666
|
const rejected = results.filter((p) => p.status === 'rejected');
|
|
597
667
|
if (rejected.length > 0) {
|
|
598
668
|
logger_1.default.warn(`rabbit: [gracefully-shutdown] #${rejected.length}/${tagsNumber} tags failed to cancel: ${rejected}`);
|
|
@@ -601,6 +671,305 @@ class RabbitMq {
|
|
|
601
671
|
logger_1.default.info('rabbit: [gracefully-shutdown] all tags successfully canceled.');
|
|
602
672
|
}
|
|
603
673
|
}
|
|
674
|
+
async consumeFromRabbitOld(queue, callback, options) {
|
|
675
|
+
const optionsWithDefaults = { ...consts_1.DEFAULT_OPTIONS, ...options };
|
|
676
|
+
RabbitMq.validateName('queue', queue);
|
|
677
|
+
this.saveConsumerOld(queue, callback, options);
|
|
678
|
+
const uniqueId = (0, node_crypto_1.randomUUID)();
|
|
679
|
+
const { limit, deadMessageTtl, useConsumeWithLock, lockTimeout, auditContext, enableRabbitTrace, } = optionsWithDefaults;
|
|
680
|
+
if (useConsumeWithLock) {
|
|
681
|
+
if (!this.redisLock) {
|
|
682
|
+
throw new rabbitError_1.default('Usage of consumeWithLock requires RedisInstance');
|
|
683
|
+
}
|
|
684
|
+
logger_1.default.info(`rabbit: Consuming with lock from queue ${queue} with lockTimeout: ${lockTimeout}ms`);
|
|
685
|
+
}
|
|
686
|
+
const channel = await this.getNewChannelOld({});
|
|
687
|
+
return channel.addSetup(async (confirmChannel) => {
|
|
688
|
+
const q = await this.assertQueueOld(queue, optionsWithDefaults);
|
|
689
|
+
await confirmChannel.prefetch(limit, false);
|
|
690
|
+
const { consumerTag } = await confirmChannel.consume(queue, async (msg) => {
|
|
691
|
+
if (!msg) {
|
|
692
|
+
return null;
|
|
693
|
+
}
|
|
694
|
+
const traceId = msg.properties.headers?.[consts_1.TRACING_HEADER];
|
|
695
|
+
const userId = msg.properties.headers?.[consts_1.USER_TRACING_HEADER];
|
|
696
|
+
const automationId = msg.properties.headers?.[consts_1.AUTOMATION_ID_HEADER];
|
|
697
|
+
const parsedMessage = RabbitMq.parseMsg(msg);
|
|
698
|
+
const releaseLock = await this.lockRedisIfNeeded(parsedMessage, optionsWithDefaults);
|
|
699
|
+
const trace = (0, zehut_1.newTrace)(zehut_1.traceTypes.RABBIT);
|
|
700
|
+
// setting also outbreak trace as part of legacy code
|
|
701
|
+
const outbreakTrace = zehut_1.outbreak.newTrace(zehut_1.traceTypes.RABBIT);
|
|
702
|
+
// enableRabbitTrace is a flag to protect from different flows that doesn't work with permission
|
|
703
|
+
// and we don't want to fail the flow because of it
|
|
704
|
+
if (userId && enableRabbitTrace) {
|
|
705
|
+
try {
|
|
706
|
+
await Promise.all([
|
|
707
|
+
(0, zehut_1.createOrSetRabbitTrace)(trace, userId),
|
|
708
|
+
(0, zehut_1.createOrSetRabbitTrace)(outbreakTrace, userId),
|
|
709
|
+
]);
|
|
710
|
+
}
|
|
711
|
+
catch (e) {
|
|
712
|
+
logger_1.default.error('rabbit: failed to setRabbitTrace', { userId, e });
|
|
713
|
+
return this.nack(confirmChannel, queue, optionsWithDefaults, { messageTtl: deadMessageTtl }, msg, releaseLock)(msg);
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
if (traceId) {
|
|
717
|
+
trace?.context?.set(consts_1.TRACING_HEADER, traceId);
|
|
718
|
+
outbreakTrace?.context.set(consts_1.TRACING_HEADER, traceId);
|
|
719
|
+
}
|
|
720
|
+
if (auditContext) {
|
|
721
|
+
await auditContext(queue, {
|
|
722
|
+
userId,
|
|
723
|
+
automationId,
|
|
724
|
+
});
|
|
725
|
+
}
|
|
726
|
+
const shouldConsume = await this.shouldConsumeMessageByTimestamp(parsedMessage);
|
|
727
|
+
if (!shouldConsume) {
|
|
728
|
+
await this.unlockRedisIfNeeded(releaseLock);
|
|
729
|
+
return this.ack(confirmChannel, msg)(msg);
|
|
730
|
+
}
|
|
731
|
+
let messageAcked = false;
|
|
732
|
+
// setting the localAck function to be used in the callback
|
|
733
|
+
const localAck = async () => {
|
|
734
|
+
if (messageAcked) {
|
|
735
|
+
return;
|
|
736
|
+
}
|
|
737
|
+
messageAcked = true;
|
|
738
|
+
return this.ack(confirmChannel, msg, true, releaseLock)(msg);
|
|
739
|
+
};
|
|
740
|
+
const localNack = async (_, nackOptions = {}) => {
|
|
741
|
+
if (messageAcked) {
|
|
742
|
+
return;
|
|
743
|
+
}
|
|
744
|
+
debug('rabbit localNack', { messageAcked, uniqueId, deliveryTag: msg.fields.deliveryTag });
|
|
745
|
+
messageAcked = true;
|
|
746
|
+
return this.nack(confirmChannel, queue, optionsWithDefaults, { messageTtl: deadMessageTtl }, msg, releaseLock)(msg, nackOptions);
|
|
747
|
+
};
|
|
748
|
+
try {
|
|
749
|
+
await callback(parsedMessage, localAck, localNack);
|
|
750
|
+
}
|
|
751
|
+
catch (e) {
|
|
752
|
+
await localNack(msg);
|
|
753
|
+
}
|
|
754
|
+
}, types_1.CONSUMER_DEFAULT_OPTIONS);
|
|
755
|
+
if (!consumerTag) {
|
|
756
|
+
logger_1.default.error(`rabbit: failed to consume from queue ${queue}`);
|
|
757
|
+
}
|
|
758
|
+
else {
|
|
759
|
+
logger_1.default.info(`rabbit: adding tag ${consumerTag} to the array.`);
|
|
760
|
+
this.oldConsumersTags.push([confirmChannel, consumerTag]);
|
|
761
|
+
}
|
|
762
|
+
});
|
|
763
|
+
}
|
|
764
|
+
// TODO: [QUORUM-PHASE-3] Delete all the function under this line (getNewChannelOld, getConnectionOld, assertQueueOld, setupQueueOld, saveConsumerOld)
|
|
765
|
+
async getNewChannelOld({ name = (0, utils_1.rand)().toString(), onClose = null, options = {} } = {}) {
|
|
766
|
+
let connection;
|
|
767
|
+
try {
|
|
768
|
+
connection = await this.getConnectionOld();
|
|
769
|
+
}
|
|
770
|
+
catch (e) {
|
|
771
|
+
logger_1.default.error(`rabbit: error on get connection for new channel ${name} `, { e });
|
|
772
|
+
throw e;
|
|
773
|
+
}
|
|
774
|
+
const channel = connection.createChannel({ ...options });
|
|
775
|
+
(0, events_1.once)(channel, 'close').then((args) => {
|
|
776
|
+
logger_1.default.error(`rabbit: channel ${name} closed`);
|
|
777
|
+
onClose?.(args);
|
|
778
|
+
});
|
|
779
|
+
try {
|
|
780
|
+
await (0, events_1.once)(channel, 'connect');
|
|
781
|
+
debug(`rabbit: channel ${name} CONNECTED`);
|
|
782
|
+
return channel;
|
|
783
|
+
}
|
|
784
|
+
catch (err) {
|
|
785
|
+
logger_1.default.error(`rabbit: channel error ${name} error`, { err });
|
|
786
|
+
throw err;
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
async getConnectionOld() {
|
|
790
|
+
return new Promise(async (resolve, reject) => {
|
|
791
|
+
if (this.oldBlockReconnect) {
|
|
792
|
+
debug('rabbit: block reconnect');
|
|
793
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
794
|
+
// @ts-ignore
|
|
795
|
+
return resolve();
|
|
796
|
+
}
|
|
797
|
+
if (this.oldConnection !== null) {
|
|
798
|
+
if (this.options?.disableReconnect || this.oldConnection?.isConnected()) {
|
|
799
|
+
debug('rabbit: connection - is connected');
|
|
800
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
801
|
+
// @ts-ignore
|
|
802
|
+
return resolve(this.oldConnection);
|
|
803
|
+
}
|
|
804
|
+
debug('rabbit: connection - reconnecting');
|
|
805
|
+
}
|
|
806
|
+
if (this.oldCreatingConnection) {
|
|
807
|
+
debug('rabbit: creating connection emi');
|
|
808
|
+
this.oldEm.once(consts_1.CONNECTION_CREATED_CONST, resolve);
|
|
809
|
+
this.oldEm.once(consts_1.CONNECTION_FAILED_CONST, reject);
|
|
810
|
+
return;
|
|
811
|
+
}
|
|
812
|
+
this.oldCreatingConnection = true;
|
|
813
|
+
let isResolved = false;
|
|
814
|
+
// It is import to use it as a function and not as a variable
|
|
815
|
+
// because of k8s changes the env variables
|
|
816
|
+
// and we want to use the new values
|
|
817
|
+
const findServers = () => {
|
|
818
|
+
const userName = process.env.RABBITMQ_USERNAME || 'guest';
|
|
819
|
+
const password = process.env.RABBITMQ_PASSWORD || 'guest';
|
|
820
|
+
const host = this.options?.rabbitHost || process.env.RABBITMQ_SERVICE_HOST || 'localhost';
|
|
821
|
+
debug('rabbit: creating connection', { host, userName, HEARTBEAT });
|
|
822
|
+
return [`amqp://${userName}:${password}@${host}?heartbeat=${HEARTBEAT}`];
|
|
823
|
+
};
|
|
824
|
+
const defaultUrls = findServers();
|
|
825
|
+
const connection = await (0, amqp_connection_manager_1.connect)(defaultUrls, {
|
|
826
|
+
findServers,
|
|
827
|
+
});
|
|
828
|
+
this.oldConnection = connection;
|
|
829
|
+
this.oldConnection.on('error', (err) => {
|
|
830
|
+
logger_1.default.error('rabbit: connection error', { err });
|
|
831
|
+
if (!isResolved) {
|
|
832
|
+
isResolved = true;
|
|
833
|
+
reject(err);
|
|
834
|
+
this.oldEm.emit(consts_1.CONNECTION_FAILED_CONST, err);
|
|
835
|
+
}
|
|
836
|
+
});
|
|
837
|
+
this.oldConnection.on('connectFailed', (err) => {
|
|
838
|
+
this.oldConsumersTags = [];
|
|
839
|
+
logger_1.default.error('rabbit: connection connectFailed', { err });
|
|
840
|
+
if (!isResolved) {
|
|
841
|
+
isResolved = true;
|
|
842
|
+
reject(err);
|
|
843
|
+
this.oldEm.emit(consts_1.CONNECTION_FAILED_CONST, err);
|
|
844
|
+
}
|
|
845
|
+
});
|
|
846
|
+
this.oldConnection.on('disconnect', ({ err }) => {
|
|
847
|
+
this.oldConsumersTags = [];
|
|
848
|
+
debug('rabbit: connection closed');
|
|
849
|
+
if (this.options?.disableReconnect) {
|
|
850
|
+
logger_1.default.error(`${this.DISCONNECT_MSG}${err && ` - ${err}`}`);
|
|
851
|
+
this.oldBlockReconnect = true;
|
|
852
|
+
}
|
|
853
|
+
else {
|
|
854
|
+
logger_1.default.error(`${this.RECONNECT_MSG}${err && ` - ${err}`}`);
|
|
855
|
+
}
|
|
856
|
+
});
|
|
857
|
+
this.oldConnection.once('connect', async () => {
|
|
858
|
+
debug('rabbit: connection established');
|
|
859
|
+
this.oldCreatingConnection = false;
|
|
860
|
+
this.oldEm.emit(consts_1.CONNECTION_CREATED_CONST, connection);
|
|
861
|
+
isResolved = true;
|
|
862
|
+
resolve(connection);
|
|
863
|
+
});
|
|
864
|
+
});
|
|
865
|
+
}
|
|
866
|
+
saveConsumerOld(queue, callback, options) {
|
|
867
|
+
const isConsumerExist = this.oldConsumers.some((consumer) => consumer.queue === queue);
|
|
868
|
+
if (!isConsumerExist) {
|
|
869
|
+
logger_1.default.info(`rabbit: consumer: ${queue} saved in consumer array`);
|
|
870
|
+
this.oldConsumers.push({
|
|
871
|
+
queue,
|
|
872
|
+
callback,
|
|
873
|
+
options,
|
|
874
|
+
});
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
async assertQueueOld(queueName, options) {
|
|
878
|
+
RabbitMq.validateName('queue', queueName);
|
|
879
|
+
if (this.oldQueues[queueName]) {
|
|
880
|
+
delete this.oldQueueSetupPromises[queueName];
|
|
881
|
+
return this.oldQueues[queueName];
|
|
882
|
+
}
|
|
883
|
+
if (this.oldQueueSetupPromises[queueName]) {
|
|
884
|
+
return this.oldQueueSetupPromises[queueName];
|
|
885
|
+
}
|
|
886
|
+
this.oldQueueSetupPromises[queueName] = this.setupQueueOld(queueName, options);
|
|
887
|
+
return this.oldQueueSetupPromises[queueName];
|
|
888
|
+
}
|
|
889
|
+
async setupQueueOld(queueName, options) {
|
|
890
|
+
// let queue: Replies.AssertQueue;
|
|
891
|
+
const shouldUseQuorum = RabbitMq.shouldUseQuorum(queueName);
|
|
892
|
+
const localeOptions = {
|
|
893
|
+
...options,
|
|
894
|
+
durable: true,
|
|
895
|
+
arguments: {
|
|
896
|
+
...options?.arguments,
|
|
897
|
+
'x-consumer-timeout': 1000 * 60 * 60 * 24,
|
|
898
|
+
'x-queue-type': shouldUseQuorum ? 'quorum' : 'classic',
|
|
899
|
+
},
|
|
900
|
+
};
|
|
901
|
+
try {
|
|
902
|
+
const channel = await this.assertChannelOld();
|
|
903
|
+
debug('assertQueue->channel.addSetup', { queueName });
|
|
904
|
+
// await channel.addSetup((setupChannel: ConfirmChannel) => setupChannel.assertQueue(queueName, localeOptions));
|
|
905
|
+
debug('assertQueue->channel.assertQueue', { queueName });
|
|
906
|
+
// queue = await channel.assertQueue(queueName, localeOptions);
|
|
907
|
+
}
|
|
908
|
+
catch (e) {
|
|
909
|
+
logger_1.default.error('rabbit: assertQueue error', { queueName, options, error: e });
|
|
910
|
+
if (!this.options?.dontRetryAssert) {
|
|
911
|
+
debug('retrying assertQueue', { queueName });
|
|
912
|
+
const channel = await this.assertChannelOld({ force: true });
|
|
913
|
+
await this.deleteQueueOld(queueName);
|
|
914
|
+
debug('retrying assertQueue->channel.addSetup', { queueName });
|
|
915
|
+
await channel.addSetup((setupChannel) => setupChannel.assertQueue(queueName, localeOptions));
|
|
916
|
+
debug('retrying assertQueue->channel.assertQueue', { queueName });
|
|
917
|
+
// queue = await channel.assertQueue(queueName, localeOptions);
|
|
918
|
+
}
|
|
919
|
+
else {
|
|
920
|
+
throw e;
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
const queue = {
|
|
924
|
+
queue: 'bla bla queue',
|
|
925
|
+
messageCount: 0,
|
|
926
|
+
consumerCount: 2,
|
|
927
|
+
};
|
|
928
|
+
this.oldQueues[queueName] = queue;
|
|
929
|
+
return queue;
|
|
930
|
+
}
|
|
931
|
+
async assertChannelOld({ force = false } = {}) {
|
|
932
|
+
if (!this.oldPublishChannelSetupPromise) {
|
|
933
|
+
this.oldPublishChannelSetupPromise = new Promise(async (resolve, reject) => {
|
|
934
|
+
if (this.oldChannel && !force) {
|
|
935
|
+
return resolve(this.oldChannel);
|
|
936
|
+
}
|
|
937
|
+
try {
|
|
938
|
+
const channel = await this.getNewChannelOld({});
|
|
939
|
+
channel.on('error', (err) => {
|
|
940
|
+
logger_1.default.error('rabbit: channel error', { err });
|
|
941
|
+
});
|
|
942
|
+
this.oldChannel = channel;
|
|
943
|
+
resolve(channel);
|
|
944
|
+
}
|
|
945
|
+
catch (e) {
|
|
946
|
+
reject(e);
|
|
947
|
+
}
|
|
948
|
+
});
|
|
949
|
+
}
|
|
950
|
+
return this.oldPublishChannelSetupPromise;
|
|
951
|
+
}
|
|
952
|
+
async deleteQueueOld(queue) {
|
|
953
|
+
RabbitMq.validateName('queue', queue);
|
|
954
|
+
const channel = await this.assertChannelOld();
|
|
955
|
+
logger_1.default.info('rabbit: deleting queue', { queue });
|
|
956
|
+
const deleteQueueRes = await channel.deleteQueue(queue);
|
|
957
|
+
debug('queue deleted', deleteQueueRes);
|
|
958
|
+
return deleteQueueRes;
|
|
959
|
+
}
|
|
960
|
+
async assertExchangeOld(exchangeName, options) {
|
|
961
|
+
const channel = await this.assertChannelOld();
|
|
962
|
+
if (this.oldExchanges[exchangeName]) {
|
|
963
|
+
delete this.oldAssertExchangePromises[exchangeName];
|
|
964
|
+
return this.oldExchanges[exchangeName];
|
|
965
|
+
}
|
|
966
|
+
if (this.oldAssertExchangePromises[exchangeName]) {
|
|
967
|
+
return this.oldAssertExchangePromises[exchangeName];
|
|
968
|
+
}
|
|
969
|
+
this.oldAssertExchangePromises[exchangeName] = (0, utils_1.assertExchangeFanout)(channel, exchangeName);
|
|
970
|
+
this.oldExchanges[exchangeName] = await this.oldAssertExchangePromises[exchangeName];
|
|
971
|
+
return this.oldExchanges[exchangeName];
|
|
972
|
+
}
|
|
604
973
|
}
|
|
605
974
|
exports.default = RabbitMq;
|
|
606
975
|
var celery_1 = require("./lib/celery");
|