@zdavison/matador 4.0.2 → 4.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/core/fanout.d.ts +51 -0
- package/dist/core/fanout.d.ts.map +1 -1
- package/dist/core/fanout.js +141 -28
- package/dist/core/fanout.test.js +533 -7
- package/dist/core/matador.d.ts +33 -0
- package/dist/core/matador.d.ts.map +1 -1
- package/dist/core/matador.js +5 -1
- package/dist/core/matador.test.js +242 -6
- package/dist/errors/index.d.ts +1 -1
- package/dist/errors/index.d.ts.map +1 -1
- package/dist/errors/index.js +2 -2
- package/dist/errors/matador-errors.d.ts +10 -8
- package/dist/errors/matador-errors.d.ts.map +1 -1
- package/dist/errors/matador-errors.js +18 -14
- package/dist/index.cjs +215 -82
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/transport/local/local-transport.d.ts +6 -0
- package/dist/transport/local/local-transport.d.ts.map +1 -1
- package/dist/transport/local/local-transport.js +35 -18
- package/dist/transport/local/local-transport.test.js +40 -4
- package/dist/transport/multi/multi-transport.d.ts.map +1 -1
- package/dist/transport/multi/multi-transport.js +3 -0
- package/dist/transport/multi/multi-transport.test.js +82 -0
- package/dist/transport/rabbitmq/rabbitmq-transport.d.ts.map +1 -1
- package/dist/transport/rabbitmq/rabbitmq-transport.js +31 -27
- package/dist/transport/transport.d.ts +6 -0
- package/dist/transport/transport.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/core/fanout.d.ts
CHANGED
|
@@ -13,6 +13,34 @@ export interface FanoutConfig {
|
|
|
13
13
|
readonly topology: Topology;
|
|
14
14
|
readonly defaultQueue: string;
|
|
15
15
|
readonly maxRetryBufferSize?: number | undefined;
|
|
16
|
+
/**
|
|
17
|
+
* Maximum number of retry attempts for a buffered message before it's
|
|
18
|
+
* dropped and reported via onEnqueueError, instead of being retried forever.
|
|
19
|
+
*
|
|
20
|
+
* @default undefined (no limit — retries until the buffer flushes successfully)
|
|
21
|
+
*/
|
|
22
|
+
readonly maxRetryAttempts?: number | undefined;
|
|
23
|
+
/**
|
|
24
|
+
* How often (in ms) to attempt flushing the retry buffer, independent of
|
|
25
|
+
* transport reconnect events. This covers the case where the transport
|
|
26
|
+
* stays connected but an individual publish keeps failing (e.g. broker
|
|
27
|
+
* nack, publish confirm timeout) — onConnected never fires again for
|
|
28
|
+
* that, so nothing would otherwise retry it.
|
|
29
|
+
*
|
|
30
|
+
* @default 30000. Pass 0 to disable the interval and rely on
|
|
31
|
+
* onConnected/manual flushes only.
|
|
32
|
+
*/
|
|
33
|
+
readonly retryIntervalMs?: number | undefined;
|
|
34
|
+
/**
|
|
35
|
+
* Number of consecutive flush failures tolerated within a single flush
|
|
36
|
+
* pass before bailing out early and re-buffering the untried remainder,
|
|
37
|
+
* instead of retrying every buffered message against a broker that's
|
|
38
|
+
* rejecting everything.
|
|
39
|
+
*
|
|
40
|
+
* @default 10. Pass 0 (or a negative number) to disable the breaker and
|
|
41
|
+
* always attempt every buffered message on every flush pass.
|
|
42
|
+
*/
|
|
43
|
+
readonly maxConsecutiveFlushFailures?: number | undefined;
|
|
16
44
|
}
|
|
17
45
|
/**
|
|
18
46
|
* Result of sending an event.
|
|
@@ -47,9 +75,13 @@ export declare class FanoutEngine {
|
|
|
47
75
|
private readonly topology;
|
|
48
76
|
private readonly defaultQueue;
|
|
49
77
|
private enqueuingCount;
|
|
78
|
+
private flushInFlightCount;
|
|
50
79
|
private readonly retryBuffer;
|
|
51
80
|
private readonly maxRetryBufferSize;
|
|
81
|
+
private readonly maxRetryAttempts;
|
|
82
|
+
private readonly maxConsecutiveFlushFailures;
|
|
52
83
|
private readonly disposeOnConnected;
|
|
84
|
+
private readonly retryTimer;
|
|
53
85
|
constructor(config: FanoutConfig);
|
|
54
86
|
dispose(): void;
|
|
55
87
|
/**
|
|
@@ -60,12 +92,31 @@ export declare class FanoutEngine {
|
|
|
60
92
|
* Sends an event to all registered subscribers.
|
|
61
93
|
*/
|
|
62
94
|
send<T>(eventClass: EventClass<T>, event: Event<T>, options?: EventOptions): Promise<SendResult>;
|
|
95
|
+
/**
|
|
96
|
+
* Attempts a single buffered item during a flush pass
|
|
97
|
+
* @returns true if the attempt succeeded; false on failure
|
|
98
|
+
*/
|
|
99
|
+
private attemptFlushItem;
|
|
63
100
|
/**
|
|
64
101
|
* Flushes the retry buffer
|
|
65
102
|
*
|
|
66
103
|
* This is called when the transport reconnects, and is used to retry any messages that were buffered while the transport was disconnected
|
|
67
104
|
*/
|
|
68
105
|
private flushRetryBuffer;
|
|
106
|
+
/**
|
|
107
|
+
* Re-buffers messages that were never attempted because a flush pass
|
|
108
|
+
* stopped out early. Respects maxRetryBufferSize: concurrent sends can have
|
|
109
|
+
* refilled the buffer while this flush was running, so anything beyond
|
|
110
|
+
* remaining capacity is dropped and reported instead of silently growing
|
|
111
|
+
* the buffer past its cap.
|
|
112
|
+
*/
|
|
113
|
+
private rebufferUntried;
|
|
114
|
+
/**
|
|
115
|
+
* Handles a failed flush attempt for a single buffered item: drops it once
|
|
116
|
+
* maxRetryAttempts is exceeded, otherwise re-buffers it (unless the buffer
|
|
117
|
+
* is full, in which case it's dropped too).
|
|
118
|
+
*/
|
|
119
|
+
private handleFlushFailure;
|
|
69
120
|
private isSubscriberEnabled;
|
|
70
121
|
}
|
|
71
122
|
//# sourceMappingURL=fanout.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fanout.d.ts","sourceRoot":"","sources":["../../src/core/fanout.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAErD,OAAO,KAAK,EAAe,SAAS,EAAE,MAAM,uBAAuB,CAAC;AACpE,OAAO,KAAK,EAGV,KAAK,EACL,UAAU,EACV,YAAY,EACb,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"fanout.d.ts","sourceRoot":"","sources":["../../src/core/fanout.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAErD,OAAO,KAAK,EAAe,SAAS,EAAE,MAAM,uBAAuB,CAAC;AACpE,OAAO,KAAK,EAGV,KAAK,EACL,UAAU,EACV,YAAY,EACb,MAAM,mBAAmB,CAAC;AAsB3B;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;IAC9B,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC;IAChC,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAC5B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAEjD;;;;;OAKG;IACH,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAE/C;;;;;;;;;OASG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAE9C;;;;;;;;OAQG;IACH,QAAQ,CAAC,2BAA2B,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC3D;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IACpC,QAAQ,CAAC,MAAM,EAAE,SAAS,SAAS,EAAE,CAAC;CACvC;AAED;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;CACvB;AAED;;;;;;;;GAQG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAY;IACtC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAiB;IACxC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAY;IAClC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAW;IACpC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,cAAc,CAAK;IAC3B,OAAO,CAAC,kBAAkB,CAAK;IAC/B,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAsB;IAClD,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAS;IAC5C,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAqB;IACtD,OAAO,CAAC,QAAQ,CAAC,2BAA2B,CAAS;IACrD,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAA2B;IAC9D,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA6C;gBAE5D,MAAM,EAAE,YAAY;IAkChC,OAAO,IAAI,IAAI;IAMf;;OAEG;IACH,IAAI,wBAAwB,IAAI,MAAM,CAErC;IAED;;OAEG;IACG,IAAI,CAAC,CAAC,EACV,UAAU,EAAE,UAAU,CAAC,CAAC,CAAC,EACzB,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,EACf,OAAO,GAAE,YAAiB,GACzB,OAAO,CAAC,UAAU,CAAC;IAgItB;;;OAGG;YACW,gBAAgB;IAsB9B;;;;OAIG;YACW,gBAAgB;IA2D9B;;;;;;OAMG;YACW,eAAe;IAqC7B;;;;OAIG;YACW,kBAAkB;YAiClB,mBAAmB;CAelC"}
|
package/dist/core/fanout.js
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import { TransportSendError } from '../errors/index.js';
|
|
2
2
|
import { resolveTargetQueueName } from '../topology/index.js';
|
|
3
3
|
import { createEnvelope } from '../types/index.js';
|
|
4
|
+
/**
|
|
5
|
+
* Default number of consecutive flush failures tolerated within a single
|
|
6
|
+
* flush pass before bailing out early and re-buffering the untried
|
|
7
|
+
* remainder.
|
|
8
|
+
*/
|
|
9
|
+
const DEFAULT_MAX_CONSECUTIVE_FLUSH_FAILURES = 10;
|
|
4
10
|
/**
|
|
5
11
|
* Engine for fanning out events to subscribers.
|
|
6
12
|
*
|
|
@@ -17,9 +23,13 @@ export class FanoutEngine {
|
|
|
17
23
|
topology;
|
|
18
24
|
defaultQueue;
|
|
19
25
|
enqueuingCount = 0;
|
|
26
|
+
flushInFlightCount = 0;
|
|
20
27
|
retryBuffer = [];
|
|
21
28
|
maxRetryBufferSize;
|
|
29
|
+
maxRetryAttempts;
|
|
30
|
+
maxConsecutiveFlushFailures;
|
|
22
31
|
disposeOnConnected;
|
|
32
|
+
retryTimer;
|
|
23
33
|
constructor(config) {
|
|
24
34
|
this.transport = config.transport;
|
|
25
35
|
this.schema = config.schema;
|
|
@@ -27,21 +37,39 @@ export class FanoutEngine {
|
|
|
27
37
|
this.topology = config.topology;
|
|
28
38
|
this.defaultQueue = config.defaultQueue;
|
|
29
39
|
this.maxRetryBufferSize = config.maxRetryBufferSize ?? 5000;
|
|
40
|
+
this.maxRetryAttempts = config.maxRetryAttempts;
|
|
41
|
+
const maxConsecutiveFlushFailures = config.maxConsecutiveFlushFailures ??
|
|
42
|
+
DEFAULT_MAX_CONSECUTIVE_FLUSH_FAILURES;
|
|
43
|
+
this.maxConsecutiveFlushFailures =
|
|
44
|
+
maxConsecutiveFlushFailures > 0
|
|
45
|
+
? maxConsecutiveFlushFailures
|
|
46
|
+
: Number.POSITIVE_INFINITY;
|
|
30
47
|
// If the transport supports it, add a callback to flush the retry buffer when the transport reconnects
|
|
31
48
|
// The returned value is a function to unsubscribe from the callback
|
|
32
49
|
this.disposeOnConnected = this.transport.onConnected?.(() => {
|
|
33
50
|
void this.flushRetryBuffer();
|
|
34
51
|
});
|
|
52
|
+
// Also flush periodically: onConnected only fires on connection-level
|
|
53
|
+
// reconnects, so it never fires when the transport stays connected but
|
|
54
|
+
// an individual publish keeps failing (e.g. broker nack, confirm timeout).
|
|
55
|
+
const retryIntervalMs = config.retryIntervalMs ?? 30_000;
|
|
56
|
+
if (retryIntervalMs > 0) {
|
|
57
|
+
this.retryTimer = setInterval(() => {
|
|
58
|
+
void this.flushRetryBuffer();
|
|
59
|
+
}, retryIntervalMs);
|
|
60
|
+
this.retryTimer.unref?.();
|
|
61
|
+
}
|
|
35
62
|
}
|
|
36
63
|
dispose() {
|
|
37
64
|
// Unsubscribe from the callback (that flushes the retry buffer when the transport reconnects)
|
|
38
65
|
this.disposeOnConnected?.();
|
|
66
|
+
clearInterval(this.retryTimer);
|
|
39
67
|
}
|
|
40
68
|
/**
|
|
41
69
|
* Current count of events being enqueued.
|
|
42
70
|
*/
|
|
43
71
|
get eventsBeingEnqueuedCount() {
|
|
44
|
-
return this.enqueuingCount;
|
|
72
|
+
return this.enqueuingCount + this.flushInFlightCount;
|
|
45
73
|
}
|
|
46
74
|
/**
|
|
47
75
|
* Sends an event to all registered subscribers.
|
|
@@ -101,8 +129,14 @@ export class FanoutEngine {
|
|
|
101
129
|
envelope,
|
|
102
130
|
sendOptions,
|
|
103
131
|
subscriberName: subscriber.name,
|
|
132
|
+
attempts: 0,
|
|
133
|
+
});
|
|
134
|
+
this.hooks.logger.warn(`[Matador] 🟡 Message for '${subscriber.name}' buffered for retry on reconnect.`, {
|
|
135
|
+
queue: qualifiedQueue,
|
|
136
|
+
subscriberName: subscriber.name,
|
|
137
|
+
bufferSize: this.retryBuffer.length,
|
|
138
|
+
maxBufferSize: this.maxRetryBufferSize,
|
|
104
139
|
});
|
|
105
|
-
this.hooks.logger.warn(`[Matador] 🟡 Message for '${subscriber.name}' buffered for retry on reconnect (buffer: ${this.retryBuffer.length}/${this.maxRetryBufferSize}).`);
|
|
106
140
|
if (options.throwOnBufferedFailure) {
|
|
107
141
|
const err = new TransportSendError(qualifiedQueue, cause);
|
|
108
142
|
// By adding the error, the main Send in Matador will throw
|
|
@@ -146,6 +180,29 @@ export class FanoutEngine {
|
|
|
146
180
|
errors,
|
|
147
181
|
};
|
|
148
182
|
}
|
|
183
|
+
/**
|
|
184
|
+
* Attempts a single buffered item during a flush pass
|
|
185
|
+
* @returns true if the attempt succeeded; false on failure
|
|
186
|
+
*/
|
|
187
|
+
async attemptFlushItem(item) {
|
|
188
|
+
this.enqueuingCount++;
|
|
189
|
+
try {
|
|
190
|
+
const usedTransport = await this.transport.send(item.queue, item.envelope, item.sendOptions);
|
|
191
|
+
await this.hooks.onEnqueueSuccess({
|
|
192
|
+
envelope: item.envelope,
|
|
193
|
+
queue: item.queue,
|
|
194
|
+
transport: usedTransport,
|
|
195
|
+
});
|
|
196
|
+
return true;
|
|
197
|
+
}
|
|
198
|
+
catch (error) {
|
|
199
|
+
await this.handleFlushFailure(item, error);
|
|
200
|
+
return false;
|
|
201
|
+
}
|
|
202
|
+
finally {
|
|
203
|
+
this.enqueuingCount--;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
149
206
|
/**
|
|
150
207
|
* Flushes the retry buffer
|
|
151
208
|
*
|
|
@@ -157,43 +214,99 @@ export class FanoutEngine {
|
|
|
157
214
|
this.hooks.logger.info(`[Matador] ⏳ Flushing ${this.retryBuffer.length} buffered message(s)...`);
|
|
158
215
|
// Drain the buffer atomically so concurrent flush calls don't double-send.
|
|
159
216
|
const toFlush = this.retryBuffer.splice(0);
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
}
|
|
170
|
-
catch (error) {
|
|
171
|
-
// Re-buffer on failure; it will be retried on the next reconnect.
|
|
172
|
-
if (this.retryBuffer.length < this.maxRetryBufferSize) {
|
|
173
|
-
this.retryBuffer.push(item);
|
|
217
|
+
// Tracked separately from enqueuingCount so shutdown doesn't see a it falsely idle while items are being flushed
|
|
218
|
+
this.flushInFlightCount += toFlush.length;
|
|
219
|
+
let consecutiveFailures = 0;
|
|
220
|
+
try {
|
|
221
|
+
for (let i = 0; i < toFlush.length; i++) {
|
|
222
|
+
const item = toFlush[i];
|
|
223
|
+
if (!item) {
|
|
224
|
+
this.flushInFlightCount--;
|
|
225
|
+
continue;
|
|
174
226
|
}
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
227
|
+
const succeeded = await this.attemptFlushItem(item);
|
|
228
|
+
this.flushInFlightCount--;
|
|
229
|
+
// Small sleep to avoid overwhelming the transport
|
|
230
|
+
// Random sleep to avoid thundering herd (between 0 and 10ms)
|
|
231
|
+
const randomMs = Math.random() * 10;
|
|
232
|
+
await new Promise((resolve) => setTimeout(resolve, randomMs));
|
|
233
|
+
// A success means the transport is actually working, so it resets the consecutive failures streak
|
|
234
|
+
consecutiveFailures = succeeded ? 0 : consecutiveFailures + 1;
|
|
235
|
+
if (consecutiveFailures >= this.maxConsecutiveFlushFailures) {
|
|
236
|
+
// A run of failures this long is more likely a connection-level issue (broker down, slow, rejecting everything)
|
|
237
|
+
// So we stop early to avoid retrying 5k messages for nothing
|
|
238
|
+
const untried = toFlush.slice(i + 1);
|
|
239
|
+
this.flushInFlightCount -= untried.length;
|
|
240
|
+
if (untried.length > 0) {
|
|
241
|
+
await this.rebufferUntried(untried, consecutiveFailures);
|
|
242
|
+
}
|
|
243
|
+
break;
|
|
183
244
|
}
|
|
184
245
|
}
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
246
|
+
}
|
|
247
|
+
finally {
|
|
248
|
+
this.flushInFlightCount = 0;
|
|
188
249
|
}
|
|
189
250
|
const remaining = this.retryBuffer.length;
|
|
190
251
|
if (remaining > 0) {
|
|
191
|
-
this.hooks.logger.warn(`[Matador] 🟡 ${remaining} buffered message(s) could not be flushed; will retry
|
|
252
|
+
this.hooks.logger.warn(`[Matador] 🟡 ${remaining} buffered message(s) could not be flushed; will retry later.`);
|
|
192
253
|
}
|
|
193
254
|
else {
|
|
194
255
|
this.hooks.logger.info('[Matador] 🟢 All buffered messages flushed successfully.');
|
|
195
256
|
}
|
|
196
257
|
}
|
|
258
|
+
/**
|
|
259
|
+
* Re-buffers messages that were never attempted because a flush pass
|
|
260
|
+
* stopped out early. Respects maxRetryBufferSize: concurrent sends can have
|
|
261
|
+
* refilled the buffer while this flush was running, so anything beyond
|
|
262
|
+
* remaining capacity is dropped and reported instead of silently growing
|
|
263
|
+
* the buffer past its cap.
|
|
264
|
+
*/
|
|
265
|
+
async rebufferUntried(untried, consecutiveFailures) {
|
|
266
|
+
const capacity = Math.max(0, this.maxRetryBufferSize - this.retryBuffer.length);
|
|
267
|
+
const toRebuffer = untried.slice(0, capacity);
|
|
268
|
+
const dropped = untried.slice(capacity);
|
|
269
|
+
if (toRebuffer.length > 0) {
|
|
270
|
+
this.retryBuffer.unshift(...toRebuffer);
|
|
271
|
+
}
|
|
272
|
+
const droppedSuffix = dropped.length > 0 ? `, ${dropped.length} dropped (buffer full).` : '.';
|
|
273
|
+
this.hooks.logger.warn(`[Matador] 🟡 Stopping this flush pass after ${consecutiveFailures} consecutive failures; ${toRebuffer.length} untried message(s) re-buffered for the next attempt${droppedSuffix}`);
|
|
274
|
+
for (const item of dropped) {
|
|
275
|
+
this.hooks.logger.error(`[Matador] 🔴 Retry buffer full (${this.maxRetryBufferSize}). Message for '${item.subscriberName}' dropped and will not be retried.`);
|
|
276
|
+
const err = new TransportSendError(item.queue, new Error('Retry buffer full'));
|
|
277
|
+
await this.hooks.onEnqueueError({
|
|
278
|
+
envelope: item.envelope,
|
|
279
|
+
error: err,
|
|
280
|
+
transport: this.transport.name,
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Handles a failed flush attempt for a single buffered item: drops it once
|
|
286
|
+
* maxRetryAttempts is exceeded, otherwise re-buffers it (unless the buffer
|
|
287
|
+
* is full, in which case it's dropped too).
|
|
288
|
+
*/
|
|
289
|
+
async handleFlushFailure(item, error) {
|
|
290
|
+
item.attempts++;
|
|
291
|
+
const cause = error instanceof Error ? error : new Error(String(error));
|
|
292
|
+
const exceededAttempts = this.maxRetryAttempts !== undefined &&
|
|
293
|
+
item.attempts >= this.maxRetryAttempts;
|
|
294
|
+
if (!exceededAttempts &&
|
|
295
|
+
this.retryBuffer.length < this.maxRetryBufferSize) {
|
|
296
|
+
// Re-buffer on failure; it will be retried on the next reconnect or flush interval.
|
|
297
|
+
this.retryBuffer.push(item);
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
if (exceededAttempts) {
|
|
301
|
+
this.hooks.logger.error(`[Matador] 🔴 Message for '${item.subscriberName}' exceeded max retry attempts (${this.maxRetryAttempts}) and will not be retried further.`);
|
|
302
|
+
}
|
|
303
|
+
const err = new TransportSendError(item.queue, cause);
|
|
304
|
+
await this.hooks.onEnqueueError({
|
|
305
|
+
envelope: item.envelope,
|
|
306
|
+
error: err,
|
|
307
|
+
transport: this.transport.name,
|
|
308
|
+
});
|
|
309
|
+
}
|
|
197
310
|
async isSubscriberEnabled(subscriber) {
|
|
198
311
|
if (!subscriber.enabled) {
|
|
199
312
|
return true;
|