@seidor-cloud-produtos/orbit-backend-lib 2.0.125 → 2.0.128
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.
|
@@ -31,7 +31,8 @@ export default class AmqpQueue implements QueueConnection {
|
|
|
31
31
|
private static instance;
|
|
32
32
|
private vHost;
|
|
33
33
|
private connection;
|
|
34
|
-
private
|
|
34
|
+
private isConnected;
|
|
35
|
+
private reconnectionPromise?;
|
|
35
36
|
private messagesInMemory;
|
|
36
37
|
private channels;
|
|
37
38
|
protected socketOptions?: SocketOptions;
|
|
@@ -118,6 +119,8 @@ export default class AmqpQueue implements QueueConnection {
|
|
|
118
119
|
* Quando a conexão fecha, a lib tenta reconectar em loop e, ao
|
|
119
120
|
* reconectar, reativa consumidores e reenvia mensagens em memória.
|
|
120
121
|
*/
|
|
122
|
+
private getActiveConnection;
|
|
123
|
+
private scheduleReconnection;
|
|
121
124
|
private treatReconnection;
|
|
122
125
|
/**
|
|
123
126
|
* Reinscreve todos os consumidores registrados anteriormente.
|
|
@@ -20,7 +20,8 @@ class AmqpQueue {
|
|
|
20
20
|
static instance;
|
|
21
21
|
vHost;
|
|
22
22
|
connection;
|
|
23
|
-
|
|
23
|
+
isConnected = false;
|
|
24
|
+
reconnectionPromise;
|
|
24
25
|
messagesInMemory = [];
|
|
25
26
|
channels = new Map();
|
|
26
27
|
socketOptions;
|
|
@@ -58,11 +59,12 @@ class AmqpQueue {
|
|
|
58
59
|
const urlConnection = `${this.uri}${buildedVhost}`;
|
|
59
60
|
this.connection = await amqplib_1.default.connect(urlConnection, this.socketOptions);
|
|
60
61
|
this.vHost = vHost;
|
|
61
|
-
this.
|
|
62
|
-
|
|
62
|
+
this.isConnected = true;
|
|
63
|
+
this.connection.on('error', async () => {
|
|
64
|
+
return await this.scheduleReconnection(vHost);
|
|
63
65
|
});
|
|
64
66
|
this.connection.on('close', async () => {
|
|
65
|
-
return await this.
|
|
67
|
+
return await this.scheduleReconnection(vHost);
|
|
66
68
|
});
|
|
67
69
|
if (process.env.NODE_ENV !== 'test') {
|
|
68
70
|
this.logger.info(`⚙️ PROCESS ${this.vHost} PID: `, process.pid);
|
|
@@ -75,6 +77,9 @@ class AmqpQueue {
|
|
|
75
77
|
retryCount >= this.socketOptions?.retry?.maxCount) {
|
|
76
78
|
throw e;
|
|
77
79
|
}
|
|
80
|
+
if (retryCount === 0) {
|
|
81
|
+
await this.close().catch(() => null);
|
|
82
|
+
}
|
|
78
83
|
this.logger.info(`Retrying connection in... ${this.socketOptions.retry.intervalMs || 0}ms`);
|
|
79
84
|
await (0, timeout_1.sleep)(this.socketOptions.retry.intervalMs);
|
|
80
85
|
return await this.connect(vHost, retryCount + 1);
|
|
@@ -84,6 +89,7 @@ class AmqpQueue {
|
|
|
84
89
|
* Fecha a conexão atual com o broker.
|
|
85
90
|
*/
|
|
86
91
|
async close() {
|
|
92
|
+
this.isConnected = false;
|
|
87
93
|
this.connection.removeAllListeners('close');
|
|
88
94
|
await this.connection.close();
|
|
89
95
|
if (process.env.NODE_ENV !== 'test') {
|
|
@@ -92,7 +98,8 @@ class AmqpQueue {
|
|
|
92
98
|
}
|
|
93
99
|
async get(queueName, options) {
|
|
94
100
|
try {
|
|
95
|
-
const
|
|
101
|
+
const connection = await this.getActiveConnection();
|
|
102
|
+
const channel = await connection.createChannel();
|
|
96
103
|
const rawMessage = await channel.get(queueName, options);
|
|
97
104
|
if (!rawMessage) {
|
|
98
105
|
await channel.close();
|
|
@@ -142,7 +149,8 @@ class AmqpQueue {
|
|
|
142
149
|
*/
|
|
143
150
|
async on(queueName, callback) {
|
|
144
151
|
try {
|
|
145
|
-
const
|
|
152
|
+
const connection = await this.getActiveConnection();
|
|
153
|
+
const channel = await connection.createChannel();
|
|
146
154
|
this.channels.set(queueName, callback);
|
|
147
155
|
await channel.prefetch(callback.getSimultaneity());
|
|
148
156
|
await channel.consume(queueName, async (message) => {
|
|
@@ -251,7 +259,8 @@ class AmqpQueue {
|
|
|
251
259
|
}
|
|
252
260
|
}
|
|
253
261
|
async publishToServerWaitForConfirms(exchangeName, domainEvent, buildedConfigs) {
|
|
254
|
-
const
|
|
262
|
+
const connection = await this.getActiveConnection();
|
|
263
|
+
const channel = await connection.createConfirmChannel();
|
|
255
264
|
channel.publish(exchangeName, domainEvent.name || '', Buffer.from(JSON.stringify(domainEvent)), {
|
|
256
265
|
persistent: true,
|
|
257
266
|
...buildedConfigs,
|
|
@@ -260,7 +269,8 @@ class AmqpQueue {
|
|
|
260
269
|
await channel.close();
|
|
261
270
|
}
|
|
262
271
|
async publishToServer(exchangeName, domainEvent, buildedConfigs) {
|
|
263
|
-
const
|
|
272
|
+
const connection = await this.getActiveConnection();
|
|
273
|
+
const channel = await connection.createChannel();
|
|
264
274
|
channel.publish(exchangeName, domainEvent.name || '', Buffer.from(JSON.stringify(domainEvent)), {
|
|
265
275
|
persistent: true,
|
|
266
276
|
...buildedConfigs,
|
|
@@ -278,7 +288,8 @@ class AmqpQueue {
|
|
|
278
288
|
*/
|
|
279
289
|
async createConsumers(queueName, configs) {
|
|
280
290
|
try {
|
|
281
|
-
const
|
|
291
|
+
const connection = await this.getActiveConnection();
|
|
292
|
+
const channel = await connection.createChannel();
|
|
282
293
|
await channel.assertExchange(configs.exchangeName, configs.exchangeType, {
|
|
283
294
|
durable: true,
|
|
284
295
|
});
|
|
@@ -347,14 +358,29 @@ class AmqpQueue {
|
|
|
347
358
|
* Quando a conexão fecha, a lib tenta reconectar em loop e, ao
|
|
348
359
|
* reconectar, reativa consumidores e reenvia mensagens em memória.
|
|
349
360
|
*/
|
|
350
|
-
async
|
|
351
|
-
if (this.
|
|
352
|
-
|
|
361
|
+
async getActiveConnection() {
|
|
362
|
+
if (!this.isConnected && this.reconnectionPromise) {
|
|
363
|
+
await this.reconnectionPromise;
|
|
364
|
+
}
|
|
365
|
+
if (!this.connection) {
|
|
366
|
+
throw new infra_error_1.default('RabbitMQ connection is not available!');
|
|
367
|
+
}
|
|
368
|
+
return this.connection;
|
|
369
|
+
}
|
|
370
|
+
async scheduleReconnection(vHost) {
|
|
371
|
+
this.isConnected = false;
|
|
372
|
+
if (!this.reconnectionPromise) {
|
|
373
|
+
this.reconnectionPromise = this.treatReconnection(vHost).finally(() => {
|
|
374
|
+
this.reconnectionPromise = undefined;
|
|
375
|
+
});
|
|
353
376
|
}
|
|
354
|
-
this.
|
|
377
|
+
return await this.reconnectionPromise;
|
|
378
|
+
}
|
|
379
|
+
async treatReconnection(vHost) {
|
|
380
|
+
this.isConnected = false;
|
|
355
381
|
this.logger.info('⛔ Connection Error!!');
|
|
356
382
|
this.logger.info(`🔄 Try connection to the vHost ${vHost}`);
|
|
357
|
-
await this.connect(vHost);
|
|
383
|
+
AmqpQueue.instance = await this.connect(vHost);
|
|
358
384
|
await this.reconnectionChannels();
|
|
359
385
|
await this.publishMessagesOfMemory();
|
|
360
386
|
}
|
|
@@ -333,6 +333,36 @@ const createLogger = () => ({
|
|
|
333
333
|
},
|
|
334
334
|
]);
|
|
335
335
|
});
|
|
336
|
+
(0, vitest_1.it)('should wait for an in-flight reconnection before publishing', async () => {
|
|
337
|
+
let finishReconnection;
|
|
338
|
+
const oldConnection = createConnection();
|
|
339
|
+
const channel = createChannel();
|
|
340
|
+
const newConnection = createConnection(channel);
|
|
341
|
+
const queue = new amqp_lib_1.default(undefined, 'amqp://broker', createLogger());
|
|
342
|
+
const event = { name: 'domain.event' };
|
|
343
|
+
queue['connection'] = oldConnection;
|
|
344
|
+
queue['vHost'] = 'jobs';
|
|
345
|
+
queue['isConnected'] = false;
|
|
346
|
+
queue['reconnectionPromise'] = new Promise(resolve => {
|
|
347
|
+
finishReconnection = () => {
|
|
348
|
+
queue['connection'] = newConnection;
|
|
349
|
+
queue['isConnected'] = true;
|
|
350
|
+
resolve();
|
|
351
|
+
};
|
|
352
|
+
});
|
|
353
|
+
const publishPromise = queue.publish('exchange', event);
|
|
354
|
+
await Promise.resolve();
|
|
355
|
+
(0, vitest_1.expect)(oldConnection.createChannel).not.toHaveBeenCalled();
|
|
356
|
+
(0, vitest_1.expect)(newConnection.createChannel).not.toHaveBeenCalled();
|
|
357
|
+
finishReconnection();
|
|
358
|
+
await publishPromise;
|
|
359
|
+
(0, vitest_1.expect)(oldConnection.createChannel).not.toHaveBeenCalled();
|
|
360
|
+
(0, vitest_1.expect)(newConnection.createChannel).toHaveBeenCalledTimes(1);
|
|
361
|
+
(0, vitest_1.expect)(channel.publish).toHaveBeenCalledWith('exchange', 'domain.event', Buffer.from(JSON.stringify(event)), {
|
|
362
|
+
persistent: true,
|
|
363
|
+
expiration: FOURTEEN_DAYS_IN_MS,
|
|
364
|
+
});
|
|
365
|
+
});
|
|
336
366
|
(0, vitest_1.it)('should not store messages when storeMessageOnError is false', async () => {
|
|
337
367
|
const queue = new amqp_lib_1.default(undefined, 'amqp://broker', createLogger());
|
|
338
368
|
queue['publishToServer'] = vitest_1.vi.fn().mockRejectedValue(new Error('fail'));
|