@autofleet/rabbit 4.1.0-beta.0 → 4.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +81 -76
- package/dist/index.js +581 -472
- 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 +12 -4
- package/dist/lib/consts.js +12 -3
- 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.d.ts +2 -1
- package/dist/lib/redis.js +3 -8
- package/dist/lib/redis.js.map +1 -0
- package/dist/lib/types.d.ts +18 -5
- package/dist/lib/types.js +1 -0
- package/dist/lib/types.js.map +1 -0
- package/dist/lib/utils.d.ts +12 -3
- package/dist/lib/utils.js +23 -13
- 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 +24 -19
- package/src/index.ts +702 -591
- package/src/lib/consts.ts +17 -4
- package/src/lib/redis.ts +2 -7
- package/src/lib/types.ts +22 -7
- package/src/lib/utils.ts +35 -12
- package/src/{mock.ts → mock/index.ts} +2 -2
- package/src/mock/vitest.ts +24 -0
- package/src/redis-lock.d.ts +5 -3
- package/tsconfig.build.json +5 -0
- package/tsconfig.json +2 -2
- package/vitest.config.ts +17 -0
- package/coverage/clover.xml +0 -669
- package/coverage/coverage-final.json +0 -9
- package/coverage/lcov-report/base.css +0 -224
- package/coverage/lcov-report/block-navigation.js +0 -87
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/index.html +0 -131
- package/coverage/lcov-report/prettify.css +0 -1
- package/coverage/lcov-report/prettify.js +0 -2
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +0 -196
- package/coverage/lcov-report/src/index.html +0 -131
- package/coverage/lcov-report/src/index.ts.html +0 -3826
- package/coverage/lcov-report/src/lib/celery.ts.html +0 -352
- package/coverage/lcov-report/src/lib/consts.ts.html +0 -142
- package/coverage/lcov-report/src/lib/index.html +0 -191
- package/coverage/lcov-report/src/lib/rabbitError.ts.html +0 -103
- package/coverage/lcov-report/src/lib/redis.ts.html +0 -133
- package/coverage/lcov-report/src/lib/types.ts.html +0 -268
- package/coverage/lcov-report/src/lib/utils.ts.html +0 -142
- package/coverage/lcov-report/src/logger.ts.html +0 -100
- package/coverage/lcov.info +0 -1076
package/dist/index.js
CHANGED
|
@@ -1,36 +1,47 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
|
|
2
|
+
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
3
|
+
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
4
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
5
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
6
|
+
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
7
|
+
};
|
|
8
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
9
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
10
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
11
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
12
|
+
};
|
|
3
13
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
14
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
15
|
};
|
|
16
|
+
var _RabbitMq_logger;
|
|
6
17
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
18
|
exports.sendCeleryTaskViaHttp = void 0;
|
|
8
|
-
const
|
|
9
|
-
const
|
|
19
|
+
const node_process_1 = require("node:process");
|
|
20
|
+
const promises_1 = require("node:timers/promises");
|
|
21
|
+
const node_events_1 = require("node:events");
|
|
10
22
|
const moment_1 = __importDefault(require("moment"));
|
|
11
23
|
const redis_lock_1 = __importDefault(require("redis-lock"));
|
|
12
24
|
const amqp_connection_manager_1 = require("amqp-connection-manager");
|
|
25
|
+
const exponential_backoff_1 = require("exponential-backoff");
|
|
13
26
|
const zehut_1 = require("@autofleet/zehut");
|
|
14
27
|
const node_crypto_1 = require("node:crypto");
|
|
15
|
-
const axios_1 = __importDefault(require("axios"));
|
|
16
28
|
const logger_1 = __importDefault(require("./logger"));
|
|
17
29
|
const rabbitError_1 = __importDefault(require("./lib/rabbitError"));
|
|
18
30
|
const redis_1 = __importDefault(require("./lib/redis"));
|
|
19
31
|
const utils_1 = require("./lib/utils");
|
|
20
32
|
const consts_1 = require("./lib/consts");
|
|
21
33
|
const types_1 = require("./lib/types");
|
|
22
|
-
// const debug = nodeDebug('af-rabbitmq')
|
|
23
|
-
const debug = logger_1.default.debug.bind(logger_1.default);
|
|
24
34
|
const PUBLISH_TIMEOUT = 1000 * 10;
|
|
35
|
+
// TODO: [QUORUM-PHASE-3] Delete these env vars
|
|
36
|
+
const { DISABLE_QUORUM_QUEUES_CONSUME, DISABLE_QUORUM_QUEUES_PUBLISH, } = node_process_1.env;
|
|
25
37
|
const HEARTBEAT = '60';
|
|
26
38
|
class RabbitMq {
|
|
27
39
|
static parseMsg(msg) {
|
|
28
|
-
let
|
|
29
|
-
content = content.toString();
|
|
40
|
+
let content = msg.content.toString();
|
|
30
41
|
try {
|
|
31
42
|
content = JSON.parse(content);
|
|
32
43
|
}
|
|
33
|
-
catch
|
|
44
|
+
catch { /* ignore error */ }
|
|
34
45
|
return {
|
|
35
46
|
...msg,
|
|
36
47
|
content,
|
|
@@ -42,10 +53,8 @@ class RabbitMq {
|
|
|
42
53
|
}
|
|
43
54
|
}
|
|
44
55
|
static getPublishOptions(customHeaders = {}) {
|
|
45
|
-
const
|
|
46
|
-
const
|
|
47
|
-
const traceId = trace?.context?.get(consts_1.TRACING_HEADER);
|
|
48
|
-
const outbreakTrace = zehut_1.outbreak.getCurrentContext();
|
|
56
|
+
const user = (0, zehut_1.getUser)();
|
|
57
|
+
const traceId = (0, zehut_1.getCurrentPayload)()?.context?.get(consts_1.TRACING_HEADER) || zehut_1.outbreak.getCurrentContext()?.context?.get(consts_1.TRACING_HEADER);
|
|
49
58
|
return {
|
|
50
59
|
timestamp: (0, moment_1.default)().unix(),
|
|
51
60
|
timeout: PUBLISH_TIMEOUT,
|
|
@@ -53,65 +62,118 @@ class RabbitMq {
|
|
|
53
62
|
creationTimestamp: (0, moment_1.default)().valueOf(),
|
|
54
63
|
...customHeaders,
|
|
55
64
|
[consts_1.USER_TRACING_HEADER]: user?.id,
|
|
56
|
-
[consts_1.TRACING_HEADER]: traceId
|
|
65
|
+
[consts_1.TRACING_HEADER]: traceId,
|
|
57
66
|
},
|
|
58
67
|
};
|
|
59
68
|
}
|
|
60
69
|
constructor(options = {}, redisConfig) {
|
|
70
|
+
this.options = options;
|
|
71
|
+
this.redisConfig = redisConfig;
|
|
61
72
|
this.DISCONNECT_MSG = 'rabbit: connection disconnect';
|
|
62
73
|
this.RECONNECT_MSG = 'rabbit: connection disconnect - reconnecting';
|
|
63
|
-
this.
|
|
64
|
-
this.
|
|
65
|
-
this.
|
|
74
|
+
this.publishChannel = null;
|
|
75
|
+
this.publishChannelSetupPromise = null;
|
|
76
|
+
this.publishConnection = {
|
|
77
|
+
amqpConnection: null,
|
|
78
|
+
creatingConnection: false,
|
|
79
|
+
connectionCreatedEventName: 'publishConnectionCreated',
|
|
80
|
+
connectionFailedEventName: 'publishConnectionFailed',
|
|
81
|
+
blockReconnect: false,
|
|
82
|
+
};
|
|
83
|
+
this.consumeConnection = {
|
|
84
|
+
amqpConnection: null,
|
|
85
|
+
creatingConnection: false,
|
|
86
|
+
connectionCreatedEventName: 'consumeConnectionCreated',
|
|
87
|
+
connectionFailedEventName: 'consumeConnectionFailed',
|
|
88
|
+
blockReconnect: false,
|
|
89
|
+
};
|
|
90
|
+
this.em = new node_events_1.EventEmitter();
|
|
91
|
+
this.exchanges = {};
|
|
92
|
+
this.queues = {};
|
|
93
|
+
this.queueSetupPromises = {};
|
|
94
|
+
this.assertExchangePromises = {};
|
|
95
|
+
/** Array of consumers tags used for canceling consumption */
|
|
96
|
+
this.consumersTags = new Map();
|
|
97
|
+
this.consumersToRegister = [];
|
|
98
|
+
this.doesVHostExist = false;
|
|
99
|
+
this.vhost = 'quorum-vhost';
|
|
100
|
+
this.gracefulShutdownStarted = false;
|
|
101
|
+
// TODO:[QUORUM-PHASE-3] Delete the old properties that we use for the old consumers and publishers
|
|
102
|
+
this.oldPublishChannel = null;
|
|
103
|
+
this.oldPublishChannelSetupPromise = null;
|
|
104
|
+
this.oldPublishConnection = {
|
|
105
|
+
amqpConnection: null,
|
|
106
|
+
creatingConnection: false,
|
|
107
|
+
connectionCreatedEventName: 'oldPublishConnectionCreated',
|
|
108
|
+
connectionFailedEventName: 'oldPublishConnectionFailed',
|
|
109
|
+
blockReconnect: false,
|
|
110
|
+
};
|
|
111
|
+
this.oldConsumeConnection = {
|
|
112
|
+
amqpConnection: null,
|
|
113
|
+
creatingConnection: false,
|
|
114
|
+
connectionCreatedEventName: 'oldConsumeConnectionCreated',
|
|
115
|
+
connectionFailedEventName: 'oldConsumeConnectionFailed',
|
|
116
|
+
blockReconnect: false,
|
|
117
|
+
};
|
|
118
|
+
this.oldEm = new node_events_1.EventEmitter();
|
|
119
|
+
this.oldExchanges = {};
|
|
120
|
+
this.oldQueues = {};
|
|
121
|
+
this.oldQueueSetupPromises = {};
|
|
122
|
+
this.oldAssertExchangePromises = {};
|
|
123
|
+
this.oldConsumersTags = new Map();
|
|
124
|
+
this.oldConsumersToRegister = [];
|
|
125
|
+
_RabbitMq_logger.set(this, void 0);
|
|
66
126
|
this.assertVHost = async () => {
|
|
67
|
-
if (this.
|
|
127
|
+
if (this.doesVHostExist) {
|
|
68
128
|
return;
|
|
69
129
|
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
}
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
username: process.env.RABBITMQ_USERNAME || 'guest',
|
|
77
|
-
password: process.env.RABBITMQ_PASSWORD || 'guest',
|
|
78
|
-
},
|
|
130
|
+
const username = process.env.RABBITMQ_USERNAME || 'guest';
|
|
131
|
+
const password = process.env.RABBITMQ_PASSWORD || 'guest';
|
|
132
|
+
const credentials = Buffer.from(`${username}:${password}`).toString('base64');
|
|
133
|
+
const headers = {
|
|
134
|
+
Authorization: `Basic ${credentials}`,
|
|
135
|
+
'Content-Type': 'application/json',
|
|
79
136
|
};
|
|
80
|
-
const rabbitHost = `http://${(this.options
|
|
137
|
+
const rabbitHost = `http://${(this.options?.rabbitHost || process.env.RABBITMQ_SERVICE_HOST || 'localhost').split(':')[0]}:15672`;
|
|
138
|
+
const url = `${rabbitHost}/api/vhosts/${encodeURIComponent(this.vhost)}`;
|
|
81
139
|
try {
|
|
82
|
-
const response = await
|
|
83
|
-
|
|
84
|
-
|
|
140
|
+
const response = await fetch(url, {
|
|
141
|
+
method: 'GET',
|
|
142
|
+
headers,
|
|
143
|
+
});
|
|
144
|
+
if (response.status === 200) {
|
|
145
|
+
this.doesVHostExist = true;
|
|
146
|
+
__classPrivateFieldGet(this, _RabbitMq_logger, "f").info('Vhost exists', { vhost: this.vhost });
|
|
147
|
+
return;
|
|
85
148
|
}
|
|
86
|
-
|
|
87
|
-
|
|
149
|
+
if (response.status !== 404) {
|
|
150
|
+
__classPrivateFieldGet(this, _RabbitMq_logger, "f").error('Failed to check vhost', { response });
|
|
151
|
+
throw new rabbitError_1.default('Failed to check vhost');
|
|
152
|
+
}
|
|
153
|
+
const createResponse = await fetch(url, {
|
|
154
|
+
method: 'PUT',
|
|
155
|
+
headers,
|
|
156
|
+
body: JSON.stringify({ default_queue_type: 'quorum' }),
|
|
157
|
+
});
|
|
158
|
+
if (!createResponse.ok) {
|
|
159
|
+
__classPrivateFieldGet(this, _RabbitMq_logger, "f").error('Failed to create vhost', { response: createResponse });
|
|
160
|
+
throw new rabbitError_1.default('Failed to create vhost');
|
|
161
|
+
}
|
|
162
|
+
this.doesVHostExist = true;
|
|
163
|
+
__classPrivateFieldGet(this, _RabbitMq_logger, "f").info('Vhost created', { vhost: this.vhost });
|
|
88
164
|
}
|
|
89
165
|
catch (error) {
|
|
90
|
-
|
|
91
|
-
if (error instanceof axios_1.default.AxiosError && error.response?.status === 404) {
|
|
92
|
-
try {
|
|
93
|
-
await axios_1.default.put(`${rabbitHost}/api/vhosts/${encodeURIComponent(vhost)}`, {
|
|
94
|
-
default_queue_type: 'quorum',
|
|
95
|
-
}, auth);
|
|
96
|
-
this.isVHostExist = true;
|
|
97
|
-
logger_1.default.info('Vhost created', { vhost: this.options?.vhost });
|
|
98
|
-
return;
|
|
99
|
-
}
|
|
100
|
-
catch (createError) {
|
|
101
|
-
logger_1.default.error('Failed to create vhost', { error: createError });
|
|
102
|
-
throw createError;
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
logger_1.default.error('Failed to check vhost', { error });
|
|
166
|
+
__classPrivateFieldGet(this, _RabbitMq_logger, "f").error('Failed to check or create vhost', { error });
|
|
106
167
|
throw error;
|
|
107
168
|
}
|
|
108
169
|
};
|
|
109
170
|
this.shouldConsumeMessageByTimestamp = async (msg) => {
|
|
110
171
|
if (msg) {
|
|
111
|
-
const {
|
|
172
|
+
const { headers } = msg.properties;
|
|
112
173
|
const timestamp = headers?.creationTimestamp;
|
|
113
174
|
if (timestamp && headers?.redisTimestampValidationKey && this.redisClient) {
|
|
114
|
-
const
|
|
175
|
+
const key = this.getRedisKey(headers.redisTimestampValidationKey);
|
|
176
|
+
const lastMessageTimestamp = await this.redisClient.get(key);
|
|
115
177
|
return !lastMessageTimestamp || (parseInt(lastMessageTimestamp, 10) <= parseInt(timestamp, 10));
|
|
116
178
|
}
|
|
117
179
|
return true;
|
|
@@ -120,68 +182,55 @@ class RabbitMq {
|
|
|
120
182
|
};
|
|
121
183
|
this.ack = (channel, msg, shouldUpdateRedisTimestamp = false, releaseLock = null) => async (userMsg) => {
|
|
122
184
|
if (msg) {
|
|
123
|
-
debug('rabbit acking message', { deliveryTag: msg.fields.deliveryTag });
|
|
185
|
+
__classPrivateFieldGet(this, _RabbitMq_logger, "f").debug('rabbit acking message', { deliveryTag: msg.fields.deliveryTag });
|
|
124
186
|
await channel.ack(msg);
|
|
125
|
-
const {
|
|
187
|
+
const { headers } = msg.properties;
|
|
126
188
|
const timestamp = headers?.creationTimestamp;
|
|
127
189
|
if (shouldUpdateRedisTimestamp && timestamp && headers?.redisTimestampValidationKey && this.redisClient) {
|
|
128
190
|
const parsedTimestamp = parseInt(timestamp, 10);
|
|
129
|
-
|
|
191
|
+
const key = this.getRedisKey(headers.redisTimestampValidationKey);
|
|
192
|
+
await this.redisClient.set(key, parsedTimestamp, { EX: 3600 });
|
|
130
193
|
await this.unlockRedisIfNeeded(releaseLock);
|
|
131
194
|
}
|
|
132
195
|
}
|
|
133
196
|
};
|
|
134
197
|
this.nack = (channel, queue, options, deadQueueOptions, msg, releaseLock) => async (userMsg, { skipRetry = false, } = {}) => {
|
|
135
198
|
await this.unlockRedisIfNeeded(releaseLock);
|
|
136
|
-
if (channel
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|| parseInt(msg.properties.headers[consts_1.RETRY_HEADER], 10) < options.retries)) {
|
|
140
|
-
await this.sendToQueue(queue, RabbitMq.parseMsg(msg).content, options, {
|
|
141
|
-
...msg.properties.headers,
|
|
142
|
-
[consts_1.RETRY_HEADER]: msg.properties.headers[consts_1.RETRY_HEADER]
|
|
143
|
-
? msg.properties.headers[consts_1.RETRY_HEADER] + 1
|
|
144
|
-
: 1,
|
|
145
|
-
});
|
|
146
|
-
}
|
|
147
|
-
else {
|
|
148
|
-
const deadQueue = `${queue}-dead`;
|
|
149
|
-
await this.sendToQueue(deadQueue, RabbitMq.parseMsg(msg).content, deadQueueOptions, {
|
|
150
|
-
...msg.properties.headers,
|
|
151
|
-
[consts_1.RETRY_HEADER]: msg.properties.headers[consts_1.RETRY_HEADER]
|
|
152
|
-
? msg.properties.headers[consts_1.RETRY_HEADER] + 1
|
|
153
|
-
: 1,
|
|
154
|
-
});
|
|
155
|
-
}
|
|
156
|
-
debug('rabbit nacking message', { deliveryTag: msg.fields.deliveryTag });
|
|
157
|
-
await channel.ack(msg);
|
|
199
|
+
if (!channel || !msg) {
|
|
200
|
+
__classPrivateFieldGet(this, _RabbitMq_logger, "f").error('no channel or msg', { msg });
|
|
201
|
+
return;
|
|
158
202
|
}
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
203
|
+
const currentRetryHeader = Number.parseInt(msg.properties.headers?.[consts_1.RETRY_HEADER] || '0', 10) || 0;
|
|
204
|
+
const sendToDeadQueue = skipRetry || currentRetryHeader >= options.retries;
|
|
205
|
+
await this.sendToQueue(`${queue}${sendToDeadQueue ? '-dead' : ''}`, RabbitMq.parseMsg(msg).content, sendToDeadQueue ? deadQueueOptions : options, {
|
|
206
|
+
...msg.properties.headers,
|
|
207
|
+
[consts_1.RETRY_HEADER]: currentRetryHeader + 1,
|
|
208
|
+
});
|
|
209
|
+
__classPrivateFieldGet(this, _RabbitMq_logger, "f").debug('rabbit nacking message', { deliveryTag: msg.fields.deliveryTag });
|
|
210
|
+
await channel.ack(msg);
|
|
211
|
+
};
|
|
212
|
+
this.maskURL = (url) => {
|
|
213
|
+
try {
|
|
214
|
+
const urlObj = new URL(url);
|
|
215
|
+
urlObj.username = '***';
|
|
216
|
+
urlObj.password = '***';
|
|
217
|
+
return urlObj.toString();
|
|
218
|
+
}
|
|
219
|
+
catch {
|
|
220
|
+
return url;
|
|
163
221
|
}
|
|
164
222
|
};
|
|
165
|
-
this
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
this.consumers = [];
|
|
175
|
-
this.options = options;
|
|
176
|
-
if (!options?.vhost) {
|
|
177
|
-
this.options.vhost = process.env.VHOST_NAME || 'quorum-vhost';
|
|
178
|
-
}
|
|
179
|
-
this.redisClient = redisConfig && (0, redis_1.default)(redisConfig);
|
|
180
|
-
if (this.redisClient) {
|
|
181
|
-
this.redisLock = (0, util_1.promisify)((0, redis_lock_1.default)(this.redisClient));
|
|
223
|
+
__classPrivateFieldSet(this, _RabbitMq_logger, options?.logger || logger_1.default, "f");
|
|
224
|
+
if (redisConfig) {
|
|
225
|
+
this.redisClient = (0, redis_1.default)(redisConfig).on('error', (err) => {
|
|
226
|
+
__classPrivateFieldGet(this, _RabbitMq_logger, "f").error('rabbit: Redis error', { err, redisConfig });
|
|
227
|
+
});
|
|
228
|
+
this.redisClient.connect().catch((err) => {
|
|
229
|
+
__classPrivateFieldGet(this, _RabbitMq_logger, "f").error('rabbit: Failed to connect to Redis', { err, redisConfig });
|
|
230
|
+
});
|
|
231
|
+
this.redisLock = (0, redis_lock_1.default)(this.redisClient);
|
|
182
232
|
}
|
|
183
|
-
this.
|
|
184
|
-
logger_1.default.info(`rabbit: [gracefully-shutdown] adding gracefully shutdown for process.pid ${process.pid}`);
|
|
233
|
+
__classPrivateFieldGet(this, _RabbitMq_logger, "f").info(`rabbit: [gracefully-shutdown] adding gracefully shutdown for process.pid ${process.pid}`);
|
|
185
234
|
if (!this.options?.dontGracefulShutdown) {
|
|
186
235
|
process.on('SIGTERM', async () => {
|
|
187
236
|
await this.gracefulShutdown('SIGTERM');
|
|
@@ -190,144 +239,139 @@ class RabbitMq {
|
|
|
190
239
|
await this.gracefulShutdown('SIGINT');
|
|
191
240
|
});
|
|
192
241
|
}
|
|
193
|
-
// TODO: DELETE OLD PROPERTIES
|
|
194
|
-
this.oldEm = new events_1.EventEmitter();
|
|
195
|
-
this.oldChannel = null;
|
|
196
|
-
this.oldPublishChannelSetupPromise = null;
|
|
197
|
-
this.oldConnection = null;
|
|
198
|
-
this.oldCreatingConnection = false;
|
|
199
|
-
this.oldExchanges = {};
|
|
200
|
-
this.oldQueues = {};
|
|
201
|
-
this.oldQueueSetupPromises = {};
|
|
202
|
-
this.oldAssertExchangePromises = {};
|
|
203
|
-
this.oldConsumers = [];
|
|
204
|
-
this.oldConsumersTags = [];
|
|
205
242
|
}
|
|
206
|
-
|
|
207
|
-
return
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
}
|
|
221
|
-
debug('rabbit: connection - reconnecting');
|
|
243
|
+
getRedisKey(key) {
|
|
244
|
+
return `${this.redisConfig?.prefix || ''}${key}`;
|
|
245
|
+
}
|
|
246
|
+
async getConnection(connection) {
|
|
247
|
+
const { amqpConnection: connectionLocal, creatingConnection, connectionCreatedEventName, connectionFailedEventName, blockReconnect, } = connection;
|
|
248
|
+
if (blockReconnect) {
|
|
249
|
+
__classPrivateFieldGet(this, _RabbitMq_logger, "f").debug('rabbit: block reconnect');
|
|
250
|
+
// @ts-expect-error we are returning undefined while the function clearly expects a connection.
|
|
251
|
+
return undefined;
|
|
252
|
+
}
|
|
253
|
+
if (connectionLocal !== null) {
|
|
254
|
+
if (this.options?.disableReconnect || connectionLocal?.isConnected()) {
|
|
255
|
+
__classPrivateFieldGet(this, _RabbitMq_logger, "f").debug('rabbit: connection - is connected');
|
|
256
|
+
return connectionLocal;
|
|
222
257
|
}
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
258
|
+
__classPrivateFieldGet(this, _RabbitMq_logger, "f").debug('rabbit: connection - reconnecting');
|
|
259
|
+
}
|
|
260
|
+
if (creatingConnection) {
|
|
261
|
+
__classPrivateFieldGet(this, _RabbitMq_logger, "f").debug('rabbit: creating connection emi');
|
|
262
|
+
const [event, value] = await Promise.race([connectionCreatedEventName, connectionFailedEventName].map((e) => (0, node_events_1.once)(this.em, e).then(([result]) => [e, result])));
|
|
263
|
+
if (event === connectionCreatedEventName) {
|
|
264
|
+
return value;
|
|
228
265
|
}
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
};
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
reject(err);
|
|
251
|
-
this.em.emit(consts_1.CONNECTION_FAILED_CONST, err);
|
|
252
|
-
}
|
|
253
|
-
});
|
|
254
|
-
this.connection.on('connectFailed', (err) => {
|
|
255
|
-
this.consumersTags = [];
|
|
256
|
-
logger_1.default.error('rabbit: connection connectFailed', { err, advice: 'Check if the vhost exist', vhost: this.options?.vhost });
|
|
257
|
-
if (!isResolved) {
|
|
258
|
-
isResolved = true;
|
|
259
|
-
reject(err);
|
|
260
|
-
this.em.emit(consts_1.CONNECTION_FAILED_CONST, err);
|
|
261
|
-
}
|
|
262
|
-
});
|
|
263
|
-
this.connection.on('disconnect', ({ err }) => {
|
|
264
|
-
// this.channel = null;
|
|
265
|
-
this.consumersTags = [];
|
|
266
|
-
debug('rabbit: connection closed');
|
|
267
|
-
if (this.options?.disableReconnect) {
|
|
268
|
-
logger_1.default.error(`${this.DISCONNECT_MSG}${err && ` - ${err}`}`);
|
|
269
|
-
this.blockReconnect = true;
|
|
270
|
-
}
|
|
271
|
-
else {
|
|
272
|
-
logger_1.default.error(`${this.RECONNECT_MSG}${err && ` - ${err}`}`);
|
|
273
|
-
}
|
|
274
|
-
});
|
|
275
|
-
this.connection.once('connect', async () => {
|
|
276
|
-
debug('rabbit: connection established');
|
|
277
|
-
this.creatingConnection = false;
|
|
278
|
-
this.em.emit(consts_1.CONNECTION_CREATED_CONST, connection);
|
|
266
|
+
throw value;
|
|
267
|
+
}
|
|
268
|
+
connection.creatingConnection = true;
|
|
269
|
+
let isResolved = false;
|
|
270
|
+
// It is import to use it as a function and not as a variable
|
|
271
|
+
// because of k8s changes the env variables
|
|
272
|
+
// and we want to use the new values
|
|
273
|
+
const findServers = () => {
|
|
274
|
+
const userName = process.env.RABBITMQ_USERNAME || 'guest';
|
|
275
|
+
const password = process.env.RABBITMQ_PASSWORD || 'guest';
|
|
276
|
+
const host = this.options?.rabbitHost || process.env.RABBITMQ_SERVICE_HOST || 'localhost';
|
|
277
|
+
__classPrivateFieldGet(this, _RabbitMq_logger, "f").debug('rabbit: creating connection', { host, userName, HEARTBEAT });
|
|
278
|
+
return [`amqp://${userName}:${password}@${host}/${this.vhost}?heartbeat=${HEARTBEAT}`];
|
|
279
|
+
};
|
|
280
|
+
const defaultUrls = findServers();
|
|
281
|
+
const newConnection = (0, amqp_connection_manager_1.connect)(defaultUrls, { findServers });
|
|
282
|
+
connection.amqpConnection = newConnection;
|
|
283
|
+
const { promise, reject, resolve } = (0, utils_1.createDeferredPromise)();
|
|
284
|
+
newConnection.on('error', (err) => {
|
|
285
|
+
__classPrivateFieldGet(this, _RabbitMq_logger, "f").error('rabbit: connection error', { err });
|
|
286
|
+
if (!isResolved) {
|
|
279
287
|
isResolved = true;
|
|
280
|
-
|
|
281
|
-
|
|
288
|
+
reject(err);
|
|
289
|
+
this.em.emit(connectionFailedEventName, err);
|
|
290
|
+
}
|
|
291
|
+
});
|
|
292
|
+
newConnection.on('connectFailed', (err) => {
|
|
293
|
+
this.consumersTags.clear();
|
|
294
|
+
if (typeof err.url === 'string') {
|
|
295
|
+
err.url = this.maskURL(err.url);
|
|
296
|
+
}
|
|
297
|
+
__classPrivateFieldGet(this, _RabbitMq_logger, "f").error('rabbit: connection connectFailed', { err, advice: 'Check if the vhost exist', vhost: this.vhost });
|
|
298
|
+
if (!isResolved) {
|
|
299
|
+
isResolved = true;
|
|
300
|
+
reject(err);
|
|
301
|
+
this.em.emit(connectionFailedEventName, err);
|
|
302
|
+
}
|
|
303
|
+
});
|
|
304
|
+
newConnection.on('disconnect', ({ err }) => {
|
|
305
|
+
this.consumersTags.clear();
|
|
306
|
+
__classPrivateFieldGet(this, _RabbitMq_logger, "f").debug('rabbit: connection closed');
|
|
307
|
+
if (this.options?.disableReconnect) {
|
|
308
|
+
__classPrivateFieldGet(this, _RabbitMq_logger, "f").error(`${this.DISCONNECT_MSG}${err && ` - ${err}`}`);
|
|
309
|
+
connection.blockReconnect = true;
|
|
310
|
+
}
|
|
311
|
+
else {
|
|
312
|
+
__classPrivateFieldGet(this, _RabbitMq_logger, "f").error(`${this.RECONNECT_MSG}${err && ` - ${err}`}`);
|
|
313
|
+
}
|
|
314
|
+
});
|
|
315
|
+
newConnection.once('connect', async () => {
|
|
316
|
+
__classPrivateFieldGet(this, _RabbitMq_logger, "f").debug('rabbit: connection established');
|
|
317
|
+
connection.creatingConnection = false;
|
|
318
|
+
this.em.emit(connectionCreatedEventName, newConnection);
|
|
319
|
+
isResolved = true;
|
|
320
|
+
resolve(newConnection);
|
|
282
321
|
});
|
|
322
|
+
return promise;
|
|
283
323
|
}
|
|
284
|
-
async getNewChannel({ name = (0, utils_1.rand)().toString(), onClose = null, options = {}
|
|
285
|
-
let
|
|
324
|
+
async getNewChannel({ name = (0, utils_1.rand)().toString(), onClose = null, options = {}, connection, }) {
|
|
325
|
+
let localConnection;
|
|
286
326
|
try {
|
|
287
|
-
|
|
327
|
+
localConnection = await this.getConnection(connection);
|
|
288
328
|
}
|
|
289
329
|
catch (e) {
|
|
290
|
-
|
|
330
|
+
__classPrivateFieldGet(this, _RabbitMq_logger, "f").error(`rabbit: error on get connection for new channel ${name} `, { e });
|
|
291
331
|
throw e;
|
|
292
332
|
}
|
|
293
|
-
const channel =
|
|
294
|
-
(0,
|
|
295
|
-
|
|
333
|
+
const channel = localConnection?.createChannel({ ...options });
|
|
334
|
+
(0, node_events_1.once)(channel, 'close').then((args) => {
|
|
335
|
+
__classPrivateFieldGet(this, _RabbitMq_logger, "f").error(`rabbit: channel ${name} closed`);
|
|
296
336
|
onClose?.(args);
|
|
297
337
|
});
|
|
298
338
|
try {
|
|
299
|
-
await (0,
|
|
300
|
-
debug(`rabbit: channel ${name} CONNECTED`);
|
|
339
|
+
await (0, node_events_1.once)(channel, 'connect');
|
|
340
|
+
__classPrivateFieldGet(this, _RabbitMq_logger, "f").debug(`rabbit: channel ${name} CONNECTED`);
|
|
301
341
|
return channel;
|
|
302
342
|
}
|
|
303
343
|
catch (err) {
|
|
304
|
-
|
|
344
|
+
__classPrivateFieldGet(this, _RabbitMq_logger, "f").error(`rabbit: channel error ${name} error`, { err });
|
|
305
345
|
throw err;
|
|
306
346
|
}
|
|
307
347
|
}
|
|
308
|
-
async assertChannel({ force = false
|
|
309
|
-
if (
|
|
310
|
-
this.publishChannelSetupPromise
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
}
|
|
322
|
-
catch (e) {
|
|
323
|
-
reject(e);
|
|
324
|
-
}
|
|
348
|
+
async assertChannel({ force = false, connection }) {
|
|
349
|
+
if (this.publishChannelSetupPromise) {
|
|
350
|
+
return this.publishChannelSetupPromise;
|
|
351
|
+
}
|
|
352
|
+
const { promise, resolve, reject } = (0, utils_1.createDeferredPromise)();
|
|
353
|
+
this.publishChannelSetupPromise = promise;
|
|
354
|
+
if (this.publishChannel && !force) {
|
|
355
|
+
resolve(this.publishChannel);
|
|
356
|
+
return promise;
|
|
357
|
+
}
|
|
358
|
+
try {
|
|
359
|
+
const channel = await this.getNewChannel({ connection });
|
|
360
|
+
channel.on('error', (err) => {
|
|
361
|
+
__classPrivateFieldGet(this, _RabbitMq_logger, "f").error('rabbit: channel error', { err });
|
|
325
362
|
});
|
|
363
|
+
if (this.publishConnection === connection) {
|
|
364
|
+
this.publishChannel = channel;
|
|
365
|
+
}
|
|
366
|
+
resolve(channel);
|
|
367
|
+
}
|
|
368
|
+
catch (e) {
|
|
369
|
+
reject(e);
|
|
326
370
|
}
|
|
327
|
-
return
|
|
371
|
+
return promise;
|
|
328
372
|
}
|
|
329
|
-
async assertExchange(exchangeName,
|
|
330
|
-
const channel = await this.assertChannel();
|
|
373
|
+
async assertExchange(exchangeName, connection) {
|
|
374
|
+
const channel = await this.assertChannel({ connection });
|
|
331
375
|
if (this.exchanges[exchangeName]) {
|
|
332
376
|
delete this.assertExchangePromises[exchangeName];
|
|
333
377
|
return this.exchanges[exchangeName];
|
|
@@ -341,28 +385,29 @@ class RabbitMq {
|
|
|
341
385
|
}
|
|
342
386
|
async getQueueLength(queue) {
|
|
343
387
|
RabbitMq.validateName('queue', queue);
|
|
344
|
-
const { channel } = this;
|
|
388
|
+
const { publishChannel: channel } = this;
|
|
345
389
|
if (!channel) {
|
|
346
|
-
throw new
|
|
390
|
+
throw new rabbitError_1.default('channel is not defined');
|
|
347
391
|
}
|
|
348
|
-
debug('rabbit: getting queue length', { queue, connected: this.
|
|
392
|
+
__classPrivateFieldGet(this, _RabbitMq_logger, "f").debug('rabbit: getting queue length', { queue, connected: this.publishConnection.amqpConnection?.isConnected() });
|
|
349
393
|
return channel?.checkQueue(queue);
|
|
350
394
|
}
|
|
351
|
-
async deleteQueue(queue) {
|
|
395
|
+
async deleteQueue(queue, connection) {
|
|
352
396
|
RabbitMq.validateName('queue', queue);
|
|
353
|
-
const channel = await this.assertChannel();
|
|
354
|
-
|
|
397
|
+
const channel = await this.assertChannel({ connection });
|
|
398
|
+
__classPrivateFieldGet(this, _RabbitMq_logger, "f").info('rabbit: deleting queue', { queue });
|
|
355
399
|
const deleteQueueRes = await channel.deleteQueue(queue);
|
|
356
|
-
debug('queue deleted', deleteQueueRes);
|
|
400
|
+
__classPrivateFieldGet(this, _RabbitMq_logger, "f").debug('queue deleted', deleteQueueRes);
|
|
357
401
|
return deleteQueueRes;
|
|
358
402
|
}
|
|
359
403
|
async bindQueue(queue, exchange) {
|
|
360
|
-
const channel = await this.assertChannel();
|
|
404
|
+
const channel = await this.assertChannel({ connection: this.publishConnection });
|
|
361
405
|
await channel.addSetup((setupChannel) => setupChannel.bindQueue(queue, exchange, ''));
|
|
362
406
|
return channel.bindQueue(queue, exchange, '');
|
|
363
407
|
}
|
|
364
408
|
async setupQueue(queueName, options) {
|
|
365
409
|
let queue;
|
|
410
|
+
const connection = this.publishConnection;
|
|
366
411
|
const localeOptions = {
|
|
367
412
|
...options,
|
|
368
413
|
durable: true,
|
|
@@ -373,23 +418,23 @@ class RabbitMq {
|
|
|
373
418
|
},
|
|
374
419
|
};
|
|
375
420
|
try {
|
|
376
|
-
const channel = await this.assertChannel();
|
|
377
|
-
debug('assertQueue->channel.addSetup', { queueName });
|
|
421
|
+
const channel = await this.assertChannel({ connection });
|
|
422
|
+
__classPrivateFieldGet(this, _RabbitMq_logger, "f").debug('assertQueue->channel.addSetup', { queueName });
|
|
378
423
|
await channel.addSetup(async (setupChannel) => {
|
|
379
424
|
await setupChannel.assertQueue(queueName, localeOptions);
|
|
380
425
|
});
|
|
381
|
-
debug('assertQueue->channel.assertQueue', { queueName });
|
|
426
|
+
__classPrivateFieldGet(this, _RabbitMq_logger, "f").debug('assertQueue->channel.assertQueue', { queueName });
|
|
382
427
|
queue = await channel.assertQueue(queueName, localeOptions);
|
|
383
428
|
}
|
|
384
429
|
catch (e) {
|
|
385
|
-
|
|
430
|
+
__classPrivateFieldGet(this, _RabbitMq_logger, "f").error('rabbit: assertQueue error', { queueName, options, error: e });
|
|
386
431
|
if (!this.options?.dontRetryAssert) {
|
|
387
|
-
debug('retrying assertQueue', { queueName });
|
|
388
|
-
const channel = await this.assertChannel({ force: true });
|
|
389
|
-
await this.deleteQueue(queueName);
|
|
390
|
-
debug('retrying assertQueue->channel.addSetup', { queueName });
|
|
432
|
+
__classPrivateFieldGet(this, _RabbitMq_logger, "f").debug('retrying assertQueue', { queueName });
|
|
433
|
+
const channel = await this.assertChannel({ force: true, connection });
|
|
434
|
+
await this.deleteQueue(queueName, connection);
|
|
435
|
+
__classPrivateFieldGet(this, _RabbitMq_logger, "f").debug('retrying assertQueue->channel.addSetup', { queueName });
|
|
391
436
|
await channel.addSetup((setupChannel) => setupChannel.assertQueue(queueName, localeOptions));
|
|
392
|
-
debug('retrying assertQueue->channel.assertQueue', { queueName });
|
|
437
|
+
__classPrivateFieldGet(this, _RabbitMq_logger, "f").debug('retrying assertQueue->channel.assertQueue', { queueName });
|
|
393
438
|
queue = await channel.assertQueue(queueName, localeOptions);
|
|
394
439
|
}
|
|
395
440
|
else {
|
|
@@ -399,7 +444,7 @@ class RabbitMq {
|
|
|
399
444
|
this.queues[queueName] = queue;
|
|
400
445
|
return queue;
|
|
401
446
|
}
|
|
402
|
-
// TODO: Can be deleted after deleting the old consumers
|
|
447
|
+
// TODO: [QUORUM-PHASE-3] Can be deleted after deleting the old consumers and publishers because all the queues are created us quorum queues
|
|
403
448
|
static shouldUseQuorum(queueName) {
|
|
404
449
|
const envQuorumQueuesWhitelist = process.env.QUORUM_QUEUES_WHITELIST;
|
|
405
450
|
if (envQuorumQueuesWhitelist === '*') {
|
|
@@ -412,22 +457,20 @@ class RabbitMq {
|
|
|
412
457
|
return false;
|
|
413
458
|
}
|
|
414
459
|
async assertQueue(queueName, options) {
|
|
460
|
+
var _a;
|
|
415
461
|
RabbitMq.validateName('queue', queueName);
|
|
416
462
|
if (this.queues[queueName]) {
|
|
417
463
|
delete this.queueSetupPromises[queueName];
|
|
418
464
|
return this.queues[queueName];
|
|
419
465
|
}
|
|
420
|
-
|
|
421
|
-
return this.queueSetupPromises[queueName];
|
|
422
|
-
}
|
|
423
|
-
this.queueSetupPromises[queueName] = this.setupQueue(queueName, options);
|
|
466
|
+
(_a = this.queueSetupPromises)[queueName] || (_a[queueName] = this.setupQueue(queueName, options));
|
|
424
467
|
return this.queueSetupPromises[queueName];
|
|
425
468
|
}
|
|
426
469
|
saveConsumer(queue, callback, options) {
|
|
427
|
-
const isConsumerExist = this.
|
|
470
|
+
const isConsumerExist = this.consumersToRegister.some((consumer) => consumer.queue === queue);
|
|
428
471
|
if (!isConsumerExist) {
|
|
429
|
-
|
|
430
|
-
this.
|
|
472
|
+
__classPrivateFieldGet(this, _RabbitMq_logger, "f").info(`rabbit: consumer: ${queue} saved in consumer array`);
|
|
473
|
+
this.consumersToRegister.push({
|
|
431
474
|
queue,
|
|
432
475
|
callback,
|
|
433
476
|
options,
|
|
@@ -436,18 +479,21 @@ class RabbitMq {
|
|
|
436
479
|
}
|
|
437
480
|
// Used by the microservices to consume messages from the queue
|
|
438
481
|
async consume(queue, callback, options) {
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
482
|
+
this.saveConsumerOld(queue, callback, options);
|
|
483
|
+
// TODO: [QUORUM-PHASE-3] Use only the implementation of consumeNew and delete consumeNew and consumeOld
|
|
484
|
+
if (options?.isQuorumQueue !== false && DISABLE_QUORUM_QUEUES_CONSUME !== 'true') {
|
|
485
|
+
this.saveConsumer(queue, callback, options);
|
|
486
|
+
const backoffConfig = (0, utils_1.getAssertVhostExponentialBackoffConfig)();
|
|
487
|
+
await (0, exponential_backoff_1.backOff)(() => this.assertVHost(), backoffConfig);
|
|
442
488
|
await this.consumeNew(queue, callback, options);
|
|
443
489
|
}
|
|
444
490
|
await this.consumeOld(queue, callback, options);
|
|
445
491
|
}
|
|
446
|
-
// TODO:
|
|
492
|
+
// TODO: [QUORUM-PHASE-3] Delete consumeNew we do not use it anymore
|
|
447
493
|
async consumeNew(queue, callback, options) {
|
|
448
494
|
await this.consumeFromRabbit(queue, callback, options);
|
|
449
495
|
}
|
|
450
|
-
// TODO:
|
|
496
|
+
// TODO: [QUORUM-PHASE-3] Delete consumeOld we do not use it anymore
|
|
451
497
|
async consumeOld(queue, callback, options) {
|
|
452
498
|
await this.consumeFromRabbitOld(queue, callback, options);
|
|
453
499
|
}
|
|
@@ -468,26 +514,23 @@ class RabbitMq {
|
|
|
468
514
|
async consumeFromRabbit(queue, callback, options) {
|
|
469
515
|
const optionsWithDefaults = { ...consts_1.DEFAULT_OPTIONS, ...options };
|
|
470
516
|
RabbitMq.validateName('queue', queue);
|
|
471
|
-
this.saveConsumer(queue, callback, options);
|
|
472
517
|
const uniqueId = (0, node_crypto_1.randomUUID)();
|
|
473
518
|
const { limit, deadMessageTtl, useConsumeWithLock, lockTimeout, auditContext, enableRabbitTrace, } = optionsWithDefaults;
|
|
474
519
|
if (useConsumeWithLock) {
|
|
475
520
|
if (!this.redisLock) {
|
|
476
|
-
throw new
|
|
521
|
+
throw new rabbitError_1.default('Usage of consumeWithLock requires RedisInstance');
|
|
477
522
|
}
|
|
478
|
-
|
|
523
|
+
__classPrivateFieldGet(this, _RabbitMq_logger, "f").info(`rabbit: Consuming with lock from queue ${queue} with lockTimeout: ${lockTimeout}ms`);
|
|
479
524
|
}
|
|
480
|
-
const channel = await this.getNewChannel({});
|
|
525
|
+
const channel = await this.getNewChannel({ connection: this.consumeConnection });
|
|
481
526
|
return channel.addSetup(async (confirmChannel) => {
|
|
482
|
-
|
|
527
|
+
await this.assertQueue(queue, optionsWithDefaults);
|
|
483
528
|
await confirmChannel.prefetch(limit, false);
|
|
484
529
|
const { consumerTag } = await confirmChannel.consume(queue, async (msg) => {
|
|
485
530
|
if (!msg) {
|
|
486
531
|
return null;
|
|
487
532
|
}
|
|
488
|
-
const traceId = msg.properties.headers
|
|
489
|
-
const userId = msg.properties.headers[consts_1.USER_TRACING_HEADER];
|
|
490
|
-
const automationId = msg.properties.headers[consts_1.AUTOMATION_ID_HEADER];
|
|
533
|
+
const { [consts_1.TRACING_HEADER]: traceId, [consts_1.USER_TRACING_HEADER]: userId, [consts_1.AUTOMATION_ID_HEADER]: automationId } = msg.properties.headers ?? {};
|
|
491
534
|
const parsedMessage = RabbitMq.parseMsg(msg);
|
|
492
535
|
const releaseLock = await this.lockRedisIfNeeded(parsedMessage, optionsWithDefaults);
|
|
493
536
|
const trace = (0, zehut_1.newTrace)(zehut_1.traceTypes.RABBIT);
|
|
@@ -503,7 +546,7 @@ class RabbitMq {
|
|
|
503
546
|
]);
|
|
504
547
|
}
|
|
505
548
|
catch (e) {
|
|
506
|
-
|
|
549
|
+
__classPrivateFieldGet(this, _RabbitMq_logger, "f").error('rabbit: failed to setRabbitTrace', { userId, e });
|
|
507
550
|
return this.nack(confirmChannel, queue, optionsWithDefaults, { messageTtl: deadMessageTtl }, msg, releaseLock)(msg);
|
|
508
551
|
}
|
|
509
552
|
}
|
|
@@ -529,29 +572,29 @@ class RabbitMq {
|
|
|
529
572
|
return;
|
|
530
573
|
}
|
|
531
574
|
messageAcked = true;
|
|
532
|
-
|
|
575
|
+
await this.ack(confirmChannel, msg, true, releaseLock)(msg);
|
|
533
576
|
};
|
|
534
577
|
const localNack = async (_, nackOptions = {}) => {
|
|
535
578
|
if (messageAcked) {
|
|
536
579
|
return;
|
|
537
580
|
}
|
|
538
|
-
debug('rabbit localNack', { messageAcked, uniqueId, deliveryTag: msg.fields.deliveryTag });
|
|
581
|
+
__classPrivateFieldGet(this, _RabbitMq_logger, "f").debug('rabbit localNack', { messageAcked, uniqueId, deliveryTag: msg.fields.deliveryTag });
|
|
539
582
|
messageAcked = true;
|
|
540
|
-
|
|
583
|
+
await this.nack(confirmChannel, queue, optionsWithDefaults, { messageTtl: deadMessageTtl }, msg, releaseLock)(msg, nackOptions);
|
|
541
584
|
};
|
|
542
585
|
try {
|
|
543
|
-
await callback(parsedMessage, localAck, localNack);
|
|
586
|
+
return await callback(parsedMessage, localAck, localNack);
|
|
544
587
|
}
|
|
545
588
|
catch (e) {
|
|
546
|
-
|
|
589
|
+
return localNack(msg);
|
|
547
590
|
}
|
|
548
591
|
}, types_1.CONSUMER_DEFAULT_OPTIONS);
|
|
549
592
|
if (!consumerTag) {
|
|
550
|
-
|
|
593
|
+
__classPrivateFieldGet(this, _RabbitMq_logger, "f").error(`rabbit: failed to consume from queue ${queue}`);
|
|
551
594
|
}
|
|
552
595
|
else {
|
|
553
|
-
|
|
554
|
-
this.consumersTags.
|
|
596
|
+
__classPrivateFieldGet(this, _RabbitMq_logger, "f").info(`rabbit: adding tag ${consumerTag} to the array.`);
|
|
597
|
+
this.consumersTags.set(queue, { channel: confirmChannel, consumerTag });
|
|
555
598
|
}
|
|
556
599
|
});
|
|
557
600
|
}
|
|
@@ -560,11 +603,16 @@ class RabbitMq {
|
|
|
560
603
|
const optionsWithDefaults = { ...consts_1.DEFAULT_OPTIONS, ...options };
|
|
561
604
|
RabbitMq.validateName('exchange', exchange);
|
|
562
605
|
RabbitMq.validateName('queue', queue);
|
|
563
|
-
const { limit
|
|
564
|
-
if
|
|
565
|
-
|
|
606
|
+
const { limit } = optionsWithDefaults;
|
|
607
|
+
// TODO: [QUORUM-PHASE-3] Delete the if statement after all the queues are created as quorum queues
|
|
608
|
+
if (options?.isQuorumQueue !== false && DISABLE_QUORUM_QUEUES_CONSUME !== 'true') {
|
|
566
609
|
await this.saveConsumer(queue, callback, options);
|
|
567
|
-
const
|
|
610
|
+
const backoffConfig = (0, utils_1.getAssertVhostExponentialBackoffConfig)();
|
|
611
|
+
await (0, exponential_backoff_1.backOff)(() => this.assertVHost(), backoffConfig);
|
|
612
|
+
const channel = await this.getNewChannel({
|
|
613
|
+
name: `consume-exchange-${exchange}-queue-${queue}`,
|
|
614
|
+
connection: this.consumeConnection,
|
|
615
|
+
});
|
|
568
616
|
await channel.addSetup(async (c) => {
|
|
569
617
|
const assertExchange = await (0, utils_1.assertExchangeFanout)(c, exchange);
|
|
570
618
|
await c.assertQueue(queue);
|
|
@@ -576,9 +624,12 @@ class RabbitMq {
|
|
|
576
624
|
]);
|
|
577
625
|
});
|
|
578
626
|
}
|
|
579
|
-
// TODO:
|
|
627
|
+
// TODO: [QUORUM-PHASE-3] Delete the old implementation
|
|
580
628
|
await this.saveConsumerOld(queue, callback, options);
|
|
581
|
-
const channelOld = await this.getNewChannelOld({
|
|
629
|
+
const channelOld = await this.getNewChannelOld({
|
|
630
|
+
name: `consume-exchange-${exchange}-queue-${queue}-old`,
|
|
631
|
+
connection: this.oldConsumeConnection,
|
|
632
|
+
});
|
|
582
633
|
await channelOld.addSetup(async (c) => {
|
|
583
634
|
const assertExchange = await (0, utils_1.assertExchangeFanout)(c, exchange);
|
|
584
635
|
await c.assertQueue(queue);
|
|
@@ -591,35 +642,33 @@ class RabbitMq {
|
|
|
591
642
|
});
|
|
592
643
|
}
|
|
593
644
|
// Used by the microservices to publish messages to the exchange
|
|
645
|
+
// TODO: [QUORUM-PHASE-3] isQuorumQueue flag is not needed anymore because all the queues are created as quorum queues
|
|
594
646
|
async publish(exchange, content, customHeaders, isQuorumQueue = true) {
|
|
595
|
-
|
|
596
|
-
if (isQuorumQueue) {
|
|
597
|
-
|
|
598
|
-
await this.assertVHost();
|
|
599
|
-
RabbitMq.validateName('exchange', exchange);
|
|
600
|
-
const channel = await this.assertChannel();
|
|
601
|
-
await this.assertExchange(exchange);
|
|
602
|
-
await channel.publish(exchange, '', Buffer.from(JSON.stringify(content)), RabbitMq.getPublishOptions(customHeaders));
|
|
603
|
-
});
|
|
604
|
-
}
|
|
605
|
-
// TODO: DELETE THE OLD
|
|
606
|
-
return (0, utils_1.wrapSetImmediate)(async () => {
|
|
647
|
+
await (0, promises_1.setImmediate)();
|
|
648
|
+
if (isQuorumQueue && DISABLE_QUORUM_QUEUES_PUBLISH !== 'true') {
|
|
649
|
+
await this.assertVHost();
|
|
607
650
|
RabbitMq.validateName('exchange', exchange);
|
|
608
|
-
const channel = await this.
|
|
609
|
-
await this.
|
|
651
|
+
const channel = await this.assertChannel({ connection: this.publishConnection });
|
|
652
|
+
await this.assertExchange(exchange, this.publishConnection);
|
|
610
653
|
await channel.publish(exchange, '', Buffer.from(JSON.stringify(content)), RabbitMq.getPublishOptions(customHeaders));
|
|
611
|
-
|
|
654
|
+
return;
|
|
655
|
+
}
|
|
656
|
+
// TODO: [QUORUM-PHASE-3] Delete the old implementation
|
|
657
|
+
RabbitMq.validateName('exchange', exchange);
|
|
658
|
+
const channel = await this.assertChannelOld({ connection: this.oldPublishConnection });
|
|
659
|
+
await this.assertExchangeOld(exchange, this.oldPublishConnection);
|
|
660
|
+
await channel.publish(exchange, '', Buffer.from(JSON.stringify(content)), RabbitMq.getPublishOptions(customHeaders));
|
|
612
661
|
}
|
|
613
662
|
// Used by the microservices to send messages to the queue
|
|
663
|
+
// TODO: [QUORUM-PHASE-3] isQuorumQueue flag is not needed anymore because all the queues are created as quorum queues
|
|
614
664
|
async sendToQueue(queue, content, options, customHeaders, isQuorumQueue = true) {
|
|
615
|
-
|
|
616
|
-
if (isQuorumQueue) {
|
|
665
|
+
if (isQuorumQueue && DISABLE_QUORUM_QUEUES_PUBLISH !== 'true') {
|
|
617
666
|
try {
|
|
618
667
|
await this.assertVHost();
|
|
619
|
-
await this.assertChannel();
|
|
668
|
+
await this.assertChannel({ connection: this.publishConnection });
|
|
620
669
|
}
|
|
621
670
|
catch (e) {
|
|
622
|
-
|
|
671
|
+
__classPrivateFieldGet(this, _RabbitMq_logger, "f").error(`rabbit sendToQueue: failed to send assert channel when sending to queue ${queue}`, { e });
|
|
623
672
|
throw e;
|
|
624
673
|
}
|
|
625
674
|
try {
|
|
@@ -627,110 +676,127 @@ class RabbitMq {
|
|
|
627
676
|
await this.assertQueue(queue, options);
|
|
628
677
|
}
|
|
629
678
|
catch (e) {
|
|
630
|
-
|
|
679
|
+
__classPrivateFieldGet(this, _RabbitMq_logger, "f").error(`rabbit sendToQueue: failed to assert queue ${queue}`, { e });
|
|
631
680
|
throw e;
|
|
632
681
|
}
|
|
633
682
|
try {
|
|
634
|
-
const res = await this.
|
|
635
|
-
debug(`rabbit: sending to queue ${queue}`, { res });
|
|
683
|
+
const res = await this.publishChannel?.sendToQueue(queue, Buffer.from(JSON.stringify(content)), RabbitMq.getPublishOptions(customHeaders));
|
|
684
|
+
__classPrivateFieldGet(this, _RabbitMq_logger, "f").debug(`rabbit: sending to queue ${queue}`, { res });
|
|
636
685
|
return res;
|
|
637
686
|
}
|
|
638
687
|
catch (e) {
|
|
639
688
|
const isConnected = await this.isConnected();
|
|
640
|
-
|
|
689
|
+
__classPrivateFieldGet(this, _RabbitMq_logger, "f").error(`rabbit sendToQueue: failed to send to queue ${queue}, isConnected: ${isConnected}`, { e });
|
|
641
690
|
throw e;
|
|
642
691
|
}
|
|
643
692
|
}
|
|
644
693
|
else {
|
|
645
694
|
try {
|
|
646
|
-
await this.assertChannelOld();
|
|
695
|
+
await this.assertChannelOld({ connection: this.oldPublishConnection });
|
|
647
696
|
}
|
|
648
697
|
catch (e) {
|
|
649
|
-
|
|
698
|
+
__classPrivateFieldGet(this, _RabbitMq_logger, "f").error(`rabbit sendToQueue: failed to send assert channel when sending to queue ${queue}`, { e });
|
|
699
|
+
throw e;
|
|
700
|
+
}
|
|
701
|
+
try {
|
|
702
|
+
RabbitMq.validateName('queue', queue);
|
|
703
|
+
await this.assertQueueOld(queue, options);
|
|
704
|
+
}
|
|
705
|
+
catch (e) {
|
|
706
|
+
__classPrivateFieldGet(this, _RabbitMq_logger, "f").error(`rabbit sendToQueue: failed to assert queue ${queue}`, { e });
|
|
707
|
+
throw e;
|
|
708
|
+
}
|
|
709
|
+
try {
|
|
710
|
+
const res = await this.oldPublishChannel?.sendToQueue(queue, Buffer.from(JSON.stringify(content)), RabbitMq.getPublishOptions(customHeaders));
|
|
711
|
+
__classPrivateFieldGet(this, _RabbitMq_logger, "f").debug(`rabbit: sending to queue ${queue}`, { res });
|
|
712
|
+
return res;
|
|
713
|
+
}
|
|
714
|
+
catch (e) {
|
|
715
|
+
const isConnected = await this.isConnectedOld();
|
|
716
|
+
__classPrivateFieldGet(this, _RabbitMq_logger, "f").error(`rabbit sendToQueue: failed to send to queue ${queue}, isConnected: ${isConnected}`, { e });
|
|
650
717
|
throw e;
|
|
651
718
|
}
|
|
652
|
-
}
|
|
653
|
-
try {
|
|
654
|
-
RabbitMq.validateName('queue', queue);
|
|
655
|
-
await this.assertQueueOld(queue, options);
|
|
656
|
-
}
|
|
657
|
-
catch (e) {
|
|
658
|
-
logger_1.default.error(`rabbit sendToQueue: failed to assert queue ${queue}`, { e });
|
|
659
|
-
throw e;
|
|
660
|
-
}
|
|
661
|
-
try {
|
|
662
|
-
const res = await this.oldChannel?.sendToQueue(queue, Buffer.from(JSON.stringify(content)), RabbitMq.getPublishOptions(customHeaders));
|
|
663
|
-
debug(`rabbit: sending to queue ${queue}`, { res });
|
|
664
|
-
return res;
|
|
665
|
-
}
|
|
666
|
-
catch (e) {
|
|
667
|
-
const isConnected = await this.isConnected();
|
|
668
|
-
logger_1.default.error(`rabbit sendToQueue: failed to send to queue ${queue}, isConnected: ${isConnected}`, { e });
|
|
669
|
-
throw e;
|
|
670
719
|
}
|
|
671
720
|
}
|
|
672
721
|
async isConnected() {
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
if (!
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
722
|
+
let isConnected = true;
|
|
723
|
+
// TODO:[QUORUM-PHASE-3] Remove the condition
|
|
724
|
+
if (!this.gracefulShutdownStarted) {
|
|
725
|
+
if (DISABLE_QUORUM_QUEUES_CONSUME !== 'true' || DISABLE_QUORUM_QUEUES_PUBLISH !== 'true') {
|
|
726
|
+
__classPrivateFieldGet(this, _RabbitMq_logger, "f").debug('rabbit: start is connected');
|
|
727
|
+
const [consumeConnection, publishConnection] = await Promise.all([
|
|
728
|
+
this.getConnection(this.consumeConnection),
|
|
729
|
+
this.getConnection(this.publishConnection),
|
|
730
|
+
]);
|
|
731
|
+
isConnected = consumeConnection.isConnected() && publishConnection.isConnected();
|
|
732
|
+
if (!isConnected) {
|
|
733
|
+
__classPrivateFieldGet(this, _RabbitMq_logger, "f").error('rabbit: isConnected - false');
|
|
734
|
+
return false;
|
|
735
|
+
}
|
|
736
|
+
try {
|
|
737
|
+
const unRegisteredConsumers = this.consumersToRegister.filter((c) => !this.consumersTags.get(c.queue));
|
|
738
|
+
if (unRegisteredConsumers.length > 0) {
|
|
739
|
+
const queueNames = unRegisteredConsumers.map((c) => c.queue);
|
|
740
|
+
__classPrivateFieldGet(this, _RabbitMq_logger, "f").error('rabbit: found unregistered consumers for queues', {
|
|
741
|
+
count: queueNames.length,
|
|
742
|
+
queues: queueNames,
|
|
743
|
+
});
|
|
744
|
+
throw new rabbitError_1.default('Found unregistered consumers');
|
|
745
|
+
}
|
|
746
|
+
const channel = await this.assertChannel({ connection: this.publishConnection });
|
|
747
|
+
await channel.waitForConnect();
|
|
748
|
+
await Promise.all(this.consumersToRegister.map((c) => channel.checkQueue(c.queue)));
|
|
749
|
+
}
|
|
750
|
+
catch (e) {
|
|
751
|
+
__classPrivateFieldGet(this, _RabbitMq_logger, "f").error('rabbit: isConnected - false', { msg: e.message });
|
|
752
|
+
return false;
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
// TODO:[QUORUM-PHASE-3] Delete the old is connected and adjust the return value to return true
|
|
756
|
+
isConnected = await this.isConnectedOld();
|
|
689
757
|
}
|
|
690
|
-
|
|
691
|
-
return
|
|
758
|
+
__classPrivateFieldGet(this, _RabbitMq_logger, "f").debug(`rabbit: isConnected - ${isConnected}`);
|
|
759
|
+
return isConnected;
|
|
692
760
|
}
|
|
693
761
|
async gracefulShutdown(signal) {
|
|
694
|
-
// TODO:
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
const
|
|
762
|
+
// TODO: [QUORUM-PHASE-3] Delete the old implementation they are not needed anymore
|
|
763
|
+
this.gracefulShutdownStarted = true;
|
|
764
|
+
const tagsNumber = this.consumersTags.size + this.oldConsumersTags.size;
|
|
765
|
+
__classPrivateFieldGet(this, _RabbitMq_logger, "f").info(`rabbit: [gracefully-shutdown] received ${signal}! canceling #${tagsNumber} tags...`);
|
|
766
|
+
const cancelTagPromises = [...this.consumersTags].map(([, { channel, consumerTag }]) => channel.cancel(consumerTag));
|
|
767
|
+
const cancelTagPromisesOld = [...this.oldConsumersTags].map(([, { channel, consumerTag }]) => channel.cancel(consumerTag));
|
|
699
768
|
// Clean the array to avoid race
|
|
700
|
-
this.consumersTags
|
|
701
|
-
this.oldConsumersTags
|
|
769
|
+
this.consumersTags.clear();
|
|
770
|
+
this.oldConsumersTags.clear();
|
|
702
771
|
const results = await Promise.allSettled([...cancelTagPromises, ...cancelTagPromisesOld]);
|
|
703
772
|
const rejected = results.filter((p) => p.status === 'rejected');
|
|
704
773
|
if (rejected.length > 0) {
|
|
705
|
-
|
|
774
|
+
__classPrivateFieldGet(this, _RabbitMq_logger, "f").warn(`rabbit: [gracefully-shutdown] #${rejected.length}/${tagsNumber} tags failed to cancel: ${rejected}`);
|
|
706
775
|
}
|
|
707
776
|
else {
|
|
708
|
-
|
|
777
|
+
__classPrivateFieldGet(this, _RabbitMq_logger, "f").info('rabbit: [gracefully-shutdown] all tags successfully canceled.');
|
|
709
778
|
}
|
|
710
779
|
}
|
|
711
780
|
async consumeFromRabbitOld(queue, callback, options) {
|
|
712
781
|
const optionsWithDefaults = { ...consts_1.DEFAULT_OPTIONS, ...options };
|
|
713
782
|
RabbitMq.validateName('queue', queue);
|
|
714
|
-
this.saveConsumerOld(queue, callback, options);
|
|
715
783
|
const uniqueId = (0, node_crypto_1.randomUUID)();
|
|
716
784
|
const { limit, deadMessageTtl, useConsumeWithLock, lockTimeout, auditContext, enableRabbitTrace, } = optionsWithDefaults;
|
|
717
785
|
if (useConsumeWithLock) {
|
|
718
786
|
if (!this.redisLock) {
|
|
719
|
-
throw new
|
|
787
|
+
throw new rabbitError_1.default('Usage of consumeWithLock requires RedisInstance');
|
|
720
788
|
}
|
|
721
|
-
|
|
789
|
+
__classPrivateFieldGet(this, _RabbitMq_logger, "f").info(`rabbit: Consuming with lock from queue ${queue} with lockTimeout: ${lockTimeout}ms`);
|
|
722
790
|
}
|
|
723
|
-
const channel = await this.getNewChannelOld({});
|
|
791
|
+
const channel = await this.getNewChannelOld({ connection: this.oldConsumeConnection });
|
|
724
792
|
return channel.addSetup(async (confirmChannel) => {
|
|
725
|
-
|
|
793
|
+
await this.assertQueueOld(queue, optionsWithDefaults);
|
|
726
794
|
await confirmChannel.prefetch(limit, false);
|
|
727
795
|
const { consumerTag } = await confirmChannel.consume(queue, async (msg) => {
|
|
728
796
|
if (!msg) {
|
|
729
797
|
return null;
|
|
730
798
|
}
|
|
731
|
-
const traceId = msg.properties.headers
|
|
732
|
-
const userId = msg.properties.headers[consts_1.USER_TRACING_HEADER];
|
|
733
|
-
const automationId = msg.properties.headers[consts_1.AUTOMATION_ID_HEADER];
|
|
799
|
+
const { [consts_1.TRACING_HEADER]: traceId, [consts_1.USER_TRACING_HEADER]: userId, [consts_1.AUTOMATION_ID_HEADER]: automationId } = msg.properties.headers ?? {};
|
|
734
800
|
const parsedMessage = RabbitMq.parseMsg(msg);
|
|
735
801
|
const releaseLock = await this.lockRedisIfNeeded(parsedMessage, optionsWithDefaults);
|
|
736
802
|
const trace = (0, zehut_1.newTrace)(zehut_1.traceTypes.RABBIT);
|
|
@@ -746,7 +812,7 @@ class RabbitMq {
|
|
|
746
812
|
]);
|
|
747
813
|
}
|
|
748
814
|
catch (e) {
|
|
749
|
-
|
|
815
|
+
__classPrivateFieldGet(this, _RabbitMq_logger, "f").error('rabbit: failed to setRabbitTrace', { userId, e });
|
|
750
816
|
return this.nack(confirmChannel, queue, optionsWithDefaults, { messageTtl: deadMessageTtl }, msg, releaseLock)(msg);
|
|
751
817
|
}
|
|
752
818
|
}
|
|
@@ -772,139 +838,144 @@ class RabbitMq {
|
|
|
772
838
|
return;
|
|
773
839
|
}
|
|
774
840
|
messageAcked = true;
|
|
775
|
-
|
|
841
|
+
await this.ack(confirmChannel, msg, true, releaseLock)(msg);
|
|
776
842
|
};
|
|
777
843
|
const localNack = async (_, nackOptions = {}) => {
|
|
778
844
|
if (messageAcked) {
|
|
779
845
|
return;
|
|
780
846
|
}
|
|
781
|
-
debug('rabbit localNack', { messageAcked, uniqueId, deliveryTag: msg.fields.deliveryTag });
|
|
847
|
+
__classPrivateFieldGet(this, _RabbitMq_logger, "f").debug('rabbit localNack', { messageAcked, uniqueId, deliveryTag: msg.fields.deliveryTag });
|
|
782
848
|
messageAcked = true;
|
|
783
|
-
|
|
849
|
+
await this.nack(confirmChannel, queue, optionsWithDefaults, { messageTtl: deadMessageTtl }, msg, releaseLock)(msg, nackOptions);
|
|
784
850
|
};
|
|
785
851
|
try {
|
|
786
|
-
await callback(parsedMessage, localAck, localNack);
|
|
852
|
+
return await callback(parsedMessage, localAck, localNack);
|
|
787
853
|
}
|
|
788
854
|
catch (e) {
|
|
789
|
-
|
|
855
|
+
return localNack(msg);
|
|
790
856
|
}
|
|
791
857
|
}, types_1.CONSUMER_DEFAULT_OPTIONS);
|
|
792
858
|
if (!consumerTag) {
|
|
793
|
-
|
|
859
|
+
__classPrivateFieldGet(this, _RabbitMq_logger, "f").error(`rabbit: failed to consume from queue ${queue} old`);
|
|
794
860
|
}
|
|
795
861
|
else {
|
|
796
|
-
|
|
797
|
-
this.oldConsumersTags.
|
|
862
|
+
__classPrivateFieldGet(this, _RabbitMq_logger, "f").info(`rabbit: adding tag ${consumerTag} to the array old`);
|
|
863
|
+
this.oldConsumersTags.set(queue, { channel: confirmChannel, consumerTag });
|
|
798
864
|
}
|
|
799
865
|
});
|
|
800
866
|
}
|
|
801
|
-
// TODO:
|
|
802
|
-
async getNewChannelOld({ name = (0, utils_1.rand)().toString(), onClose = null, options = {}
|
|
803
|
-
let
|
|
867
|
+
// TODO: [QUORUM-PHASE-3] Delete all the function under this line.
|
|
868
|
+
async getNewChannelOld({ name = (0, utils_1.rand)().toString(), onClose = null, options = {}, connection, }) {
|
|
869
|
+
let localConnection;
|
|
804
870
|
try {
|
|
805
|
-
|
|
871
|
+
localConnection = await this.getConnectionOld(connection);
|
|
806
872
|
}
|
|
807
873
|
catch (e) {
|
|
808
|
-
|
|
874
|
+
__classPrivateFieldGet(this, _RabbitMq_logger, "f").error(`rabbit: error on get connection for new channel ${name} `, { e });
|
|
809
875
|
throw e;
|
|
810
876
|
}
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
877
|
+
if (!localConnection) {
|
|
878
|
+
throw new Error(`rabbit: couldnt get connection for new channel ${name}`);
|
|
879
|
+
}
|
|
880
|
+
const channel = localConnection?.createChannel({ ...options });
|
|
881
|
+
(0, node_events_1.once)(channel, 'close').then((args) => {
|
|
882
|
+
__classPrivateFieldGet(this, _RabbitMq_logger, "f").error(`rabbit: channel ${name} closed`);
|
|
814
883
|
onClose?.(args);
|
|
815
884
|
});
|
|
816
885
|
try {
|
|
817
|
-
await (0,
|
|
818
|
-
debug(`rabbit: channel ${name} CONNECTED`);
|
|
886
|
+
await (0, node_events_1.once)(channel, 'connect');
|
|
887
|
+
__classPrivateFieldGet(this, _RabbitMq_logger, "f").debug(`rabbit: channel ${name} CONNECTED`);
|
|
819
888
|
return channel;
|
|
820
889
|
}
|
|
821
890
|
catch (err) {
|
|
822
|
-
|
|
891
|
+
__classPrivateFieldGet(this, _RabbitMq_logger, "f").error(`rabbit: channel error ${name} error`, { err });
|
|
823
892
|
throw err;
|
|
824
893
|
}
|
|
825
894
|
}
|
|
826
|
-
async getConnectionOld() {
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
895
|
+
async getConnectionOld(connection) {
|
|
896
|
+
const { amqpConnection: connectionLocal, creatingConnection, connectionCreatedEventName, connectionFailedEventName, blockReconnect, } = connection;
|
|
897
|
+
if (blockReconnect) {
|
|
898
|
+
__classPrivateFieldGet(this, _RabbitMq_logger, "f").debug('rabbit: block reconnect');
|
|
899
|
+
// @ts-expect-error we are returning undefined while the function clearly expects a connection.
|
|
900
|
+
return undefined;
|
|
901
|
+
}
|
|
902
|
+
if (connectionLocal !== null) {
|
|
903
|
+
if (this.options?.disableReconnect || connectionLocal?.isConnected()) {
|
|
904
|
+
__classPrivateFieldGet(this, _RabbitMq_logger, "f").debug('rabbit: connection - is connected');
|
|
830
905
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
831
906
|
// @ts-ignore
|
|
832
|
-
return
|
|
833
|
-
}
|
|
834
|
-
if (this.oldConnection !== null) {
|
|
835
|
-
if (this.options?.disableReconnect || this.oldConnection?.isConnected()) {
|
|
836
|
-
debug('rabbit: connection - is connected');
|
|
837
|
-
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
838
|
-
// @ts-ignore
|
|
839
|
-
return resolve(this.oldConnection);
|
|
840
|
-
}
|
|
841
|
-
debug('rabbit: connection - reconnecting');
|
|
907
|
+
return connectionLocal;
|
|
842
908
|
}
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
909
|
+
__classPrivateFieldGet(this, _RabbitMq_logger, "f").debug('rabbit: connection - reconnecting');
|
|
910
|
+
}
|
|
911
|
+
if (creatingConnection) {
|
|
912
|
+
const [event, value] = await Promise.race([connectionCreatedEventName, connectionFailedEventName].map((e) => (0, node_events_1.once)(this.oldEm, e).then(([result]) => [e, result])));
|
|
913
|
+
if (event === connectionCreatedEventName) {
|
|
914
|
+
return value;
|
|
848
915
|
}
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
};
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
reject(err);
|
|
871
|
-
this.oldEm.emit(consts_1.CONNECTION_FAILED_CONST, err);
|
|
872
|
-
}
|
|
873
|
-
});
|
|
874
|
-
this.oldConnection.on('connectFailed', (err) => {
|
|
875
|
-
this.oldConsumersTags = [];
|
|
876
|
-
logger_1.default.error('rabbit: connection connectFailed', { err });
|
|
877
|
-
if (!isResolved) {
|
|
878
|
-
isResolved = true;
|
|
879
|
-
reject(err);
|
|
880
|
-
this.oldEm.emit(consts_1.CONNECTION_FAILED_CONST, err);
|
|
881
|
-
}
|
|
882
|
-
});
|
|
883
|
-
this.oldConnection.on('disconnect', ({ err }) => {
|
|
884
|
-
this.oldConsumersTags = [];
|
|
885
|
-
debug('rabbit: connection closed');
|
|
886
|
-
if (this.options?.disableReconnect) {
|
|
887
|
-
logger_1.default.error(`${this.DISCONNECT_MSG}${err && ` - ${err}`}`);
|
|
888
|
-
this.oldBlockReconnect = true;
|
|
889
|
-
}
|
|
890
|
-
else {
|
|
891
|
-
logger_1.default.error(`${this.RECONNECT_MSG}${err && ` - ${err}`}`);
|
|
892
|
-
}
|
|
893
|
-
});
|
|
894
|
-
this.oldConnection.once('connect', async () => {
|
|
895
|
-
debug('rabbit: connection established');
|
|
896
|
-
this.oldCreatingConnection = false;
|
|
897
|
-
this.oldEm.emit(consts_1.CONNECTION_CREATED_CONST, connection);
|
|
916
|
+
throw value;
|
|
917
|
+
}
|
|
918
|
+
connection.creatingConnection = true;
|
|
919
|
+
let isResolved = false;
|
|
920
|
+
// It is import to use it as a function and not as a variable
|
|
921
|
+
// because of k8s changes the env variables
|
|
922
|
+
// and we want to use the new values
|
|
923
|
+
const findServers = () => {
|
|
924
|
+
const userName = process.env.RABBITMQ_USERNAME || 'guest';
|
|
925
|
+
const password = process.env.RABBITMQ_PASSWORD || 'guest';
|
|
926
|
+
const host = this.options?.rabbitHost || process.env.RABBITMQ_SERVICE_HOST || 'localhost';
|
|
927
|
+
__classPrivateFieldGet(this, _RabbitMq_logger, "f").debug('rabbit: creating connection', { host, userName, HEARTBEAT });
|
|
928
|
+
return [`amqp://${userName}:${password}@${host}?heartbeat=${HEARTBEAT}`];
|
|
929
|
+
};
|
|
930
|
+
const defaultUrls = findServers();
|
|
931
|
+
const newConnection = await (0, amqp_connection_manager_1.connect)(defaultUrls, { findServers });
|
|
932
|
+
connection.amqpConnection = newConnection;
|
|
933
|
+
const { promise, reject, resolve } = (0, utils_1.createDeferredPromise)();
|
|
934
|
+
newConnection.on('error', (err) => {
|
|
935
|
+
__classPrivateFieldGet(this, _RabbitMq_logger, "f").error('rabbit: connection error', { err });
|
|
936
|
+
if (!isResolved) {
|
|
898
937
|
isResolved = true;
|
|
899
|
-
|
|
900
|
-
|
|
938
|
+
reject(err);
|
|
939
|
+
this.oldEm.emit(connectionFailedEventName, err);
|
|
940
|
+
}
|
|
941
|
+
});
|
|
942
|
+
newConnection.on('connectFailed', (err) => {
|
|
943
|
+
this.oldConsumersTags.clear();
|
|
944
|
+
if (typeof err.url === 'string') {
|
|
945
|
+
err.url = this.maskURL(err.url);
|
|
946
|
+
}
|
|
947
|
+
logger_1.default.error('rabbit: connection connectFailed', { err });
|
|
948
|
+
if (!isResolved) {
|
|
949
|
+
isResolved = true;
|
|
950
|
+
reject(err);
|
|
951
|
+
this.oldEm.emit(connectionFailedEventName, err);
|
|
952
|
+
}
|
|
953
|
+
});
|
|
954
|
+
newConnection.on('disconnect', ({ err }) => {
|
|
955
|
+
this.oldConsumersTags.clear();
|
|
956
|
+
__classPrivateFieldGet(this, _RabbitMq_logger, "f").debug('rabbit: connection closed');
|
|
957
|
+
if (this.options?.disableReconnect) {
|
|
958
|
+
logger_1.default.error(`${this.DISCONNECT_MSG}${err && ` - ${err}`}`);
|
|
959
|
+
connection.blockReconnect = true;
|
|
960
|
+
}
|
|
961
|
+
else {
|
|
962
|
+
logger_1.default.error(`${this.RECONNECT_MSG}${err && ` - ${err}`}`);
|
|
963
|
+
}
|
|
901
964
|
});
|
|
965
|
+
newConnection.once('connect', async () => {
|
|
966
|
+
__classPrivateFieldGet(this, _RabbitMq_logger, "f").debug('rabbit: connection established');
|
|
967
|
+
connection.creatingConnection = false;
|
|
968
|
+
this.oldEm.emit(connectionCreatedEventName, newConnection);
|
|
969
|
+
isResolved = true;
|
|
970
|
+
resolve(newConnection);
|
|
971
|
+
});
|
|
972
|
+
return promise;
|
|
902
973
|
}
|
|
903
974
|
saveConsumerOld(queue, callback, options) {
|
|
904
|
-
const isConsumerExist = this.
|
|
975
|
+
const isConsumerExist = this.oldConsumersToRegister.some((consumer) => consumer.queue === queue);
|
|
905
976
|
if (!isConsumerExist) {
|
|
906
977
|
logger_1.default.info(`rabbit: consumer: ${queue} saved in consumer array`);
|
|
907
|
-
this.
|
|
978
|
+
this.oldConsumersToRegister.push({
|
|
908
979
|
queue,
|
|
909
980
|
callback,
|
|
910
981
|
options,
|
|
@@ -912,19 +983,18 @@ class RabbitMq {
|
|
|
912
983
|
}
|
|
913
984
|
}
|
|
914
985
|
async assertQueueOld(queueName, options) {
|
|
986
|
+
var _a;
|
|
915
987
|
RabbitMq.validateName('queue', queueName);
|
|
916
988
|
if (this.oldQueues[queueName]) {
|
|
917
989
|
delete this.oldQueueSetupPromises[queueName];
|
|
918
990
|
return this.oldQueues[queueName];
|
|
919
991
|
}
|
|
920
|
-
|
|
921
|
-
return this.oldQueueSetupPromises[queueName];
|
|
922
|
-
}
|
|
923
|
-
this.oldQueueSetupPromises[queueName] = this.setupQueueOld(queueName, options);
|
|
992
|
+
(_a = this.oldQueueSetupPromises)[queueName] || (_a[queueName] = this.setupQueueOld(queueName, options));
|
|
924
993
|
return this.oldQueueSetupPromises[queueName];
|
|
925
994
|
}
|
|
926
995
|
async setupQueueOld(queueName, options) {
|
|
927
996
|
let queue;
|
|
997
|
+
const connection = this.oldPublishConnection;
|
|
928
998
|
const shouldUseQuorum = RabbitMq.shouldUseQuorum(queueName);
|
|
929
999
|
const localeOptions = {
|
|
930
1000
|
...options,
|
|
@@ -936,21 +1006,21 @@ class RabbitMq {
|
|
|
936
1006
|
},
|
|
937
1007
|
};
|
|
938
1008
|
try {
|
|
939
|
-
const channel = await this.assertChannelOld();
|
|
940
|
-
debug('assertQueue->channel.addSetup', { queueName });
|
|
1009
|
+
const channel = await this.assertChannelOld({ connection });
|
|
1010
|
+
__classPrivateFieldGet(this, _RabbitMq_logger, "f").debug('assertQueue->channel.addSetup', { queueName });
|
|
941
1011
|
await channel.addSetup((setupChannel) => setupChannel.assertQueue(queueName, localeOptions));
|
|
942
|
-
debug('assertQueue->channel.assertQueue', { queueName });
|
|
1012
|
+
__classPrivateFieldGet(this, _RabbitMq_logger, "f").debug('assertQueue->channel.assertQueue', { queueName });
|
|
943
1013
|
queue = await channel.assertQueue(queueName, localeOptions);
|
|
944
1014
|
}
|
|
945
1015
|
catch (e) {
|
|
946
1016
|
logger_1.default.error('rabbit: assertQueue error', { queueName, options, error: e });
|
|
947
1017
|
if (!this.options?.dontRetryAssert) {
|
|
948
|
-
debug('retrying assertQueue', { queueName });
|
|
949
|
-
const channel = await this.assertChannelOld({ force: true });
|
|
950
|
-
await this.deleteQueueOld(queueName);
|
|
951
|
-
debug('retrying assertQueue->channel.addSetup', { queueName });
|
|
1018
|
+
__classPrivateFieldGet(this, _RabbitMq_logger, "f").debug('retrying assertQueue', { queueName });
|
|
1019
|
+
const channel = await this.assertChannelOld({ force: true, connection });
|
|
1020
|
+
await this.deleteQueueOld(queueName, connection);
|
|
1021
|
+
__classPrivateFieldGet(this, _RabbitMq_logger, "f").debug('retrying assertQueue->channel.addSetup', { queueName });
|
|
952
1022
|
await channel.addSetup((setupChannel) => setupChannel.assertQueue(queueName, localeOptions));
|
|
953
|
-
debug('retrying assertQueue->channel.assertQueue', { queueName });
|
|
1023
|
+
__classPrivateFieldGet(this, _RabbitMq_logger, "f").debug('retrying assertQueue->channel.assertQueue', { queueName });
|
|
954
1024
|
queue = await channel.assertQueue(queueName, localeOptions);
|
|
955
1025
|
}
|
|
956
1026
|
else {
|
|
@@ -960,37 +1030,41 @@ class RabbitMq {
|
|
|
960
1030
|
this.oldQueues[queueName] = queue;
|
|
961
1031
|
return queue;
|
|
962
1032
|
}
|
|
963
|
-
async assertChannelOld({ force = false
|
|
964
|
-
if (
|
|
965
|
-
this.oldPublishChannelSetupPromise
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
}
|
|
977
|
-
catch (e) {
|
|
978
|
-
reject(e);
|
|
979
|
-
}
|
|
1033
|
+
async assertChannelOld({ force = false, connection }) {
|
|
1034
|
+
if (this.oldPublishChannelSetupPromise) {
|
|
1035
|
+
return this.oldPublishChannelSetupPromise;
|
|
1036
|
+
}
|
|
1037
|
+
const { promise, resolve, reject } = (0, utils_1.createDeferredPromise)();
|
|
1038
|
+
this.oldPublishChannelSetupPromise = promise;
|
|
1039
|
+
if (this.oldPublishChannel && !force) {
|
|
1040
|
+
resolve(this.oldPublishChannel);
|
|
1041
|
+
return promise;
|
|
1042
|
+
}
|
|
1043
|
+
try {
|
|
1044
|
+
const channel = await this.getNewChannelOld({ connection });
|
|
1045
|
+
channel.on('error', (err) => {
|
|
1046
|
+
logger_1.default.error('rabbit: channel error', { err });
|
|
980
1047
|
});
|
|
1048
|
+
if (this.oldPublishConnection === connection) {
|
|
1049
|
+
this.oldPublishChannel = channel;
|
|
1050
|
+
}
|
|
1051
|
+
resolve(channel);
|
|
981
1052
|
}
|
|
982
|
-
|
|
1053
|
+
catch (e) {
|
|
1054
|
+
reject(e);
|
|
1055
|
+
}
|
|
1056
|
+
return promise;
|
|
983
1057
|
}
|
|
984
|
-
async deleteQueueOld(queue) {
|
|
1058
|
+
async deleteQueueOld(queue, connection) {
|
|
985
1059
|
RabbitMq.validateName('queue', queue);
|
|
986
|
-
const channel = await this.assertChannelOld();
|
|
1060
|
+
const channel = await this.assertChannelOld({ connection });
|
|
987
1061
|
logger_1.default.info('rabbit: deleting queue', { queue });
|
|
988
1062
|
const deleteQueueRes = await channel.deleteQueue(queue);
|
|
989
|
-
debug('queue deleted', deleteQueueRes);
|
|
1063
|
+
__classPrivateFieldGet(this, _RabbitMq_logger, "f").debug('queue deleted', deleteQueueRes);
|
|
990
1064
|
return deleteQueueRes;
|
|
991
1065
|
}
|
|
992
|
-
async assertExchangeOld(exchangeName,
|
|
993
|
-
const channel = await this.assertChannelOld();
|
|
1066
|
+
async assertExchangeOld(exchangeName, connection) {
|
|
1067
|
+
const channel = await this.assertChannelOld({ connection });
|
|
994
1068
|
if (this.oldExchanges[exchangeName]) {
|
|
995
1069
|
delete this.oldAssertExchangePromises[exchangeName];
|
|
996
1070
|
return this.oldExchanges[exchangeName];
|
|
@@ -1002,7 +1076,42 @@ class RabbitMq {
|
|
|
1002
1076
|
this.oldExchanges[exchangeName] = await this.oldAssertExchangePromises[exchangeName];
|
|
1003
1077
|
return this.oldExchanges[exchangeName];
|
|
1004
1078
|
}
|
|
1079
|
+
async isConnectedOld() {
|
|
1080
|
+
if (!this.gracefulShutdownStarted) {
|
|
1081
|
+
__classPrivateFieldGet(this, _RabbitMq_logger, "f").debug('rabbit: start is connected old');
|
|
1082
|
+
const [oldConsumeConnection, oldPublishConnection] = await Promise.all([
|
|
1083
|
+
this.getConnectionOld(this.oldConsumeConnection),
|
|
1084
|
+
this.getConnectionOld(this.oldPublishConnection),
|
|
1085
|
+
]);
|
|
1086
|
+
const isConnected = oldConsumeConnection.isConnected() && oldPublishConnection.isConnected();
|
|
1087
|
+
if (!isConnected) {
|
|
1088
|
+
logger_1.default.error('rabbit: isConnected old - false');
|
|
1089
|
+
return false;
|
|
1090
|
+
}
|
|
1091
|
+
try {
|
|
1092
|
+
const unRegisteredConsumersOld = this.consumersToRegister.filter((c) => !this.consumersTags.get(c.queue));
|
|
1093
|
+
if (unRegisteredConsumersOld.length > 0) {
|
|
1094
|
+
const queueNames = unRegisteredConsumersOld.map((c) => c.queue);
|
|
1095
|
+
logger_1.default.error('rabbit: found unregistered consumers for queues old', {
|
|
1096
|
+
count: queueNames.length,
|
|
1097
|
+
queues: queueNames,
|
|
1098
|
+
});
|
|
1099
|
+
throw new rabbitError_1.default('Found unregistered consumers old');
|
|
1100
|
+
}
|
|
1101
|
+
const channelOld = await this.assertChannelOld({ connection: this.oldPublishConnection });
|
|
1102
|
+
await channelOld.waitForConnect();
|
|
1103
|
+
await Promise.all(this.oldConsumersToRegister.map((c) => channelOld.checkQueue(c.queue)));
|
|
1104
|
+
}
|
|
1105
|
+
catch (e) {
|
|
1106
|
+
logger_1.default.error('rabbit: isConnected - false old', { msg: e.message });
|
|
1107
|
+
return false;
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
return true;
|
|
1111
|
+
}
|
|
1005
1112
|
}
|
|
1113
|
+
_RabbitMq_logger = new WeakMap();
|
|
1006
1114
|
exports.default = RabbitMq;
|
|
1007
1115
|
var celery_1 = require("./lib/celery");
|
|
1008
1116
|
Object.defineProperty(exports, "sendCeleryTaskViaHttp", { enumerable: true, get: function () { return celery_1.sendCeleryTaskViaHttp; } });
|
|
1117
|
+
//# sourceMappingURL=index.js.map
|