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