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