@autofleet/rabbit 3.1.2 → 3.2.2-2.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/.nvmrc +1 -1
- package/dist/index.d.ts +29 -18
- package/dist/index.js +251 -169
- package/dist/lib/redis.d.ts +1 -1
- package/dist/lib/types.d.ts +20 -8
- package/dist/lib/types.js +7 -1
- package/dist/lib/utils.js +6 -3
- package/dist/logger.d.ts +1 -1
- package/dist/logger.js +5 -2
- package/package.json +14 -14
- package/src/index.ts +257 -155
- package/src/lib/types.ts +16 -1
- package/src/logger.ts +1 -1
- package/tsconfig.json +5 -2
package/dist/index.js
CHANGED
|
@@ -10,6 +10,7 @@ const moment_1 = __importDefault(require("moment"));
|
|
|
10
10
|
const redis_lock_1 = __importDefault(require("redis-lock"));
|
|
11
11
|
const amqp_connection_manager_1 = require("amqp-connection-manager");
|
|
12
12
|
const zehut_1 = require("@autofleet/zehut");
|
|
13
|
+
const node_crypto_1 = require("node:crypto");
|
|
13
14
|
const logger_1 = __importDefault(require("./logger"));
|
|
14
15
|
const rabbitError_1 = __importDefault(require("./lib/rabbitError"));
|
|
15
16
|
const redis_1 = __importDefault(require("./lib/redis"));
|
|
@@ -17,15 +18,44 @@ const utils_1 = require("./lib/utils");
|
|
|
17
18
|
const consts_1 = require("./lib/consts");
|
|
18
19
|
const types_1 = require("./lib/types");
|
|
19
20
|
// const debug = nodeDebug('af-rabbitmq')
|
|
20
|
-
const debug = logger_1.default.
|
|
21
|
-
const
|
|
22
|
-
const PASSWORD = process.env.RABBITMQ_PASSWORD || 'guest';
|
|
23
|
-
const HOST = process.env.RABBITMQ_SERVICE_HOST || 'localhost';
|
|
21
|
+
const debug = logger_1.default.debug.bind(logger_1.default);
|
|
22
|
+
const PUBLISH_TIMEOUT = 1000 * 10;
|
|
24
23
|
const HEARTBEAT = '60';
|
|
25
24
|
class RabbitMq {
|
|
25
|
+
static parseMsg(msg) {
|
|
26
|
+
let { content } = msg;
|
|
27
|
+
content = content.toString();
|
|
28
|
+
try {
|
|
29
|
+
content = JSON.parse(content);
|
|
30
|
+
}
|
|
31
|
+
catch (e) { }
|
|
32
|
+
return {
|
|
33
|
+
...msg,
|
|
34
|
+
content,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
static validateName(type, name) {
|
|
38
|
+
if (!name || name === '') {
|
|
39
|
+
throw new rabbitError_1.default(`error while using ${type} with no name`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
static getPublishOptions(customHeaders = {}) {
|
|
43
|
+
const trace = (0, zehut_1.getCurrentPayload)();
|
|
44
|
+
const user = trace?.context?.get(consts_1.USER_OBJECT);
|
|
45
|
+
const traceId = trace?.context?.get(consts_1.TRACING_HEADER);
|
|
46
|
+
const outbreakTrace = zehut_1.outbreak.getCurrentContext();
|
|
47
|
+
return {
|
|
48
|
+
timestamp: (0, moment_1.default)().unix(),
|
|
49
|
+
timeout: PUBLISH_TIMEOUT,
|
|
50
|
+
headers: {
|
|
51
|
+
creationTimestamp: (0, moment_1.default)().valueOf(),
|
|
52
|
+
...customHeaders,
|
|
53
|
+
[consts_1.USER_TRACING_HEADER]: user?.id,
|
|
54
|
+
[consts_1.TRACING_HEADER]: traceId || outbreakTrace?.context?.get(consts_1.TRACING_HEADER),
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
}
|
|
26
58
|
constructor(options, redisConfig) {
|
|
27
|
-
// PUBLISH_TIMEOUT = Number(process.env.RABBITMQ_PUBLISH_TIMEOUT) || 60000;
|
|
28
|
-
// PUBLISH_ERROR_MSG = `rabbit: publish timeout(${this.PUBLISH_TIMEOUT}ms) has pass, exchange: `;
|
|
29
59
|
this.DISCONNECT_MSG = 'rabbit: connection disconnect';
|
|
30
60
|
this.RECONNECT_MSG = 'rabbit: connection disconnect - reconnecting';
|
|
31
61
|
this.consumers = [];
|
|
@@ -43,6 +73,7 @@ class RabbitMq {
|
|
|
43
73
|
};
|
|
44
74
|
this.ack = (channel, msg, shouldUpdateRedisTimestamp = false, releaseLock = null) => async (userMsg) => {
|
|
45
75
|
if (msg) {
|
|
76
|
+
debug('rabbit acking message', { deliveryTag: msg.fields.deliveryTag });
|
|
46
77
|
await channel.ack(msg);
|
|
47
78
|
const { properties: { headers } } = msg;
|
|
48
79
|
const timestamp = headers?.creationTimestamp;
|
|
@@ -75,25 +106,30 @@ class RabbitMq {
|
|
|
75
106
|
: 1,
|
|
76
107
|
});
|
|
77
108
|
}
|
|
109
|
+
debug('rabbit nacking message', { deliveryTag: msg.fields.deliveryTag });
|
|
78
110
|
await channel.ack(msg);
|
|
79
111
|
}
|
|
80
112
|
else {
|
|
81
113
|
logger_1.default.error('no channel or msg', {
|
|
82
|
-
channel: channel ? channel.name : '',
|
|
83
114
|
msg,
|
|
84
115
|
});
|
|
85
116
|
}
|
|
86
117
|
};
|
|
87
118
|
this.em = new events_1.EventEmitter();
|
|
88
119
|
this.channel = null;
|
|
89
|
-
this.
|
|
90
|
-
this.
|
|
120
|
+
this.publishChannelSetupPromise = null;
|
|
121
|
+
this.connectionsMap = {
|
|
122
|
+
[types_1.ConnectionPurpose.Consume]: { connection: null, creatingConnection: false },
|
|
123
|
+
[types_1.ConnectionPurpose.Publish]: { connection: null, creatingConnection: false },
|
|
124
|
+
};
|
|
91
125
|
this.exchanges = {};
|
|
92
126
|
this.queues = {};
|
|
127
|
+
this.queueSetupPromises = {};
|
|
128
|
+
this.consumers = [];
|
|
93
129
|
this.options = options;
|
|
94
|
-
this.redisClient = redisConfig && redis_1.default(redisConfig);
|
|
130
|
+
this.redisClient = redisConfig && (0, redis_1.default)(redisConfig);
|
|
95
131
|
if (this.redisClient) {
|
|
96
|
-
this.redisLock = util_1.promisify(redis_lock_1.default(this.redisClient));
|
|
132
|
+
this.redisLock = (0, util_1.promisify)((0, redis_lock_1.default)(this.redisClient));
|
|
97
133
|
}
|
|
98
134
|
this.consumersTags = [];
|
|
99
135
|
logger_1.default.info(`rabbit: [gracefully-shutdown] adding gracefully shutdown for process.pid ${process.pid}`);
|
|
@@ -106,67 +142,52 @@ class RabbitMq {
|
|
|
106
142
|
});
|
|
107
143
|
}
|
|
108
144
|
}
|
|
109
|
-
|
|
110
|
-
let { content } = msg;
|
|
111
|
-
content = content.toString();
|
|
112
|
-
try {
|
|
113
|
-
content = JSON.parse(content);
|
|
114
|
-
}
|
|
115
|
-
catch (e) { }
|
|
116
|
-
return {
|
|
117
|
-
...msg,
|
|
118
|
-
content,
|
|
119
|
-
};
|
|
120
|
-
}
|
|
121
|
-
static validateName(type, name) {
|
|
122
|
-
if (!name || name === '') {
|
|
123
|
-
throw new rabbitError_1.default(`error while using ${type} with no name`);
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
static getPublishOptions(customHeaders = {}) {
|
|
127
|
-
const trace = zehut_1.getCurrentPayload();
|
|
128
|
-
const user = trace?.context?.get(consts_1.USER_OBJECT);
|
|
129
|
-
const traceId = trace?.context?.get(consts_1.TRACING_HEADER);
|
|
130
|
-
const outbreakTrace = zehut_1.outbreak.getCurrentContext();
|
|
131
|
-
return {
|
|
132
|
-
timestamp: moment_1.default().unix(),
|
|
133
|
-
headers: {
|
|
134
|
-
creationTimestamp: moment_1.default().valueOf(),
|
|
135
|
-
...customHeaders,
|
|
136
|
-
[consts_1.USER_TRACING_HEADER]: user?.id,
|
|
137
|
-
[consts_1.TRACING_HEADER]: traceId || outbreakTrace?.context?.get(consts_1.TRACING_HEADER),
|
|
138
|
-
},
|
|
139
|
-
};
|
|
140
|
-
}
|
|
141
|
-
async getConnection() {
|
|
145
|
+
async getConnection(connectionPurpose) {
|
|
142
146
|
return new Promise(async (resolve, reject) => {
|
|
147
|
+
let { connection, creatingConnection } = this.connectionsMap[connectionPurpose];
|
|
143
148
|
if (this.blockReconnect) {
|
|
144
149
|
debug('rabbit: block reconnect');
|
|
145
150
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
146
151
|
// @ts-ignore
|
|
147
152
|
return resolve();
|
|
148
153
|
}
|
|
149
|
-
if (
|
|
150
|
-
if (this.options?.disableReconnect ||
|
|
154
|
+
if (connection !== null) {
|
|
155
|
+
if (this.options?.disableReconnect || connection?.isConnected()) {
|
|
151
156
|
debug('rabbit: connection - is connected');
|
|
152
157
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
153
158
|
// @ts-ignore
|
|
154
|
-
return resolve(
|
|
159
|
+
return resolve(connection);
|
|
155
160
|
}
|
|
156
161
|
debug('rabbit: connection - reconnecting');
|
|
157
162
|
}
|
|
158
|
-
if (
|
|
163
|
+
if (creatingConnection) {
|
|
159
164
|
debug('rabbit: creating connection emi');
|
|
160
165
|
this.em.once(consts_1.CONNECTION_CREATED_CONST, resolve);
|
|
161
166
|
this.em.once(consts_1.CONNECTION_FAILED_CONST, reject);
|
|
162
167
|
return;
|
|
163
168
|
}
|
|
164
|
-
this.creatingConnection = true;
|
|
169
|
+
this.connectionsMap[connectionPurpose].creatingConnection = true;
|
|
165
170
|
let isResolved = false;
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
171
|
+
// It is import to use it as a function and not as a variable
|
|
172
|
+
// because of k8s changes the env variables
|
|
173
|
+
// and we want to use the new values
|
|
174
|
+
const findServers = () => {
|
|
175
|
+
const userName = process.env.RABBITMQ_USERNAME || 'guest';
|
|
176
|
+
const password = process.env.RABBITMQ_PASSWORD || 'guest';
|
|
177
|
+
const host = this.options?.rabbitHost || process.env.RABBITMQ_SERVICE_HOST || 'localhost';
|
|
178
|
+
debug('rabbit: creating connection', { host, userName, HEARTBEAT });
|
|
179
|
+
return [`amqp://${userName}:${password}@${host}?heartbeat=${HEARTBEAT}`];
|
|
180
|
+
};
|
|
181
|
+
const defaultUrls = findServers();
|
|
182
|
+
const newConnection = await (0, amqp_connection_manager_1.connect)(defaultUrls, {
|
|
183
|
+
findServers,
|
|
184
|
+
});
|
|
185
|
+
if (!newConnection) {
|
|
186
|
+
logger_1.default.error('rabbit: couldnt create a connection');
|
|
187
|
+
return resolve(connection);
|
|
188
|
+
}
|
|
189
|
+
this.connectionsMap[connectionPurpose].connection = newConnection;
|
|
190
|
+
newConnection.on('error', (err) => {
|
|
170
191
|
logger_1.default.error('rabbit: connection error', { err });
|
|
171
192
|
if (!isResolved) {
|
|
172
193
|
isResolved = true;
|
|
@@ -174,7 +195,8 @@ class RabbitMq {
|
|
|
174
195
|
this.em.emit(consts_1.CONNECTION_FAILED_CONST, err);
|
|
175
196
|
}
|
|
176
197
|
});
|
|
177
|
-
|
|
198
|
+
newConnection.on('connectFailed', (err) => {
|
|
199
|
+
this.consumersTags = [];
|
|
178
200
|
logger_1.default.error('rabbit: connection connectFailed', { err });
|
|
179
201
|
if (!isResolved) {
|
|
180
202
|
isResolved = true;
|
|
@@ -182,12 +204,9 @@ class RabbitMq {
|
|
|
182
204
|
this.em.emit(consts_1.CONNECTION_FAILED_CONST, err);
|
|
183
205
|
}
|
|
184
206
|
});
|
|
185
|
-
|
|
207
|
+
newConnection.on('disconnect', ({ err }) => {
|
|
208
|
+
this.consumersTags = [];
|
|
186
209
|
debug('rabbit: connection closed');
|
|
187
|
-
this.exchanges = {};
|
|
188
|
-
this.queues = {};
|
|
189
|
-
this.connection = null;
|
|
190
|
-
this.channel = null;
|
|
191
210
|
if (this.options?.disableReconnect) {
|
|
192
211
|
logger_1.default.error(`${this.DISCONNECT_MSG}${err && ` - ${err}`}`);
|
|
193
212
|
this.blockReconnect = true;
|
|
@@ -196,96 +215,99 @@ class RabbitMq {
|
|
|
196
215
|
logger_1.default.error(`${this.RECONNECT_MSG}${err && ` - ${err}`}`);
|
|
197
216
|
}
|
|
198
217
|
});
|
|
199
|
-
|
|
218
|
+
newConnection.once('connect', async () => {
|
|
200
219
|
debug('rabbit: connection established');
|
|
201
|
-
|
|
202
|
-
this.
|
|
203
|
-
this.em.emit(consts_1.CONNECTION_CREATED_CONST, connection);
|
|
220
|
+
this.connectionsMap[connectionPurpose].creatingConnection = false;
|
|
221
|
+
this.em.emit(consts_1.CONNECTION_CREATED_CONST, newConnection);
|
|
204
222
|
isResolved = true;
|
|
205
|
-
resolve(
|
|
223
|
+
resolve(newConnection);
|
|
206
224
|
});
|
|
207
225
|
});
|
|
208
226
|
}
|
|
209
|
-
async getNewChannel({ name = utils_1.rand().toString(), onClose = null
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
227
|
+
async getNewChannel({ name = (0, utils_1.rand)().toString(), onClose = null, options = {}, connectionPurpose = types_1.ConnectionPurpose.Consume, }) {
|
|
228
|
+
let connection;
|
|
229
|
+
try {
|
|
230
|
+
connection = await this.getConnection(connectionPurpose);
|
|
231
|
+
}
|
|
232
|
+
catch (e) {
|
|
233
|
+
logger_1.default.error(`rabbit: error on get connection for new channel ${name} `, { e });
|
|
234
|
+
throw e;
|
|
235
|
+
}
|
|
236
|
+
if (!connection) {
|
|
237
|
+
throw new Error(`rabbit: couldnt get connection for new channel ${name}`);
|
|
238
|
+
}
|
|
239
|
+
const channel = connection.createChannel({ ...options });
|
|
240
|
+
(0, events_1.once)(channel, 'close').then((args) => {
|
|
241
|
+
logger_1.default.error(`rabbit: channel ${name} closed`);
|
|
242
|
+
onClose?.(args);
|
|
243
|
+
});
|
|
244
|
+
try {
|
|
245
|
+
await (0, events_1.once)(channel, 'connect');
|
|
246
|
+
debug(`rabbit: channel ${name} CONNECTED`);
|
|
247
|
+
return channel;
|
|
248
|
+
}
|
|
249
|
+
catch (err) {
|
|
250
|
+
logger_1.default.error(`rabbit: channel error ${name} error`, { err });
|
|
251
|
+
throw err;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
async assertChannel({ force = false, connectionPurpose = types_1.ConnectionPurpose.Consume }) {
|
|
255
|
+
if (!this.publishChannelSetupPromise) {
|
|
256
|
+
this.publishChannelSetupPromise = new Promise(async (resolve, reject) => {
|
|
257
|
+
if (this.channel && !force) {
|
|
258
|
+
return resolve(this.channel);
|
|
219
259
|
}
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
260
|
+
try {
|
|
261
|
+
const channel = await this.getNewChannel({ connectionPurpose });
|
|
262
|
+
channel.on('error', (err) => {
|
|
263
|
+
logger_1.default.error('rabbit: channel error', { err });
|
|
264
|
+
});
|
|
265
|
+
this.channel = channel;
|
|
266
|
+
resolve(channel);
|
|
267
|
+
}
|
|
268
|
+
catch (e) {
|
|
269
|
+
reject(e);
|
|
225
270
|
}
|
|
226
271
|
});
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
isResolved = true;
|
|
230
|
-
resolve(channel);
|
|
231
|
-
});
|
|
232
|
-
});
|
|
233
|
-
}
|
|
234
|
-
async assertChannel({ force = false } = {}) {
|
|
235
|
-
return new Promise(async (resolve, reject) => {
|
|
236
|
-
if (this.channel && !force) {
|
|
237
|
-
return resolve(this.channel);
|
|
238
|
-
}
|
|
239
|
-
try {
|
|
240
|
-
const channel = await this.getNewChannel({
|
|
241
|
-
onClose: () => {
|
|
242
|
-
this.channel = null;
|
|
243
|
-
},
|
|
244
|
-
});
|
|
245
|
-
this.channel = channel;
|
|
246
|
-
resolve(channel);
|
|
247
|
-
}
|
|
248
|
-
catch (e) {
|
|
249
|
-
reject(e);
|
|
250
|
-
}
|
|
251
|
-
});
|
|
272
|
+
}
|
|
273
|
+
return this.publishChannelSetupPromise;
|
|
252
274
|
}
|
|
253
275
|
async assertExchange(exchangeName, options) {
|
|
254
|
-
const channel = await this.assertChannel();
|
|
276
|
+
const channel = await this.assertChannel({ connectionPurpose: options.connectionPurpose });
|
|
255
277
|
if (this.exchanges[exchangeName]) {
|
|
256
278
|
return this.exchanges[exchangeName];
|
|
257
279
|
}
|
|
258
|
-
const exchange = await utils_1.assertExchangeFanout(channel, exchangeName);
|
|
280
|
+
const exchange = await (0, utils_1.assertExchangeFanout)(channel, exchangeName);
|
|
259
281
|
this.exchanges[exchangeName] = exchange;
|
|
260
282
|
return exchange;
|
|
261
283
|
}
|
|
262
|
-
async getQueueLength(queue) {
|
|
284
|
+
async getQueueLength(queue, connectionPurpose = types_1.ConnectionPurpose.Consume) {
|
|
263
285
|
RabbitMq.validateName('queue', queue);
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
286
|
+
let { connection } = this.connectionsMap[connectionPurpose];
|
|
287
|
+
const { channel } = this;
|
|
288
|
+
if (!channel) {
|
|
289
|
+
throw new Error('channel is not defined');
|
|
290
|
+
}
|
|
291
|
+
debug('rabbit: getting queue length', { queue, connected: connection?.isConnected() });
|
|
292
|
+
return channel?.checkQueue(queue);
|
|
267
293
|
}
|
|
268
|
-
async deleteQueue(queue) {
|
|
294
|
+
async deleteQueue(queue, connectionPurpose) {
|
|
269
295
|
RabbitMq.validateName('queue', queue);
|
|
270
|
-
const channel = await this.assertChannel();
|
|
296
|
+
const channel = await this.assertChannel({ connectionPurpose });
|
|
271
297
|
logger_1.default.info('rabbit: deleting queue', { queue });
|
|
272
298
|
const deleteQueueRes = await channel.deleteQueue(queue);
|
|
273
299
|
debug('queue deleted', deleteQueueRes);
|
|
274
300
|
return deleteQueueRes;
|
|
275
301
|
}
|
|
276
|
-
async bindQueue(queue, exchange) {
|
|
277
|
-
const channel = await this.assertChannel();
|
|
302
|
+
async bindQueue(queue, exchange, connectionPurpose) {
|
|
303
|
+
const channel = await this.assertChannel({ connectionPurpose });
|
|
278
304
|
await channel.addSetup((setupChannel) => setupChannel.bindQueue(queue, exchange, ''));
|
|
279
305
|
return channel.bindQueue(queue, exchange, '');
|
|
280
306
|
}
|
|
281
|
-
async
|
|
282
|
-
let queue
|
|
283
|
-
RabbitMq.validateName('queue', queueName);
|
|
284
|
-
if (this.queues[queueName]) {
|
|
285
|
-
return this.queues[queueName];
|
|
286
|
-
}
|
|
307
|
+
async setupQueue(queueName, connectionPurpose, options) {
|
|
308
|
+
let queue;
|
|
287
309
|
try {
|
|
288
|
-
const channel = await this.assertChannel();
|
|
310
|
+
const channel = await this.assertChannel({ connectionPurpose });
|
|
289
311
|
debug('assertQueue->channel.addSetup', { queueName });
|
|
290
312
|
await channel.addSetup((setupChannel) => setupChannel.assertQueue(queueName, options));
|
|
291
313
|
debug('assertQueue->channel.assertQueue', { queueName });
|
|
@@ -295,11 +317,11 @@ class RabbitMq {
|
|
|
295
317
|
logger_1.default.error('rabbit: assertQueue error', { queueName, options, error: e });
|
|
296
318
|
if (!this.options?.dontRetryAssert) {
|
|
297
319
|
debug('retrying assertQueue', { queueName });
|
|
298
|
-
const channel = await this.assertChannel({ force: true });
|
|
299
|
-
await this.deleteQueue(queueName);
|
|
300
|
-
debug('
|
|
320
|
+
const channel = await this.assertChannel({ force: true, connectionPurpose });
|
|
321
|
+
await this.deleteQueue(queueName, connectionPurpose);
|
|
322
|
+
debug('retrying assertQueue->channel.addSetup', { queueName });
|
|
301
323
|
await channel.addSetup((setupChannel) => setupChannel.assertQueue(queueName, options));
|
|
302
|
-
debug('
|
|
324
|
+
debug('retrying assertQueue->channel.assertQueue', { queueName });
|
|
303
325
|
queue = await channel.assertQueue(queueName, options);
|
|
304
326
|
}
|
|
305
327
|
else {
|
|
@@ -309,25 +331,31 @@ class RabbitMq {
|
|
|
309
331
|
this.queues[queueName] = queueName;
|
|
310
332
|
return queue;
|
|
311
333
|
}
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
}
|
|
334
|
+
async assertQueue(queueName, connectionPurpose, options) {
|
|
335
|
+
RabbitMq.validateName('queue', queueName);
|
|
336
|
+
if (this.queues[queueName]) {
|
|
337
|
+
delete this.queueSetupPromises[queueName];
|
|
338
|
+
return this.queues[queueName];
|
|
339
|
+
}
|
|
340
|
+
if (this.queueSetupPromises[queueName]) {
|
|
341
|
+
return this.queueSetupPromises[queueName];
|
|
342
|
+
}
|
|
343
|
+
this.queueSetupPromises[queueName] = this.setupQueue(queueName, connectionPurpose, options);
|
|
344
|
+
return this.queueSetupPromises[queueName];
|
|
318
345
|
}
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
if (
|
|
322
|
-
|
|
323
|
-
|
|
346
|
+
saveConsumer(queue, callback, options) {
|
|
347
|
+
const isConsumerExist = this.consumers.some((consumer) => consumer.queue === queue);
|
|
348
|
+
if (!isConsumerExist) {
|
|
349
|
+
logger_1.default.info(`rabbit: consumer: ${queue} saved in consumer array`);
|
|
350
|
+
this.consumers.push({
|
|
351
|
+
queue,
|
|
352
|
+
callback,
|
|
353
|
+
options,
|
|
354
|
+
});
|
|
324
355
|
}
|
|
325
356
|
}
|
|
326
357
|
async consume(queue, callback, options) {
|
|
327
|
-
|
|
328
|
-
this.consumeFromRabbit(queue, callback, options);
|
|
329
|
-
}
|
|
330
|
-
return this.saveConsumer(queue, callback, options);
|
|
358
|
+
await this.consumeFromRabbit(queue, callback, options);
|
|
331
359
|
}
|
|
332
360
|
async lockRedisIfNeeded(msg, options) {
|
|
333
361
|
const { properties: { headers } } = msg;
|
|
@@ -346,6 +374,8 @@ class RabbitMq {
|
|
|
346
374
|
async consumeFromRabbit(queue, callback, options) {
|
|
347
375
|
const optionsWithDefaults = { ...consts_1.DEFAULT_OPTIONS, ...options };
|
|
348
376
|
RabbitMq.validateName('queue', queue);
|
|
377
|
+
this.saveConsumer(queue, callback, options);
|
|
378
|
+
const uniqueId = (0, node_crypto_1.randomUUID)();
|
|
349
379
|
const { limit, deadMessageTtl, useConsumeWithLock, lockTimeout, auditContext, enableRabbitTrace, } = optionsWithDefaults;
|
|
350
380
|
if (useConsumeWithLock) {
|
|
351
381
|
if (!this.redisLock) {
|
|
@@ -353,11 +383,11 @@ class RabbitMq {
|
|
|
353
383
|
}
|
|
354
384
|
logger_1.default.info(`rabbit: Consuming with lock from queue ${queue} with lockTimeout: ${lockTimeout}ms`);
|
|
355
385
|
}
|
|
356
|
-
const channel = await this.getNewChannel({
|
|
357
|
-
return channel.addSetup(async (
|
|
358
|
-
await
|
|
359
|
-
await
|
|
360
|
-
const { consumerTag } = await
|
|
386
|
+
const channel = await this.getNewChannel({ connectionPurpose: types_1.ConnectionPurpose.Consume });
|
|
387
|
+
return channel.addSetup(async (confirmChannel) => {
|
|
388
|
+
await confirmChannel.assertQueue(queue, options ? { messageTtl: options.messageTtl } : undefined);
|
|
389
|
+
await confirmChannel.prefetch(limit, true);
|
|
390
|
+
const { consumerTag } = await confirmChannel.consume(queue, async (msg) => {
|
|
361
391
|
if (!msg) {
|
|
362
392
|
return null;
|
|
363
393
|
}
|
|
@@ -365,7 +395,7 @@ class RabbitMq {
|
|
|
365
395
|
const userId = msg.properties.headers[consts_1.USER_TRACING_HEADER];
|
|
366
396
|
const parsedMessage = RabbitMq.parseMsg(msg);
|
|
367
397
|
const releaseLock = await this.lockRedisIfNeeded(parsedMessage, optionsWithDefaults);
|
|
368
|
-
const trace = zehut_1.newTrace(zehut_1.traceTypes.RABBIT);
|
|
398
|
+
const trace = (0, zehut_1.newTrace)(zehut_1.traceTypes.RABBIT);
|
|
369
399
|
// setting also outbreak trace as part of legacy code
|
|
370
400
|
const outbreakTrace = zehut_1.outbreak.newTrace(zehut_1.traceTypes.RABBIT);
|
|
371
401
|
// enableRabbitTrace is a flag to protect from different flows that doesn't work with permission
|
|
@@ -373,13 +403,13 @@ class RabbitMq {
|
|
|
373
403
|
if (userId && enableRabbitTrace) {
|
|
374
404
|
try {
|
|
375
405
|
await Promise.all([
|
|
376
|
-
zehut_1.createOrSetRabbitTrace(trace, userId),
|
|
377
|
-
zehut_1.createOrSetRabbitTrace(outbreakTrace, userId),
|
|
406
|
+
(0, zehut_1.createOrSetRabbitTrace)(trace, userId),
|
|
407
|
+
(0, zehut_1.createOrSetRabbitTrace)(outbreakTrace, userId),
|
|
378
408
|
]);
|
|
379
409
|
}
|
|
380
410
|
catch (e) {
|
|
381
411
|
logger_1.default.error('rabbit: failed to setRabbitTrace', { userId, e });
|
|
382
|
-
|
|
412
|
+
return this.nack(confirmChannel, queue, optionsWithDefaults, { messageTtl: deadMessageTtl }, msg, releaseLock)(msg);
|
|
383
413
|
}
|
|
384
414
|
}
|
|
385
415
|
if (traceId) {
|
|
@@ -392,13 +422,30 @@ class RabbitMq {
|
|
|
392
422
|
const shouldConsume = await this.shouldConsumeMessageByTimestamp(parsedMessage);
|
|
393
423
|
if (!shouldConsume) {
|
|
394
424
|
await this.unlockRedisIfNeeded(releaseLock);
|
|
395
|
-
return this.ack(
|
|
425
|
+
return this.ack(confirmChannel, msg)(msg);
|
|
396
426
|
}
|
|
427
|
+
let messageAcked = false;
|
|
428
|
+
// setting the localAck function to be used in the callback
|
|
429
|
+
const localAck = async () => {
|
|
430
|
+
if (messageAcked) {
|
|
431
|
+
return;
|
|
432
|
+
}
|
|
433
|
+
messageAcked = true;
|
|
434
|
+
return this.ack(confirmChannel, msg, true, releaseLock)(msg);
|
|
435
|
+
};
|
|
436
|
+
const localNack = async (_, nackOptions = {}) => {
|
|
437
|
+
if (messageAcked) {
|
|
438
|
+
return;
|
|
439
|
+
}
|
|
440
|
+
debug('rabbit localNack', { messageAcked, uniqueId, deliveryTag: msg.fields.deliveryTag });
|
|
441
|
+
messageAcked = true;
|
|
442
|
+
return this.nack(confirmChannel, queue, optionsWithDefaults, { messageTtl: deadMessageTtl }, msg, releaseLock)(msg, nackOptions);
|
|
443
|
+
};
|
|
397
444
|
try {
|
|
398
|
-
await callback(parsedMessage,
|
|
445
|
+
await callback(parsedMessage, localAck, localNack);
|
|
399
446
|
}
|
|
400
447
|
catch (e) {
|
|
401
|
-
await
|
|
448
|
+
await localNack(msg);
|
|
402
449
|
}
|
|
403
450
|
}, types_1.CONSUMER_DEFAULT_OPTIONS);
|
|
404
451
|
if (!consumerTag) {
|
|
@@ -406,7 +453,7 @@ class RabbitMq {
|
|
|
406
453
|
}
|
|
407
454
|
else {
|
|
408
455
|
logger_1.default.info(`rabbit: adding tag ${consumerTag} to the array.`);
|
|
409
|
-
this.consumersTags.push([
|
|
456
|
+
this.consumersTags.push([confirmChannel, consumerTag]);
|
|
410
457
|
}
|
|
411
458
|
});
|
|
412
459
|
}
|
|
@@ -415,9 +462,10 @@ class RabbitMq {
|
|
|
415
462
|
RabbitMq.validateName('exchange', exchange);
|
|
416
463
|
RabbitMq.validateName('queue', queue);
|
|
417
464
|
const { limit, deadMessageTtl } = optionsWithDefaults;
|
|
418
|
-
|
|
465
|
+
await this.saveConsumer(queue, callback, options);
|
|
466
|
+
const channel = await this.getNewChannel({ name: `consume - exchange - ${exchange} -queue - ${queue}`, connectionPurpose: types_1.ConnectionPurpose.Consume });
|
|
419
467
|
return channel.addSetup(async (c) => {
|
|
420
|
-
const assertExchange = await utils_1.assertExchangeFanout(c, exchange);
|
|
468
|
+
const assertExchange = await (0, utils_1.assertExchangeFanout)(c, exchange);
|
|
421
469
|
await c.assertQueue(queue);
|
|
422
470
|
this.exchanges[exchange] = assertExchange;
|
|
423
471
|
await c.prefetch(limit, true);
|
|
@@ -428,30 +476,64 @@ class RabbitMq {
|
|
|
428
476
|
});
|
|
429
477
|
}
|
|
430
478
|
async publish(exchange, content, customHeaders) {
|
|
431
|
-
return utils_1.wrapSetImmediate(async () => {
|
|
479
|
+
return (0, utils_1.wrapSetImmediate)(async () => {
|
|
432
480
|
RabbitMq.validateName('exchange', exchange);
|
|
433
|
-
const channel = await this.assertChannel();
|
|
434
|
-
await this.assertExchange(exchange);
|
|
481
|
+
const channel = await this.assertChannel({ connectionPurpose: types_1.ConnectionPurpose.Publish });
|
|
482
|
+
await this.assertExchange(exchange, { connectionPurpose: types_1.ConnectionPurpose.Publish });
|
|
435
483
|
await channel.publish(exchange, '', Buffer.from(JSON.stringify(content)), RabbitMq.getPublishOptions(customHeaders));
|
|
436
484
|
});
|
|
437
485
|
}
|
|
438
|
-
async sendToQueue(queue, content, options, customHeaders
|
|
439
|
-
|
|
486
|
+
async sendToQueue(queue, content, options, customHeaders) {
|
|
487
|
+
try {
|
|
488
|
+
await this.assertChannel({ connectionPurpose: types_1.ConnectionPurpose.Publish });
|
|
489
|
+
}
|
|
490
|
+
catch (e) {
|
|
491
|
+
logger_1.default.error(`rabbit sendToQueue: failed to send assert channel when sending to queue ${queue}`, { e });
|
|
492
|
+
throw e;
|
|
493
|
+
}
|
|
494
|
+
try {
|
|
440
495
|
RabbitMq.validateName('queue', queue);
|
|
441
|
-
await this.
|
|
442
|
-
|
|
496
|
+
await this.assertQueue(queue, types_1.ConnectionPurpose.Publish, options);
|
|
497
|
+
}
|
|
498
|
+
catch (e) {
|
|
499
|
+
logger_1.default.error(`rabbit sendToQueue: failed to assert queue ${queue}`, { e });
|
|
500
|
+
throw e;
|
|
501
|
+
}
|
|
502
|
+
try {
|
|
443
503
|
const res = await this.channel?.sendToQueue(queue, Buffer.from(JSON.stringify(content)), RabbitMq.getPublishOptions(customHeaders));
|
|
444
504
|
debug(`rabbit: sending to queue ${queue}`, { res });
|
|
445
505
|
return res;
|
|
446
|
-
};
|
|
447
|
-
if (isBlocking) {
|
|
448
|
-
return callback();
|
|
449
506
|
}
|
|
450
|
-
|
|
507
|
+
catch (e) {
|
|
508
|
+
const isConnected = await this.isConnected(types_1.ConnectionPurpose.Publish);
|
|
509
|
+
logger_1.default.error(`rabbit sendToQueue: failed to send to queue ${queue}, isConnected: ${isConnected}`, { e });
|
|
510
|
+
throw e;
|
|
511
|
+
}
|
|
451
512
|
}
|
|
452
|
-
async isConnected() {
|
|
453
|
-
const connection = await this.getConnection();
|
|
454
|
-
|
|
513
|
+
async isConnected(connectionPurpose) {
|
|
514
|
+
const connection = await this.getConnection(connectionPurpose);
|
|
515
|
+
if (!connection) {
|
|
516
|
+
logger_1.default.error('rabbit: isConnected - false');
|
|
517
|
+
return false;
|
|
518
|
+
}
|
|
519
|
+
const isConnected = connection.isConnected();
|
|
520
|
+
if (!isConnected) {
|
|
521
|
+
logger_1.default.error('rabbit: isConnected - false');
|
|
522
|
+
return false;
|
|
523
|
+
}
|
|
524
|
+
const channel = await this.assertChannel({ connectionPurpose });
|
|
525
|
+
try {
|
|
526
|
+
await Promise.all([
|
|
527
|
+
channel.waitForConnect(),
|
|
528
|
+
...this.consumers.map((c) => channel.checkQueue(c.queue)),
|
|
529
|
+
]);
|
|
530
|
+
}
|
|
531
|
+
catch (e) {
|
|
532
|
+
logger_1.default.error('rabbit: isConnected - false');
|
|
533
|
+
return false;
|
|
534
|
+
}
|
|
535
|
+
logger_1.default.info('rabbit: isConnected - true');
|
|
536
|
+
return true;
|
|
455
537
|
}
|
|
456
538
|
async gracefulShutdown(signal) {
|
|
457
539
|
const tagsNumber = this.consumersTags.length;
|
package/dist/lib/redis.d.ts
CHANGED