@autofleet/rabbit 3.2.4 → 3.2.6-testing
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/.env +3 -0
- package/dist/index.d.ts +9 -13
- package/dist/index.js +100 -166
- package/dist/lib/consts.d.ts +0 -5
- package/dist/lib/consts.js +1 -6
- package/dist/lib/redis.d.ts +1 -1
- package/dist/lib/types.d.ts +5 -21
- package/dist/lib/types.js +0 -9
- package/dist/lib/utils.d.ts +0 -1
- package/dist/lib/utils.js +5 -4
- package/package.json +1 -1
- package/src/index.ts +39 -8
package/.env
ADDED
package/dist/index.d.ts
CHANGED
|
@@ -3,7 +3,6 @@ import { EventEmitter } from 'events';
|
|
|
3
3
|
import { AmqpConnectionManager, ChannelWrapper } from 'amqp-connection-manager';
|
|
4
4
|
import { ConfirmChannel, ConsumeMessage, Options, Replies } from 'amqplib';
|
|
5
5
|
import { RedisConfig } from './lib/redis';
|
|
6
|
-
import { TRACING_HEADER, USER_TRACING_HEADER } from './lib/consts';
|
|
7
6
|
import { CallbackFunction, ConsumeMessageOrNull, ConsumeOptions, CustomMessageHeaders, QueuesCache, RedisLockType, ExchangesCache } from './lib/types';
|
|
8
7
|
export interface IAfRabbitMq {
|
|
9
8
|
ack: any;
|
|
@@ -17,9 +16,6 @@ export interface IAfRabbitMq {
|
|
|
17
16
|
sendToQueue: any;
|
|
18
17
|
redisClient?: any;
|
|
19
18
|
}
|
|
20
|
-
interface NackOptions {
|
|
21
|
-
skipRetry?: boolean;
|
|
22
|
-
}
|
|
23
19
|
export interface AfRabbitOptions {
|
|
24
20
|
disableReconnect?: boolean;
|
|
25
21
|
/**
|
|
@@ -28,17 +24,16 @@ export interface AfRabbitOptions {
|
|
|
28
24
|
*/
|
|
29
25
|
dontGracefulShutdown?: boolean;
|
|
30
26
|
/**
|
|
31
|
-
*
|
|
27
|
+
* retry on creation error
|
|
32
28
|
* @default false
|
|
33
29
|
*/
|
|
34
|
-
|
|
35
|
-
rabbitHost?: string;
|
|
30
|
+
retryAssert?: boolean;
|
|
36
31
|
}
|
|
37
|
-
|
|
32
|
+
type newChannelOpts = {
|
|
38
33
|
name?: string;
|
|
39
34
|
onClose?: null | ((args: any | null) => void);
|
|
40
35
|
};
|
|
41
|
-
|
|
36
|
+
type assertChannelOpts = {
|
|
42
37
|
channelName?: string;
|
|
43
38
|
force?: boolean;
|
|
44
39
|
};
|
|
@@ -47,14 +42,14 @@ declare class RabbitMq implements IAfRabbitMq {
|
|
|
47
42
|
static validateName(type: string, name: string): void;
|
|
48
43
|
static getPublishOptions(customHeaders?: CustomMessageHeaders): {
|
|
49
44
|
timestamp: number;
|
|
50
|
-
timeout: number;
|
|
51
45
|
headers: {
|
|
52
|
-
"x-af-user-id": any;
|
|
53
46
|
"x-trace-id": any;
|
|
54
47
|
redisTimestampValidationKey?: string | undefined;
|
|
55
48
|
creationTimestamp: number;
|
|
56
49
|
};
|
|
57
50
|
};
|
|
51
|
+
PUBLISH_TIMEOUT: number;
|
|
52
|
+
PUBLISH_ERROR_MSG: string;
|
|
58
53
|
DISCONNECT_MSG: string;
|
|
59
54
|
RECONNECT_MSG: string;
|
|
60
55
|
channel: ChannelWrapper | null;
|
|
@@ -72,8 +67,8 @@ declare class RabbitMq implements IAfRabbitMq {
|
|
|
72
67
|
private consumers;
|
|
73
68
|
constructor(options?: AfRabbitOptions, redisConfig?: RedisConfig);
|
|
74
69
|
private shouldConsumeMessageByTimestamp;
|
|
75
|
-
ack: (channel:
|
|
76
|
-
nack: (channel:
|
|
70
|
+
ack: (channel: ChannelWrapper, msg: ConsumeMessageOrNull, shouldUpdateRedisTimestamp?: boolean, releaseLock?: null) => (userMsg: ConsumeMessage) => Promise<any>;
|
|
71
|
+
nack: (channel: ChannelWrapper, queue: string, options: any, deadQueueOptions: Options.AssertQueue, msg: ConsumeMessageOrNull, releaseLock: any) => (userMsg: ConsumeMessageOrNull, { skipRetry, }?: any) => Promise<any>;
|
|
77
72
|
getConnection(): Promise<AmqpConnectionManager>;
|
|
78
73
|
getNewChannel({ name, onClose }?: newChannelOpts): Promise<ChannelWrapper>;
|
|
79
74
|
assertChannel({ force }?: assertChannelOpts): Promise<ChannelWrapper>;
|
|
@@ -83,6 +78,7 @@ declare class RabbitMq implements IAfRabbitMq {
|
|
|
83
78
|
bindQueue(queue: string, exchange: string): Promise<void>;
|
|
84
79
|
assertQueue(queueName: string, options?: Options.AssertQueue): Promise<any>;
|
|
85
80
|
private saveConsumer;
|
|
81
|
+
private loadConsumers;
|
|
86
82
|
consume(queue: string, callback: CallbackFunction, options?: ConsumeOptions): Promise<any>;
|
|
87
83
|
private lockRedisIfNeeded;
|
|
88
84
|
private unlockRedisIfNeeded;
|
package/dist/index.js
CHANGED
|
@@ -9,19 +9,50 @@ const util_1 = require("util");
|
|
|
9
9
|
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
|
-
const
|
|
12
|
+
const outbreak_1 = require("@autofleet/outbreak");
|
|
13
13
|
const logger_1 = __importDefault(require("./logger"));
|
|
14
14
|
const rabbitError_1 = __importDefault(require("./lib/rabbitError"));
|
|
15
15
|
const redis_1 = __importDefault(require("./lib/redis"));
|
|
16
16
|
const utils_1 = require("./lib/utils");
|
|
17
17
|
const consts_1 = require("./lib/consts");
|
|
18
|
-
const types_1 = require("./lib/types");
|
|
19
18
|
// const debug = nodeDebug('af-rabbitmq')
|
|
20
19
|
const debug = logger_1.default.info;
|
|
21
|
-
const
|
|
20
|
+
const USERNAME = process.env.RABBITMQ_USERNAME || 'guest';
|
|
21
|
+
const PASSWORD = process.env.RABBITMQ_PASSWORD || 'guest';
|
|
22
|
+
const HOST = process.env.RABBITMQ_SERVICE_HOST || 'localhost';
|
|
22
23
|
const HEARTBEAT = '60';
|
|
23
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, outbreak_1.getCurrentContext)();
|
|
44
|
+
return {
|
|
45
|
+
timestamp: (0, moment_1.default)().unix(),
|
|
46
|
+
headers: {
|
|
47
|
+
creationTimestamp: (0, moment_1.default)().valueOf(),
|
|
48
|
+
...customHeaders,
|
|
49
|
+
[consts_1.TRACING_HEADER]: trace?.context?.get(consts_1.TRACING_HEADER),
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
}
|
|
24
53
|
constructor(options, redisConfig) {
|
|
54
|
+
this.PUBLISH_TIMEOUT = Number(process.env.RABBITMQ_PUBLISH_TIMEOUT) || 60000;
|
|
55
|
+
this.PUBLISH_ERROR_MSG = `rabbit: publish timeout(${this.PUBLISH_TIMEOUT}ms) has pass, exchange: `;
|
|
25
56
|
this.DISCONNECT_MSG = 'rabbit: connection disconnect';
|
|
26
57
|
this.RECONNECT_MSG = 'rabbit: connection disconnect - reconnecting';
|
|
27
58
|
this.consumers = [];
|
|
@@ -75,6 +106,7 @@ class RabbitMq {
|
|
|
75
106
|
}
|
|
76
107
|
else {
|
|
77
108
|
logger_1.default.error('no channel or msg', {
|
|
109
|
+
channel: channel ? channel.name : '',
|
|
78
110
|
msg,
|
|
79
111
|
});
|
|
80
112
|
}
|
|
@@ -86,9 +118,9 @@ class RabbitMq {
|
|
|
86
118
|
this.exchanges = {};
|
|
87
119
|
this.queues = {};
|
|
88
120
|
this.options = options;
|
|
89
|
-
this.redisClient = redisConfig && redis_1.default(redisConfig);
|
|
121
|
+
this.redisClient = redisConfig && (0, redis_1.default)(redisConfig);
|
|
90
122
|
if (this.redisClient) {
|
|
91
|
-
this.redisLock = util_1.promisify(redis_lock_1.default(this.redisClient));
|
|
123
|
+
this.redisLock = (0, util_1.promisify)((0, redis_lock_1.default)(this.redisClient));
|
|
92
124
|
}
|
|
93
125
|
this.consumersTags = [];
|
|
94
126
|
logger_1.default.info(`rabbit: [gracefully-shutdown] adding gracefully shutdown for process.pid ${process.pid}`);
|
|
@@ -101,41 +133,8 @@ class RabbitMq {
|
|
|
101
133
|
});
|
|
102
134
|
}
|
|
103
135
|
}
|
|
104
|
-
static parseMsg(msg) {
|
|
105
|
-
let { content } = msg;
|
|
106
|
-
content = content.toString();
|
|
107
|
-
try {
|
|
108
|
-
content = JSON.parse(content);
|
|
109
|
-
}
|
|
110
|
-
catch (e) { }
|
|
111
|
-
return {
|
|
112
|
-
...msg,
|
|
113
|
-
content,
|
|
114
|
-
};
|
|
115
|
-
}
|
|
116
|
-
static validateName(type, name) {
|
|
117
|
-
if (!name || name === '') {
|
|
118
|
-
throw new rabbitError_1.default(`error while using ${type} with no name`);
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
static getPublishOptions(customHeaders = {}) {
|
|
122
|
-
const trace = zehut_1.getCurrentPayload();
|
|
123
|
-
const user = trace?.context?.get(consts_1.USER_OBJECT);
|
|
124
|
-
const traceId = trace?.context?.get(consts_1.TRACING_HEADER);
|
|
125
|
-
const outbreakTrace = zehut_1.outbreak.getCurrentContext();
|
|
126
|
-
return {
|
|
127
|
-
timestamp: moment_1.default().unix(),
|
|
128
|
-
timeout: PUBLISH_TIMEOUT,
|
|
129
|
-
headers: {
|
|
130
|
-
creationTimestamp: moment_1.default().valueOf(),
|
|
131
|
-
...customHeaders,
|
|
132
|
-
[consts_1.USER_TRACING_HEADER]: user?.id,
|
|
133
|
-
[consts_1.TRACING_HEADER]: traceId || outbreakTrace?.context?.get(consts_1.TRACING_HEADER),
|
|
134
|
-
},
|
|
135
|
-
};
|
|
136
|
-
}
|
|
137
136
|
async getConnection() {
|
|
138
|
-
return new Promise(async (resolve
|
|
137
|
+
return new Promise(async (resolve) => {
|
|
139
138
|
if (this.blockReconnect) {
|
|
140
139
|
debug('rabbit: block reconnect');
|
|
141
140
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
@@ -143,55 +142,29 @@ class RabbitMq {
|
|
|
143
142
|
return resolve();
|
|
144
143
|
}
|
|
145
144
|
if (this.connection !== null) {
|
|
145
|
+
debug('rabbit: connection already exist');
|
|
146
146
|
if (this.options?.disableReconnect || this.connection?.isConnected()) {
|
|
147
|
-
debug('rabbit: connection - is connected');
|
|
148
147
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
149
148
|
// @ts-ignore
|
|
150
149
|
return resolve(this.connection);
|
|
151
150
|
}
|
|
152
|
-
debug('rabbit: connection - reconnecting');
|
|
151
|
+
debug('rabbit: connection already exist - reconnecting');
|
|
153
152
|
}
|
|
154
153
|
if (this.creatingConnection) {
|
|
155
154
|
debug('rabbit: creating connection emi');
|
|
156
155
|
this.em.once(consts_1.CONNECTION_CREATED_CONST, resolve);
|
|
157
|
-
this.em.once(consts_1.CONNECTION_FAILED_CONST, reject);
|
|
158
156
|
return;
|
|
159
157
|
}
|
|
160
158
|
this.creatingConnection = true;
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
// because of k8s changes the env variables
|
|
164
|
-
// and we want to use the new values
|
|
165
|
-
const findServers = () => {
|
|
166
|
-
const userName = process.env.RABBITMQ_USERNAME || 'guest';
|
|
167
|
-
const password = process.env.RABBITMQ_PASSWORD || 'guest';
|
|
168
|
-
const host = this.options?.rabbitHost || process.env.RABBITMQ_SERVICE_HOST || 'localhost';
|
|
169
|
-
debug('rabbit: creating connection', { host, userName, HEARTBEAT });
|
|
170
|
-
return [`amqp://${userName}:${password}@${host}?heartbeat=${HEARTBEAT}`];
|
|
171
|
-
};
|
|
172
|
-
const defaultUrls = findServers();
|
|
173
|
-
const connection = await amqp_connection_manager_1.connect(defaultUrls, {
|
|
174
|
-
findServers,
|
|
175
|
-
});
|
|
159
|
+
debug('rabbit: creating connection', { HOST, USERNAME, HEARTBEAT });
|
|
160
|
+
const connection = await (0, amqp_connection_manager_1.connect)([`amqp://${USERNAME}:${PASSWORD}@${HOST}?heartbeat=${HEARTBEAT}}`]);
|
|
176
161
|
this.connection = connection;
|
|
177
|
-
this.connection.on('error', (err) => {
|
|
178
|
-
logger_1.default.error('rabbit: connection error', { err });
|
|
179
|
-
if (!isResolved) {
|
|
180
|
-
isResolved = true;
|
|
181
|
-
reject(err);
|
|
182
|
-
this.em.emit(consts_1.CONNECTION_FAILED_CONST, err);
|
|
183
|
-
}
|
|
184
|
-
});
|
|
185
|
-
this.connection.on('connectFailed', (err) => {
|
|
186
|
-
logger_1.default.error('rabbit: connection connectFailed', { err });
|
|
187
|
-
if (!isResolved) {
|
|
188
|
-
isResolved = true;
|
|
189
|
-
reject(err);
|
|
190
|
-
this.em.emit(consts_1.CONNECTION_FAILED_CONST, err);
|
|
191
|
-
}
|
|
192
|
-
});
|
|
193
162
|
this.connection.on('disconnect', ({ err }) => {
|
|
194
163
|
debug('rabbit: connection closed');
|
|
164
|
+
this.exchanges = {};
|
|
165
|
+
this.queues = {};
|
|
166
|
+
this.connection = null;
|
|
167
|
+
this.channel = null;
|
|
195
168
|
if (this.options?.disableReconnect) {
|
|
196
169
|
logger_1.default.error(`${this.DISCONNECT_MSG}${err && ` - ${err}`}`);
|
|
197
170
|
this.blockReconnect = true;
|
|
@@ -202,24 +175,19 @@ class RabbitMq {
|
|
|
202
175
|
});
|
|
203
176
|
this.connection.once('connect', async () => {
|
|
204
177
|
debug('rabbit: connection established');
|
|
178
|
+
await this.loadConsumers();
|
|
205
179
|
this.creatingConnection = false;
|
|
206
180
|
this.em.emit(consts_1.CONNECTION_CREATED_CONST, connection);
|
|
207
|
-
isResolved = true;
|
|
208
181
|
resolve(connection);
|
|
209
182
|
});
|
|
210
183
|
});
|
|
211
184
|
}
|
|
212
|
-
async getNewChannel({ name =
|
|
185
|
+
async getNewChannel({ name = '', onClose = null } = {}) {
|
|
213
186
|
return new Promise(async (resolve, reject) => {
|
|
214
187
|
const connection = await this.getConnection();
|
|
215
188
|
const channel = connection.createChannel({});
|
|
216
|
-
let isResolved = false;
|
|
217
189
|
channel.on('error', (err) => {
|
|
218
|
-
logger_1.default.error(`rabbit: channel
|
|
219
|
-
if (!isResolved) {
|
|
220
|
-
isResolved = true;
|
|
221
|
-
reject(err);
|
|
222
|
-
}
|
|
190
|
+
logger_1.default.error(`rabbit: channel ${name} error`, { err });
|
|
223
191
|
});
|
|
224
192
|
channel.on('close', (...args) => {
|
|
225
193
|
logger_1.default.error(`rabbit: channel ${name} closed`, { args });
|
|
@@ -229,7 +197,6 @@ class RabbitMq {
|
|
|
229
197
|
});
|
|
230
198
|
channel.once('connect', () => {
|
|
231
199
|
debug(`rabbit: channel ${name} CONNECTED`);
|
|
232
|
-
isResolved = true;
|
|
233
200
|
resolve(channel);
|
|
234
201
|
});
|
|
235
202
|
});
|
|
@@ -240,9 +207,10 @@ class RabbitMq {
|
|
|
240
207
|
return resolve(this.channel);
|
|
241
208
|
}
|
|
242
209
|
try {
|
|
243
|
-
const channel = await this.getNewChannel({
|
|
244
|
-
|
|
245
|
-
|
|
210
|
+
const channel = await this.getNewChannel({
|
|
211
|
+
onClose: () => {
|
|
212
|
+
this.channel = null;
|
|
213
|
+
},
|
|
246
214
|
});
|
|
247
215
|
this.channel = channel;
|
|
248
216
|
resolve(channel);
|
|
@@ -257,26 +225,25 @@ class RabbitMq {
|
|
|
257
225
|
if (this.exchanges[exchangeName]) {
|
|
258
226
|
return this.exchanges[exchangeName];
|
|
259
227
|
}
|
|
260
|
-
const exchange = await utils_1.assertExchangeFanout(channel, exchangeName);
|
|
228
|
+
const exchange = await (0, utils_1.assertExchangeFanout)(channel, exchangeName);
|
|
261
229
|
this.exchanges[exchangeName] = exchange;
|
|
262
230
|
return exchange;
|
|
263
231
|
}
|
|
264
232
|
async getQueueLength(queue) {
|
|
265
233
|
RabbitMq.validateName('queue', queue);
|
|
266
|
-
const
|
|
267
|
-
if (!channel) {
|
|
268
|
-
throw new Error('channel is not defined');
|
|
269
|
-
}
|
|
234
|
+
const channel = await this.assertChannel();
|
|
270
235
|
debug('rabbit: getting queue length', { queue, connected: this.connection?.isConnected() });
|
|
271
|
-
return channel
|
|
236
|
+
return channel.checkQueue(queue);
|
|
272
237
|
}
|
|
273
238
|
async deleteQueue(queue) {
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
239
|
+
return new Promise(async (resolve, reject) => {
|
|
240
|
+
RabbitMq.validateName('queue', queue);
|
|
241
|
+
const channel = await this.assertChannel();
|
|
242
|
+
logger_1.default.info('rabbit: deleting queue', { queue });
|
|
243
|
+
const deleteQueueRes = await channel.deleteQueue(queue);
|
|
244
|
+
debug('queue deleted', deleteQueueRes);
|
|
245
|
+
resolve(deleteQueueRes);
|
|
246
|
+
});
|
|
280
247
|
}
|
|
281
248
|
async bindQueue(queue, exchange) {
|
|
282
249
|
const channel = await this.assertChannel();
|
|
@@ -298,7 +265,7 @@ class RabbitMq {
|
|
|
298
265
|
}
|
|
299
266
|
catch (e) {
|
|
300
267
|
logger_1.default.error('rabbit: assertQueue error', { queueName, options, error: e });
|
|
301
|
-
if (
|
|
268
|
+
if (this.options?.retryAssert) {
|
|
302
269
|
debug('retrying assertQueue', { queueName });
|
|
303
270
|
const channel = await this.assertChannel({ force: true });
|
|
304
271
|
await this.deleteQueue(queueName);
|
|
@@ -321,8 +288,18 @@ class RabbitMq {
|
|
|
321
288
|
options,
|
|
322
289
|
});
|
|
323
290
|
}
|
|
291
|
+
async loadConsumers() {
|
|
292
|
+
debug('rabbit: loading consumers', { consumers: this.consumers.length });
|
|
293
|
+
if (this.consumers.length > 0) {
|
|
294
|
+
await Promise.all(this.consumers.map((consumer) => this
|
|
295
|
+
.consumeFromRabbit(consumer.queue, consumer.callback, consumer.options)));
|
|
296
|
+
}
|
|
297
|
+
}
|
|
324
298
|
async consume(queue, callback, options) {
|
|
325
|
-
|
|
299
|
+
if (this.connection && !this.creatingConnection) {
|
|
300
|
+
this.consumeFromRabbit(queue, callback, options);
|
|
301
|
+
}
|
|
302
|
+
return this.saveConsumer(queue, callback, options);
|
|
326
303
|
}
|
|
327
304
|
async lockRedisIfNeeded(msg, options) {
|
|
328
305
|
const { properties: { headers } } = msg;
|
|
@@ -341,83 +318,46 @@ class RabbitMq {
|
|
|
341
318
|
async consumeFromRabbit(queue, callback, options) {
|
|
342
319
|
const optionsWithDefaults = { ...consts_1.DEFAULT_OPTIONS, ...options };
|
|
343
320
|
RabbitMq.validateName('queue', queue);
|
|
344
|
-
const { limit, deadMessageTtl, useConsumeWithLock, lockTimeout,
|
|
321
|
+
const { limit, deadMessageTtl, useConsumeWithLock, lockTimeout, } = optionsWithDefaults;
|
|
345
322
|
if (useConsumeWithLock) {
|
|
346
323
|
if (!this.redisLock) {
|
|
347
324
|
throw new Error('Usage of consumeWithLock requires RedisInstance');
|
|
348
325
|
}
|
|
349
326
|
logger_1.default.info(`rabbit: Consuming with lock from queue ${queue} with lockTimeout: ${lockTimeout}ms`);
|
|
350
327
|
}
|
|
351
|
-
const channel = await this.getNewChannel({ name: `consume
|
|
352
|
-
return channel.addSetup(async (
|
|
353
|
-
await
|
|
354
|
-
await
|
|
355
|
-
const { consumerTag } = await
|
|
328
|
+
const channel = await this.getNewChannel({ name: `consume-${queue}` });
|
|
329
|
+
return channel.addSetup(async (c) => {
|
|
330
|
+
await c.assertQueue(queue, options ? { messageTtl: options.messageTtl } : undefined);
|
|
331
|
+
await c.prefetch(limit, true);
|
|
332
|
+
const { consumerTag } = await c.consume(queue, async (msg) => {
|
|
356
333
|
if (!msg) {
|
|
357
334
|
return null;
|
|
358
335
|
}
|
|
359
336
|
const traceId = msg.properties.headers[consts_1.TRACING_HEADER];
|
|
360
|
-
const userId = msg.properties.headers[consts_1.USER_TRACING_HEADER];
|
|
361
|
-
const parsedMessage = RabbitMq.parseMsg(msg);
|
|
362
|
-
const releaseLock = await this.lockRedisIfNeeded(parsedMessage, optionsWithDefaults);
|
|
363
|
-
const trace = zehut_1.newTrace(zehut_1.traceTypes.RABBIT);
|
|
364
|
-
// setting also outbreak trace as part of legacy code
|
|
365
|
-
const outbreakTrace = zehut_1.outbreak.newTrace(zehut_1.traceTypes.RABBIT);
|
|
366
|
-
// enableRabbitTrace is a flag to protect from different flows that doesn't work with permission
|
|
367
|
-
// and we don't want to fail the flow because of it
|
|
368
|
-
if (userId && enableRabbitTrace) {
|
|
369
|
-
try {
|
|
370
|
-
await Promise.all([
|
|
371
|
-
zehut_1.createOrSetRabbitTrace(trace, userId),
|
|
372
|
-
zehut_1.createOrSetRabbitTrace(outbreakTrace, userId),
|
|
373
|
-
]);
|
|
374
|
-
}
|
|
375
|
-
catch (e) {
|
|
376
|
-
logger_1.default.error('rabbit: failed to setRabbitTrace', { userId, e });
|
|
377
|
-
await this.nack(confirmChannel, queue, optionsWithDefaults, { messageTtl: deadMessageTtl }, msg, releaseLock)(msg);
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
337
|
if (traceId) {
|
|
338
|
+
const trace = (0, outbreak_1.newTrace)(outbreak_1.traceTypes.RABBIT);
|
|
381
339
|
trace?.context?.set(consts_1.TRACING_HEADER, traceId);
|
|
382
|
-
outbreakTrace?.context.set(consts_1.TRACING_HEADER, traceId);
|
|
383
|
-
}
|
|
384
|
-
if (auditContext) {
|
|
385
|
-
await auditContext(queue);
|
|
386
340
|
}
|
|
341
|
+
const parsedMessage = RabbitMq.parseMsg(msg);
|
|
342
|
+
const releaseLock = await this.lockRedisIfNeeded(parsedMessage, optionsWithDefaults);
|
|
387
343
|
const shouldConsume = await this.shouldConsumeMessageByTimestamp(parsedMessage);
|
|
388
344
|
if (!shouldConsume) {
|
|
389
345
|
await this.unlockRedisIfNeeded(releaseLock);
|
|
390
|
-
return this.ack(
|
|
346
|
+
return this.ack(channel, msg)(msg);
|
|
391
347
|
}
|
|
392
|
-
let messageAcked = false;
|
|
393
|
-
// setting the localAck function to be used in the callback
|
|
394
|
-
const localAck = async () => {
|
|
395
|
-
if (messageAcked) {
|
|
396
|
-
return;
|
|
397
|
-
}
|
|
398
|
-
messageAcked = true;
|
|
399
|
-
return this.ack(confirmChannel, msg, true, releaseLock)(msg);
|
|
400
|
-
};
|
|
401
|
-
const localNack = async (_, nackOptions = {}) => {
|
|
402
|
-
if (messageAcked) {
|
|
403
|
-
return;
|
|
404
|
-
}
|
|
405
|
-
messageAcked = true;
|
|
406
|
-
return this.nack(confirmChannel, queue, optionsWithDefaults, { messageTtl: deadMessageTtl }, msg, releaseLock)(msg, nackOptions);
|
|
407
|
-
};
|
|
408
348
|
try {
|
|
409
|
-
await callback(parsedMessage,
|
|
349
|
+
await callback(parsedMessage, this.ack(channel, msg, true, releaseLock), this.nack(channel, queue, optionsWithDefaults, { messageTtl: deadMessageTtl }, msg, releaseLock));
|
|
410
350
|
}
|
|
411
351
|
catch (e) {
|
|
412
|
-
await
|
|
352
|
+
await this.nack(channel, queue, optionsWithDefaults, { messageTtl: deadMessageTtl }, msg, releaseLock)(msg);
|
|
413
353
|
}
|
|
414
|
-
}
|
|
354
|
+
});
|
|
415
355
|
if (!consumerTag) {
|
|
416
356
|
logger_1.default.error(`rabbit: failed to consume from queue ${queue}`);
|
|
417
357
|
}
|
|
418
358
|
else {
|
|
419
359
|
logger_1.default.info(`rabbit: adding tag ${consumerTag} to the array.`);
|
|
420
|
-
this.consumersTags.push([
|
|
360
|
+
this.consumersTags.push([c, consumerTag]);
|
|
421
361
|
}
|
|
422
362
|
});
|
|
423
363
|
}
|
|
@@ -426,9 +366,9 @@ class RabbitMq {
|
|
|
426
366
|
RabbitMq.validateName('exchange', exchange);
|
|
427
367
|
RabbitMq.validateName('queue', queue);
|
|
428
368
|
const { limit, deadMessageTtl } = optionsWithDefaults;
|
|
429
|
-
const channel = await this.getNewChannel(
|
|
369
|
+
const channel = await this.getNewChannel();
|
|
430
370
|
return channel.addSetup(async (c) => {
|
|
431
|
-
const assertExchange = await utils_1.assertExchangeFanout(c, exchange);
|
|
371
|
+
const assertExchange = await (0, utils_1.assertExchangeFanout)(c, exchange);
|
|
432
372
|
await c.assertQueue(queue);
|
|
433
373
|
this.exchanges[exchange] = assertExchange;
|
|
434
374
|
await c.prefetch(limit, true);
|
|
@@ -439,7 +379,7 @@ class RabbitMq {
|
|
|
439
379
|
});
|
|
440
380
|
}
|
|
441
381
|
async publish(exchange, content, customHeaders) {
|
|
442
|
-
return utils_1.wrapSetImmediate(async () => {
|
|
382
|
+
return (0, utils_1.wrapSetImmediate)(async () => {
|
|
443
383
|
RabbitMq.validateName('exchange', exchange);
|
|
444
384
|
const channel = await this.assertChannel();
|
|
445
385
|
await this.assertExchange(exchange);
|
|
@@ -448,23 +388,17 @@ class RabbitMq {
|
|
|
448
388
|
}
|
|
449
389
|
async sendToQueue(queue, content, options, customHeaders, isBlocking) {
|
|
450
390
|
const callback = async () => {
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
return res;
|
|
458
|
-
}
|
|
459
|
-
catch (e) {
|
|
460
|
-
logger_1.default.error(`rabbit: failed to send to queue ${queue}`, { e });
|
|
461
|
-
throw e;
|
|
462
|
-
}
|
|
391
|
+
RabbitMq.validateName('queue', queue);
|
|
392
|
+
await this.assertChannel();
|
|
393
|
+
await this.assertQueue(queue, options);
|
|
394
|
+
const res = await this.channel?.sendToQueue(queue, Buffer.from(JSON.stringify(content)), RabbitMq.getPublishOptions(customHeaders));
|
|
395
|
+
debug(`rabbit: sending to queue ${queue}`, { res });
|
|
396
|
+
return res;
|
|
463
397
|
};
|
|
464
398
|
if (isBlocking) {
|
|
465
399
|
return callback();
|
|
466
400
|
}
|
|
467
|
-
return utils_1.wrapSetImmediate(callback);
|
|
401
|
+
return (0, utils_1.wrapSetImmediate)(callback);
|
|
468
402
|
}
|
|
469
403
|
async isConnected() {
|
|
470
404
|
const connection = await this.getConnection();
|
package/dist/lib/consts.d.ts
CHANGED
|
@@ -2,17 +2,12 @@ export declare const DEFAULT_DEAD_TTL_TWO_DAYS: number;
|
|
|
2
2
|
export declare const DEFAULT_LOCK_TIMEOUT: number;
|
|
3
3
|
export declare const RETRY_HEADER = "x-retry-count";
|
|
4
4
|
export declare const TRACING_HEADER = "x-trace-id";
|
|
5
|
-
export declare const USER_TRACING_HEADER = "x-af-user-id";
|
|
6
|
-
export declare const USER_OBJECT = "userObject";
|
|
7
5
|
export declare const DEFAULT_USE_CONSUME_WITH_LOCK = false;
|
|
8
6
|
export declare const CONNECTION_CREATED_CONST = "connectionCreated";
|
|
9
|
-
export declare const CONNECTION_FAILED_CONST = "connectionFailed";
|
|
10
7
|
export declare const DEFAULT_OPTIONS: {
|
|
11
8
|
limit: number;
|
|
12
9
|
retries: number;
|
|
13
10
|
deadMessageTtl: number;
|
|
14
11
|
lockTimeout: number;
|
|
15
12
|
useConsumeWithLock: boolean;
|
|
16
|
-
auditContext: null;
|
|
17
|
-
enableRabbitTrace: boolean;
|
|
18
13
|
};
|
package/dist/lib/consts.js
CHANGED
|
@@ -1,21 +1,16 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.DEFAULT_OPTIONS = exports.
|
|
3
|
+
exports.DEFAULT_OPTIONS = exports.CONNECTION_CREATED_CONST = exports.DEFAULT_USE_CONSUME_WITH_LOCK = exports.TRACING_HEADER = exports.RETRY_HEADER = exports.DEFAULT_LOCK_TIMEOUT = exports.DEFAULT_DEAD_TTL_TWO_DAYS = void 0;
|
|
4
4
|
exports.DEFAULT_DEAD_TTL_TWO_DAYS = 60000 * 60 * 12;
|
|
5
5
|
exports.DEFAULT_LOCK_TIMEOUT = 1000 * 5;
|
|
6
6
|
exports.RETRY_HEADER = 'x-retry-count';
|
|
7
7
|
exports.TRACING_HEADER = 'x-trace-id';
|
|
8
|
-
exports.USER_TRACING_HEADER = 'x-af-user-id';
|
|
9
|
-
exports.USER_OBJECT = 'userObject';
|
|
10
8
|
exports.DEFAULT_USE_CONSUME_WITH_LOCK = false;
|
|
11
9
|
exports.CONNECTION_CREATED_CONST = 'connectionCreated';
|
|
12
|
-
exports.CONNECTION_FAILED_CONST = 'connectionFailed';
|
|
13
10
|
exports.DEFAULT_OPTIONS = {
|
|
14
11
|
limit: 1,
|
|
15
12
|
retries: 1,
|
|
16
13
|
deadMessageTtl: exports.DEFAULT_DEAD_TTL_TWO_DAYS,
|
|
17
14
|
lockTimeout: exports.DEFAULT_LOCK_TIMEOUT,
|
|
18
15
|
useConsumeWithLock: exports.DEFAULT_USE_CONSUME_WITH_LOCK,
|
|
19
|
-
auditContext: null,
|
|
20
|
-
enableRabbitTrace: false,
|
|
21
16
|
};
|
package/dist/lib/redis.d.ts
CHANGED
package/dist/lib/types.d.ts
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import { ConsumeMessage
|
|
1
|
+
import { ConsumeMessage } from 'amqplib';
|
|
2
2
|
export interface ExchangesCache {
|
|
3
3
|
[key: string]: any;
|
|
4
4
|
}
|
|
5
5
|
export interface QueuesCache {
|
|
6
6
|
[key: string]: any;
|
|
7
7
|
}
|
|
8
|
-
export
|
|
8
|
+
export type CustomMessageHeaders = {
|
|
9
9
|
redisTimestampValidationKey?: string;
|
|
10
10
|
};
|
|
11
|
-
export
|
|
12
|
-
export
|
|
11
|
+
export type RedisLockType = (args0?: string, arg1?: number) => Promise<any>;
|
|
12
|
+
export type ConsumeMessageOrNull = ConsumeMessage | null;
|
|
13
13
|
export interface ConsumeOptions {
|
|
14
14
|
retries?: number;
|
|
15
15
|
deadMessageTtl?: number;
|
|
@@ -17,21 +17,5 @@ export interface ConsumeOptions {
|
|
|
17
17
|
limit?: number;
|
|
18
18
|
lockTimeout?: number;
|
|
19
19
|
useConsumeWithLock?: boolean;
|
|
20
|
-
auditContext?: any;
|
|
21
|
-
enableRabbitTrace?: boolean;
|
|
22
20
|
}
|
|
23
|
-
export
|
|
24
|
-
export declare type newChannelOpts = {
|
|
25
|
-
name?: string;
|
|
26
|
-
onClose?: null | ((args: any | null) => void);
|
|
27
|
-
};
|
|
28
|
-
export declare type assertChannelOpts = {
|
|
29
|
-
channelName?: string;
|
|
30
|
-
force?: boolean;
|
|
31
|
-
};
|
|
32
|
-
export declare type AfConsumer = {
|
|
33
|
-
queue: string;
|
|
34
|
-
callback: CallbackFunction;
|
|
35
|
-
options: ConsumeOptions | undefined;
|
|
36
|
-
};
|
|
37
|
-
export declare const CONSUMER_DEFAULT_OPTIONS: Options.Consume;
|
|
21
|
+
export type CallbackFunction = (msg: ConsumeMessage, ack: any, nack: any) => Promise<any>;
|
package/dist/lib/types.js
CHANGED
|
@@ -1,11 +1,2 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.CONSUMER_DEFAULT_OPTIONS = void 0;
|
|
4
|
-
const HA_PROMOTE_ON_FAILURE = 'ha-promote-on-failure';
|
|
5
|
-
const HA_PROMOTE_ON_SHUTDOWN = 'ha-promote-on-shutdown';
|
|
6
|
-
exports.CONSUMER_DEFAULT_OPTIONS = {
|
|
7
|
-
arguments: {
|
|
8
|
-
[HA_PROMOTE_ON_FAILURE]: 'always',
|
|
9
|
-
[HA_PROMOTE_ON_SHUTDOWN]: 'always',
|
|
10
|
-
},
|
|
11
|
-
};
|
package/dist/lib/utils.d.ts
CHANGED
|
@@ -2,4 +2,3 @@ import { ChannelWrapper } from 'amqp-connection-manager';
|
|
|
2
2
|
import { ConfirmChannel } from 'amqplib';
|
|
3
3
|
export declare const assertExchangeFanout: (c: ChannelWrapper | ConfirmChannel, exchangeName: string) => Promise<import("amqplib").Replies.AssertExchange>;
|
|
4
4
|
export declare const wrapSetImmediate: (callback: () => any) => Promise<any>;
|
|
5
|
-
export declare const rand: () => number;
|
package/dist/lib/utils.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
4
|
-
|
|
5
|
-
exports.
|
|
3
|
+
exports.wrapSetImmediate = exports.assertExchangeFanout = void 0;
|
|
4
|
+
const assertExchangeFanout = async (c, exchangeName) => c.assertExchange(exchangeName, 'fanout');
|
|
5
|
+
exports.assertExchangeFanout = assertExchangeFanout;
|
|
6
|
+
const wrapSetImmediate = (callback) => new Promise((resolve, reject) => {
|
|
6
7
|
setImmediate(async () => {
|
|
7
8
|
try {
|
|
8
9
|
const value = await callback();
|
|
@@ -13,4 +14,4 @@ exports.wrapSetImmediate = (callback) => new Promise((resolve, reject) => {
|
|
|
13
14
|
}
|
|
14
15
|
});
|
|
15
16
|
});
|
|
16
|
-
exports.
|
|
17
|
+
exports.wrapSetImmediate = wrapSetImmediate;
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -4,13 +4,16 @@ import { EventEmitter } from 'events';
|
|
|
4
4
|
import { promisify } from 'util';
|
|
5
5
|
import moment from 'moment';
|
|
6
6
|
import RedisLock from 'redis-lock';
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
AmqpConnectionManager, ChannelWrapper, connect, CreateChannelOpts,
|
|
9
|
+
} from 'amqp-connection-manager';
|
|
8
10
|
import {
|
|
9
11
|
ConfirmChannel, ConsumeMessage, Options, Replies,
|
|
10
12
|
} from 'amqplib';
|
|
11
13
|
import {
|
|
12
14
|
getCurrentPayload, newTrace, traceTypes, createOrSetRabbitTrace, outbreak,
|
|
13
15
|
} from '@autofleet/zehut';
|
|
16
|
+
import { v4 } from 'uuid';
|
|
14
17
|
import logger from './logger';
|
|
15
18
|
import RabbitError from './lib/rabbitError';
|
|
16
19
|
import getRedisInstance, { RedisConfig } from './lib/redis';
|
|
@@ -78,6 +81,7 @@ export interface AfRabbitOptions {
|
|
|
78
81
|
type newChannelOpts = {
|
|
79
82
|
name?: string;
|
|
80
83
|
onClose?: null | ((args: any | null) => void);
|
|
84
|
+
options?: CreateChannelOpts | undefined;
|
|
81
85
|
};
|
|
82
86
|
|
|
83
87
|
type assertChannelOpts = {
|
|
@@ -167,6 +171,7 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
167
171
|
this.creatingConnection = false;
|
|
168
172
|
this.exchanges = {};
|
|
169
173
|
this.queues = {};
|
|
174
|
+
this.consumers = [];
|
|
170
175
|
this.options = options;
|
|
171
176
|
this.redisClient = redisConfig && getRedisInstance(redisConfig);
|
|
172
177
|
if (this.redisClient) {
|
|
@@ -200,6 +205,7 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
200
205
|
|
|
201
206
|
public ack = (channel: ConfirmChannel, msg: ConsumeMessageOrNull, shouldUpdateRedisTimestamp = false, releaseLock = null) => async (userMsg: ConsumeMessage) : Promise<any> => {
|
|
202
207
|
if (msg) {
|
|
208
|
+
debug('rabbit acking message', { deliveryTag: msg.fields.deliveryTag });
|
|
203
209
|
await channel.ack(msg);
|
|
204
210
|
const { properties: { headers } } = msg;
|
|
205
211
|
const timestamp = headers?.creationTimestamp;
|
|
@@ -249,6 +255,7 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
249
255
|
: 1,
|
|
250
256
|
});
|
|
251
257
|
}
|
|
258
|
+
debug('rabbit nacking message', { deliveryTag: msg.fields.deliveryTag });
|
|
252
259
|
await channel.ack(msg);
|
|
253
260
|
} else {
|
|
254
261
|
logger.error('no channel or msg', {
|
|
@@ -312,6 +319,7 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
312
319
|
});
|
|
313
320
|
|
|
314
321
|
this.connection.on('connectFailed', (err) => {
|
|
322
|
+
this.consumersTags = [];
|
|
315
323
|
logger.error('rabbit: connection connectFailed', { err });
|
|
316
324
|
if (!isResolved) {
|
|
317
325
|
isResolved = true;
|
|
@@ -321,6 +329,7 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
321
329
|
});
|
|
322
330
|
|
|
323
331
|
this.connection.on('disconnect', ({ err }) => {
|
|
332
|
+
this.consumersTags = [];
|
|
324
333
|
debug('rabbit: connection closed');
|
|
325
334
|
if (this.options?.disableReconnect) {
|
|
326
335
|
logger.error(`${this.DISCONNECT_MSG}${err && ` - ${err}`}`);
|
|
@@ -339,10 +348,11 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
339
348
|
});
|
|
340
349
|
}
|
|
341
350
|
|
|
342
|
-
async getNewChannel({ name = rand().toString(), onClose = null }: newChannelOpts = {}) {
|
|
351
|
+
async getNewChannel({ name = rand().toString(), onClose = null, options = {} }: newChannelOpts = {}) {
|
|
343
352
|
return new Promise<ChannelWrapper>(async (resolve, reject) => {
|
|
344
353
|
const connection: AmqpConnectionManager = await this.getConnection();
|
|
345
354
|
const channel = connection.createChannel({
|
|
355
|
+
...options,
|
|
346
356
|
});
|
|
347
357
|
|
|
348
358
|
let isResolved = false;
|
|
@@ -374,8 +384,7 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
374
384
|
}
|
|
375
385
|
|
|
376
386
|
try {
|
|
377
|
-
const channel = await this.getNewChannel({
|
|
378
|
-
});
|
|
387
|
+
const channel = await this.getNewChannel({});
|
|
379
388
|
channel.on('error', (err) => {
|
|
380
389
|
logger.error('rabbit: channel error', { err });
|
|
381
390
|
});
|
|
@@ -463,7 +472,8 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
463
472
|
}
|
|
464
473
|
|
|
465
474
|
async consume(queue: string, callback: CallbackFunction, options?: ConsumeOptions): Promise<any> {
|
|
466
|
-
|
|
475
|
+
await this.consumeFromRabbit(queue, callback, options);
|
|
476
|
+
return this.saveConsumer(queue, callback, options);
|
|
467
477
|
}
|
|
468
478
|
|
|
469
479
|
private async lockRedisIfNeeded(msg: any, options: any) {
|
|
@@ -489,6 +499,7 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
489
499
|
private async consumeFromRabbit(queue: string, callback: CallbackFunction, options?: ConsumeOptions): Promise<any> {
|
|
490
500
|
const optionsWithDefaults = { ...DEFAULT_OPTIONS, ...options };
|
|
491
501
|
RabbitMq.validateName('queue', queue);
|
|
502
|
+
const uniqueId = v4();
|
|
492
503
|
const {
|
|
493
504
|
limit, deadMessageTtl, useConsumeWithLock, lockTimeout, auditContext, enableRabbitTrace,
|
|
494
505
|
} = optionsWithDefaults;
|
|
@@ -498,7 +509,7 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
498
509
|
}
|
|
499
510
|
logger.info(`rabbit: Consuming with lock from queue ${queue} with lockTimeout: ${lockTimeout}ms`);
|
|
500
511
|
}
|
|
501
|
-
const channel
|
|
512
|
+
const channel = await this.assertChannel();
|
|
502
513
|
return channel.addSetup(async (confirmChannel: ConfirmChannel) => {
|
|
503
514
|
await confirmChannel.assertQueue(queue, options ? { messageTtl: options.messageTtl } : undefined);
|
|
504
515
|
await confirmChannel.prefetch(limit, true);
|
|
@@ -526,7 +537,7 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
526
537
|
]);
|
|
527
538
|
} catch (e) {
|
|
528
539
|
logger.error('rabbit: failed to setRabbitTrace', { userId, e });
|
|
529
|
-
|
|
540
|
+
return this.nack(confirmChannel, queue, optionsWithDefaults, { messageTtl: deadMessageTtl }, msg, releaseLock)(msg);
|
|
530
541
|
}
|
|
531
542
|
}
|
|
532
543
|
|
|
@@ -551,6 +562,7 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
551
562
|
if (messageAcked) {
|
|
552
563
|
return;
|
|
553
564
|
}
|
|
565
|
+
debug('rabbit localAck', { messageAcked, uniqueId, deliveryTag: msg.fields.deliveryTag });
|
|
554
566
|
messageAcked = true;
|
|
555
567
|
return this.ack(confirmChannel, msg, true, releaseLock)(msg);
|
|
556
568
|
};
|
|
@@ -559,6 +571,7 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
559
571
|
if (messageAcked) {
|
|
560
572
|
return;
|
|
561
573
|
}
|
|
574
|
+
debug('rabbit localNack', { messageAcked, uniqueId, deliveryTag: msg.fields.deliveryTag });
|
|
562
575
|
messageAcked = true;
|
|
563
576
|
return this.nack(confirmChannel, queue, optionsWithDefaults, { messageTtl: deadMessageTtl }, msg, releaseLock)(msg, nackOptions);
|
|
564
577
|
};
|
|
@@ -647,7 +660,25 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
647
660
|
|
|
648
661
|
async isConnected() : Promise<boolean> {
|
|
649
662
|
const connection = await this.getConnection();
|
|
650
|
-
|
|
663
|
+
const isConnected = connection.isConnected();
|
|
664
|
+
if (!isConnected) {
|
|
665
|
+
logger.error('rabbit: isConnected - false');
|
|
666
|
+
return false;
|
|
667
|
+
}
|
|
668
|
+
const channel = await this.assertChannel();
|
|
669
|
+
try {
|
|
670
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
671
|
+
// @ts-ignore
|
|
672
|
+
await Promise.all([
|
|
673
|
+
channel.waitForConnect(),
|
|
674
|
+
...this.consumers.map((c: AfConsumer) => channel.checkQueue(c.queue)),
|
|
675
|
+
]);
|
|
676
|
+
} catch (e) {
|
|
677
|
+
logger.error('rabbit: isConnected - false');
|
|
678
|
+
return false;
|
|
679
|
+
}
|
|
680
|
+
logger.info('rabbit: isConnected - true');
|
|
681
|
+
return true;
|
|
651
682
|
}
|
|
652
683
|
|
|
653
684
|
async gracefulShutdown(signal: string) : Promise<void> {
|