@zdavison/matador 2.0.8 → 2.0.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/package.json +6 -2
- package/examples/config.ts +0 -126
- package/examples/event.ts +0 -26
- package/examples/order-event.json +0 -19
- package/src/checkpoint/context.test.ts +0 -510
- package/src/checkpoint/context.ts +0 -213
- package/src/checkpoint/index.ts +0 -30
- package/src/checkpoint/stores/memory.ts +0 -47
- package/src/checkpoint/stores/noop.ts +0 -22
- package/src/checkpoint/stores/stores.test.ts +0 -177
- package/src/checkpoint/types.ts +0 -147
- package/src/codec/codec.ts +0 -42
- package/src/codec/header-aware-codec.ts +0 -41
- package/src/codec/index.ts +0 -11
- package/src/codec/json-codec.ts +0 -69
- package/src/codec/rabbitmq-codec.test.ts +0 -516
- package/src/codec/rabbitmq-codec.ts +0 -336
- package/src/core/fanout.test.ts +0 -1350
- package/src/core/fanout.ts +0 -184
- package/src/core/index.ts +0 -12
- package/src/core/matador.test.ts +0 -575
- package/src/core/matador.ts +0 -357
- package/src/core/shutdown.test.ts +0 -853
- package/src/core/shutdown.ts +0 -165
- package/src/errors/checkpoint-errors.ts +0 -62
- package/src/errors/has-description.ts +0 -25
- package/src/errors/index.ts +0 -58
- package/src/errors/matador-errors.ts +0 -477
- package/src/errors/retry-errors.test.ts +0 -175
- package/src/errors/retry-errors.ts +0 -188
- package/src/hooks/index.ts +0 -14
- package/src/hooks/safe-hooks.ts +0 -200
- package/src/hooks/types.ts +0 -226
- package/src/index.ts +0 -241
- package/src/pipeline/index.ts +0 -2
- package/src/pipeline/pipeline.test.ts +0 -1377
- package/src/pipeline/pipeline.ts +0 -393
- package/src/retry/index.ts +0 -4
- package/src/retry/policy.ts +0 -46
- package/src/retry/standard-policy.test.ts +0 -290
- package/src/retry/standard-policy.ts +0 -156
- package/src/schema/index.ts +0 -16
- package/src/schema/registry.test.ts +0 -339
- package/src/schema/registry.ts +0 -229
- package/src/schema/types.test.ts +0 -280
- package/src/schema/types.ts +0 -217
- package/src/topology/builder.test.ts +0 -451
- package/src/topology/builder.ts +0 -238
- package/src/topology/index.ts +0 -19
- package/src/topology/types.ts +0 -183
- package/src/transport/capabilities.ts +0 -88
- package/src/transport/connection-manager.ts +0 -218
- package/src/transport/index.ts +0 -42
- package/src/transport/local/local-transport.test.ts +0 -262
- package/src/transport/local/local-transport.ts +0 -330
- package/src/transport/multi/multi-transport.test.ts +0 -320
- package/src/transport/multi/multi-transport.ts +0 -294
- package/src/transport/rabbitmq/rabbitmq-transport.test.ts +0 -120
- package/src/transport/rabbitmq/rabbitmq-transport.ts +0 -782
- package/src/transport/transport.ts +0 -200
- package/src/types/common.ts +0 -53
- package/src/types/dispatcher.ts +0 -18
- package/src/types/envelope.ts +0 -244
- package/src/types/event.test.ts +0 -157
- package/src/types/event.ts +0 -112
- package/src/types/index.ts +0 -62
- package/src/types/subscriber.ts +0 -333
- package/test/e2e/multi-transport.e2e.test.ts +0 -237
- package/test/e2e/rabbitmq-transport.e2e.test.ts +0 -618
- package/test/e2e/transport-compliance.e2e.test.ts +0 -506
- package/test/integration/matador.integration.test.ts +0 -634
- package/tsconfig.json +0 -29
- package/tsconfig.tsbuildinfo +0 -1
- package/tsup.config.ts +0 -13
|
@@ -1,290 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'bun:test';
|
|
2
|
-
import {
|
|
3
|
-
DoRetry,
|
|
4
|
-
DontRetry,
|
|
5
|
-
EventAssertionError,
|
|
6
|
-
} from '../errors/retry-errors.js';
|
|
7
|
-
import type { MessageReceipt } from '../transport/transport.js';
|
|
8
|
-
import { createEnvelope } from '../types/envelope.js';
|
|
9
|
-
import type { SubscriberDefinition } from '../types/subscriber.js';
|
|
10
|
-
import type { RetryContext, RetryDecision } from './policy.js';
|
|
11
|
-
import { StandardRetryPolicy } from './standard-policy.js';
|
|
12
|
-
|
|
13
|
-
describe('StandardRetryPolicy', () => {
|
|
14
|
-
describe('shouldRetry', () => {
|
|
15
|
-
it('should dead-letter on EventAssertionError', () => {
|
|
16
|
-
const policy = new StandardRetryPolicy();
|
|
17
|
-
const envelope = createEnvelope({
|
|
18
|
-
eventKey: 'test.event',
|
|
19
|
-
targetSubscriber: 'test-subscriber',
|
|
20
|
-
data: { test: 'data' },
|
|
21
|
-
importance: 'should-investigate',
|
|
22
|
-
});
|
|
23
|
-
const context = createContext(
|
|
24
|
-
new EventAssertionError(envelope, 'Invalid event'),
|
|
25
|
-
);
|
|
26
|
-
|
|
27
|
-
const decision = policy.shouldRetry(context);
|
|
28
|
-
|
|
29
|
-
expect(decision.action).toBe('dead-letter');
|
|
30
|
-
assertDeadLetter(decision);
|
|
31
|
-
expect(decision.queue).toBe('undeliverable');
|
|
32
|
-
expect(decision.reason).toContain('assertion error');
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
it('should dead-letter on DontRetry error', () => {
|
|
36
|
-
const policy = new StandardRetryPolicy();
|
|
37
|
-
const context = createContext(new DontRetry('Business rule violation'));
|
|
38
|
-
|
|
39
|
-
const decision = policy.shouldRetry(context);
|
|
40
|
-
|
|
41
|
-
expect(decision.action).toBe('dead-letter');
|
|
42
|
-
assertDeadLetter(decision);
|
|
43
|
-
expect(decision.queue).toBe('undeliverable');
|
|
44
|
-
expect(decision.reason).toContain('Business rule violation');
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
it('should retry on DoRetry error if under max attempts', () => {
|
|
48
|
-
const policy = new StandardRetryPolicy({ maxAttempts: 3 });
|
|
49
|
-
const context = createContext(new DoRetry('Temporary failure'), {
|
|
50
|
-
attemptNumber: 1,
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
const decision = policy.shouldRetry(context);
|
|
54
|
-
|
|
55
|
-
expect(decision.action).toBe('retry');
|
|
56
|
-
assertRetry(decision);
|
|
57
|
-
expect(decision.delay).toBeGreaterThan(0);
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
it('should dead-letter on DoRetry error if max attempts exceeded', () => {
|
|
61
|
-
const policy = new StandardRetryPolicy({ maxAttempts: 3 });
|
|
62
|
-
const context = createContext(new DoRetry('Temporary failure'), {
|
|
63
|
-
attemptNumber: 3,
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
const decision = policy.shouldRetry(context);
|
|
67
|
-
|
|
68
|
-
expect(decision.action).toBe('dead-letter');
|
|
69
|
-
assertDeadLetter(decision);
|
|
70
|
-
expect(decision.reason).toContain('max attempts exceeded');
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
it('should dead-letter when max attempts exceeded', () => {
|
|
74
|
-
const policy = new StandardRetryPolicy({ maxAttempts: 3 });
|
|
75
|
-
const context = createContext(new Error('Generic error'), {
|
|
76
|
-
attemptNumber: 3,
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
const decision = policy.shouldRetry(context);
|
|
80
|
-
|
|
81
|
-
expect(decision.action).toBe('dead-letter');
|
|
82
|
-
assertDeadLetter(decision);
|
|
83
|
-
expect(decision.queue).toBe('undeliverable');
|
|
84
|
-
expect(decision.reason).toContain('max attempts exceeded (3)');
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
it('should dead-letter non-idempotent subscriber on redelivery', () => {
|
|
88
|
-
const policy = new StandardRetryPolicy();
|
|
89
|
-
const context = createContext(
|
|
90
|
-
new Error('Some error'),
|
|
91
|
-
{ attemptNumber: 1, redelivered: true },
|
|
92
|
-
{ idempotent: 'no' },
|
|
93
|
-
);
|
|
94
|
-
|
|
95
|
-
const decision = policy.shouldRetry(context);
|
|
96
|
-
|
|
97
|
-
expect(decision.action).toBe('dead-letter');
|
|
98
|
-
assertDeadLetter(decision);
|
|
99
|
-
expect(decision.reason).toContain('Non-idempotent subscriber');
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
it('should retry idempotent subscriber on redelivery', () => {
|
|
103
|
-
const policy = new StandardRetryPolicy();
|
|
104
|
-
const context = createContext(
|
|
105
|
-
new Error('Some error'),
|
|
106
|
-
{ attemptNumber: 1, redelivered: true },
|
|
107
|
-
{ idempotent: 'yes' },
|
|
108
|
-
);
|
|
109
|
-
|
|
110
|
-
const decision = policy.shouldRetry(context);
|
|
111
|
-
|
|
112
|
-
expect(decision.action).toBe('retry');
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
it('should dead-letter unknown idempotency subscriber on redelivery (unknown == no)', () => {
|
|
116
|
-
const policy = new StandardRetryPolicy();
|
|
117
|
-
const context = createContext(
|
|
118
|
-
new Error('Some error'),
|
|
119
|
-
{ attemptNumber: 1, redelivered: true },
|
|
120
|
-
{ idempotent: 'unknown' },
|
|
121
|
-
);
|
|
122
|
-
|
|
123
|
-
const decision = policy.shouldRetry(context);
|
|
124
|
-
|
|
125
|
-
expect(decision.action).toBe('dead-letter');
|
|
126
|
-
assertDeadLetter(decision);
|
|
127
|
-
expect(decision.reason).toContain('Non-idempotent subscriber');
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
it('should retry resumable subscriber on redelivery', () => {
|
|
131
|
-
const policy = new StandardRetryPolicy();
|
|
132
|
-
const context = createContext(
|
|
133
|
-
new Error('Some error'),
|
|
134
|
-
{ attemptNumber: 1, redelivered: true },
|
|
135
|
-
{ idempotent: 'resumable' },
|
|
136
|
-
);
|
|
137
|
-
|
|
138
|
-
const decision = policy.shouldRetry(context);
|
|
139
|
-
|
|
140
|
-
expect(decision.action).toBe('retry');
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
it('should retry generic errors with backoff', () => {
|
|
144
|
-
const policy = new StandardRetryPolicy({ maxAttempts: 5 });
|
|
145
|
-
const context = createContext(new Error('Generic error'), {
|
|
146
|
-
attemptNumber: 1,
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
const decision = policy.shouldRetry(context);
|
|
150
|
-
|
|
151
|
-
expect(decision.action).toBe('retry');
|
|
152
|
-
assertRetry(decision);
|
|
153
|
-
expect(decision.delay).toBeDefined();
|
|
154
|
-
});
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
describe('getDelay', () => {
|
|
158
|
-
it('should calculate exponential backoff', () => {
|
|
159
|
-
const policy = new StandardRetryPolicy({
|
|
160
|
-
baseDelay: 1000,
|
|
161
|
-
backoffMultiplier: 2,
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
const delay1 = policy.getDelay(
|
|
165
|
-
createContext(new Error(), { attemptNumber: 1 }),
|
|
166
|
-
);
|
|
167
|
-
const delay2 = policy.getDelay(
|
|
168
|
-
createContext(new Error(), { attemptNumber: 2 }),
|
|
169
|
-
);
|
|
170
|
-
const delay3 = policy.getDelay(
|
|
171
|
-
createContext(new Error(), { attemptNumber: 3 }),
|
|
172
|
-
);
|
|
173
|
-
|
|
174
|
-
expect(delay1).toBe(1000); // 1000 * 2^0
|
|
175
|
-
expect(delay2).toBe(2000); // 1000 * 2^1
|
|
176
|
-
expect(delay3).toBe(4000); // 1000 * 2^2
|
|
177
|
-
});
|
|
178
|
-
|
|
179
|
-
it('should cap delay at maxDelay', () => {
|
|
180
|
-
const policy = new StandardRetryPolicy({
|
|
181
|
-
baseDelay: 1000,
|
|
182
|
-
backoffMultiplier: 10,
|
|
183
|
-
maxDelay: 5000,
|
|
184
|
-
});
|
|
185
|
-
|
|
186
|
-
const delay = policy.getDelay(
|
|
187
|
-
createContext(new Error(), { attemptNumber: 5 }),
|
|
188
|
-
);
|
|
189
|
-
|
|
190
|
-
expect(delay).toBe(5000);
|
|
191
|
-
});
|
|
192
|
-
});
|
|
193
|
-
|
|
194
|
-
describe('configuration', () => {
|
|
195
|
-
it('should use default configuration', () => {
|
|
196
|
-
const policy = new StandardRetryPolicy();
|
|
197
|
-
const context = createContext(new Error(), { attemptNumber: 1 });
|
|
198
|
-
|
|
199
|
-
const decision = policy.shouldRetry(context);
|
|
200
|
-
|
|
201
|
-
// Default maxAttempts is 3, so should retry on attempt 1
|
|
202
|
-
expect(decision.action).toBe('retry');
|
|
203
|
-
});
|
|
204
|
-
|
|
205
|
-
it('should accept custom configuration', () => {
|
|
206
|
-
const policy = new StandardRetryPolicy({
|
|
207
|
-
maxAttempts: 1,
|
|
208
|
-
baseDelay: 500,
|
|
209
|
-
maxDelay: 1000,
|
|
210
|
-
backoffMultiplier: 1.5,
|
|
211
|
-
});
|
|
212
|
-
|
|
213
|
-
// With maxAttempts: 1, first attempt should dead-letter
|
|
214
|
-
const context = createContext(new Error(), { attemptNumber: 1 });
|
|
215
|
-
const decision = policy.shouldRetry(context);
|
|
216
|
-
|
|
217
|
-
expect(decision.action).toBe('dead-letter');
|
|
218
|
-
});
|
|
219
|
-
|
|
220
|
-
it('should merge partial configuration with defaults', () => {
|
|
221
|
-
const policy = new StandardRetryPolicy({ maxAttempts: 10 });
|
|
222
|
-
// Use attemptNumber: 5 but deliveryCount: 3 to avoid poison detection
|
|
223
|
-
const context = createContext(new Error(), {
|
|
224
|
-
attemptNumber: 5,
|
|
225
|
-
deliveryCount: 3,
|
|
226
|
-
});
|
|
227
|
-
|
|
228
|
-
// Should still retry because we increased maxAttempts
|
|
229
|
-
const decision = policy.shouldRetry(context);
|
|
230
|
-
expect(decision.action).toBe('retry');
|
|
231
|
-
});
|
|
232
|
-
});
|
|
233
|
-
});
|
|
234
|
-
|
|
235
|
-
function assertDeadLetter(decision: RetryDecision): asserts decision is {
|
|
236
|
-
action: 'dead-letter';
|
|
237
|
-
queue: string;
|
|
238
|
-
reason: string;
|
|
239
|
-
} {
|
|
240
|
-
if (decision.action !== 'dead-letter') {
|
|
241
|
-
throw new Error(`Expected dead-letter action, got ${decision.action}`);
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
function assertRetry(
|
|
246
|
-
decision: RetryDecision,
|
|
247
|
-
): asserts decision is { action: 'retry'; delay: number } {
|
|
248
|
-
if (decision.action !== 'retry') {
|
|
249
|
-
throw new Error(`Expected retry action, got ${decision.action}`);
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
function createContext(
|
|
254
|
-
error: Error,
|
|
255
|
-
receiptOverrides: Partial<MessageReceipt> = {},
|
|
256
|
-
subscriberOverrides: Partial<SubscriberDefinition> = {},
|
|
257
|
-
): RetryContext {
|
|
258
|
-
const receipt: MessageReceipt = {
|
|
259
|
-
handle: {},
|
|
260
|
-
redelivered: false,
|
|
261
|
-
attemptNumber: 1,
|
|
262
|
-
deliveryCount:
|
|
263
|
-
receiptOverrides.deliveryCount ?? receiptOverrides.attemptNumber ?? 1,
|
|
264
|
-
sourceQueue: 'test-queue',
|
|
265
|
-
sourceTransport: 'mock',
|
|
266
|
-
...receiptOverrides,
|
|
267
|
-
};
|
|
268
|
-
|
|
269
|
-
const subscriber: SubscriberDefinition = {
|
|
270
|
-
name: 'test-subscriber',
|
|
271
|
-
description: 'Test subscriber',
|
|
272
|
-
idempotent: 'yes',
|
|
273
|
-
importance: 'should-investigate',
|
|
274
|
-
...subscriberOverrides,
|
|
275
|
-
};
|
|
276
|
-
|
|
277
|
-
const envelope = createEnvelope({
|
|
278
|
-
eventKey: 'test.event',
|
|
279
|
-
targetSubscriber: 'test-subscriber',
|
|
280
|
-
data: { test: 'data' },
|
|
281
|
-
importance: 'should-investigate',
|
|
282
|
-
});
|
|
283
|
-
|
|
284
|
-
return {
|
|
285
|
-
envelope,
|
|
286
|
-
error,
|
|
287
|
-
receipt,
|
|
288
|
-
subscriber,
|
|
289
|
-
};
|
|
290
|
-
}
|
|
@@ -1,156 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
IdempotentMessageCannotRetryError,
|
|
3
|
-
MessageMaybePoisonedError,
|
|
4
|
-
isAssertionError,
|
|
5
|
-
isDoRetry,
|
|
6
|
-
isDontRetry,
|
|
7
|
-
} from '../errors/index.js';
|
|
8
|
-
import type { RetryContext, RetryDecision, RetryPolicy } from './policy.js';
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Configuration for the standard retry policy.
|
|
12
|
-
*/
|
|
13
|
-
export interface StandardRetryPolicyConfig {
|
|
14
|
-
/** Maximum number of attempts before dead-lettering */
|
|
15
|
-
readonly maxAttempts: number;
|
|
16
|
-
|
|
17
|
-
/** Base delay between retries in milliseconds */
|
|
18
|
-
readonly baseDelay: number;
|
|
19
|
-
|
|
20
|
-
/** Maximum delay between retries in milliseconds */
|
|
21
|
-
readonly maxDelay: number;
|
|
22
|
-
|
|
23
|
-
/** Multiplier for exponential backoff */
|
|
24
|
-
readonly backoffMultiplier: number;
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Maximum native delivery count before considering message poisoned.
|
|
28
|
-
* This prevents crash loops from messages that crash the worker.
|
|
29
|
-
* Poison messages are sent directly to the dead-letter queue.
|
|
30
|
-
* Default: 5
|
|
31
|
-
*/
|
|
32
|
-
readonly maxDeliveries: number;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Default configuration values.
|
|
37
|
-
*/
|
|
38
|
-
export const defaultRetryConfig: StandardRetryPolicyConfig = {
|
|
39
|
-
maxAttempts: 3,
|
|
40
|
-
baseDelay: 1000,
|
|
41
|
-
maxDelay: 300000, // 5 minutes
|
|
42
|
-
backoffMultiplier: 2,
|
|
43
|
-
maxDeliveries: 5,
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Standard retry policy implementing Matador v1 behavior.
|
|
48
|
-
*
|
|
49
|
-
* Decision logic (in priority order):
|
|
50
|
-
* 1. Poison message → dead-letter (prevent crash loops)
|
|
51
|
-
* 2. EventAssertionError → dead-letter (never retry)
|
|
52
|
-
* 3. DontRetry → dead-letter (explicit no-retry)
|
|
53
|
-
* 4. DoRetry → retry if under max attempts
|
|
54
|
-
* 5. Max attempts exceeded → dead-letter
|
|
55
|
-
* 6. Non-idempotent subscriber on redelivery → dead-letter
|
|
56
|
-
* 7. Default → retry with exponential backoff
|
|
57
|
-
*/
|
|
58
|
-
export class StandardRetryPolicy implements RetryPolicy {
|
|
59
|
-
private readonly config: StandardRetryPolicyConfig;
|
|
60
|
-
|
|
61
|
-
constructor(config: Partial<StandardRetryPolicyConfig> = {}) {
|
|
62
|
-
this.config = { ...defaultRetryConfig, ...config };
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
shouldRetry(context: RetryContext): RetryDecision {
|
|
66
|
-
const { envelope, error, subscriber, receipt } = context;
|
|
67
|
-
const errorMessage = error.message;
|
|
68
|
-
|
|
69
|
-
// 1. Poison message detection - prevent crash loops
|
|
70
|
-
if (receipt.deliveryCount >= this.config.maxDeliveries) {
|
|
71
|
-
const poisonError = new MessageMaybePoisonedError(
|
|
72
|
-
envelope.id,
|
|
73
|
-
receipt.deliveryCount,
|
|
74
|
-
this.config.maxDeliveries,
|
|
75
|
-
);
|
|
76
|
-
return {
|
|
77
|
-
action: 'dead-letter',
|
|
78
|
-
queue: 'undeliverable',
|
|
79
|
-
reason: poisonError.message,
|
|
80
|
-
};
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// 2. Assertion errors never retry
|
|
84
|
-
if (isAssertionError(error)) {
|
|
85
|
-
return {
|
|
86
|
-
action: 'dead-letter',
|
|
87
|
-
queue: 'undeliverable',
|
|
88
|
-
reason: `assertion error: ${errorMessage}`,
|
|
89
|
-
};
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// 3. Explicit no-retry
|
|
93
|
-
if (isDontRetry(error)) {
|
|
94
|
-
return {
|
|
95
|
-
action: 'dead-letter',
|
|
96
|
-
queue: 'undeliverable',
|
|
97
|
-
reason: errorMessage,
|
|
98
|
-
};
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
// 4. Explicit retry request
|
|
102
|
-
if (isDoRetry(error)) {
|
|
103
|
-
if (receipt.attemptNumber >= this.config.maxAttempts) {
|
|
104
|
-
return {
|
|
105
|
-
action: 'dead-letter',
|
|
106
|
-
queue: 'undeliverable',
|
|
107
|
-
reason: `max attempts exceeded (${this.config.maxAttempts}) with forced retry`,
|
|
108
|
-
};
|
|
109
|
-
}
|
|
110
|
-
return {
|
|
111
|
-
action: 'retry',
|
|
112
|
-
delay: this.getDelay(context),
|
|
113
|
-
};
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
// 5. Max attempts exceeded
|
|
117
|
-
if (receipt.attemptNumber >= this.config.maxAttempts) {
|
|
118
|
-
return {
|
|
119
|
-
action: 'dead-letter',
|
|
120
|
-
queue: 'undeliverable',
|
|
121
|
-
reason: `max attempts exceeded (${this.config.maxAttempts})`,
|
|
122
|
-
};
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
// 6. Non-idempotent subscriber on redelivery
|
|
126
|
-
// 'no' and 'unknown' are treated the same (safer default)
|
|
127
|
-
if (
|
|
128
|
-
receipt.redelivered &&
|
|
129
|
-
(subscriber.idempotent === 'no' || subscriber.idempotent === 'unknown')
|
|
130
|
-
) {
|
|
131
|
-
const idempotentError = new IdempotentMessageCannotRetryError(
|
|
132
|
-
envelope.id,
|
|
133
|
-
subscriber.name,
|
|
134
|
-
);
|
|
135
|
-
return {
|
|
136
|
-
action: 'dead-letter',
|
|
137
|
-
queue: 'undeliverable',
|
|
138
|
-
reason: idempotentError.message,
|
|
139
|
-
};
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
// 7. Default: retry with backoff
|
|
143
|
-
return {
|
|
144
|
-
action: 'retry',
|
|
145
|
-
delay: this.getDelay(context),
|
|
146
|
-
};
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
getDelay(context: RetryContext): number {
|
|
150
|
-
const attempt = context.receipt.attemptNumber;
|
|
151
|
-
const delay =
|
|
152
|
-
this.config.baseDelay *
|
|
153
|
-
Math.pow(this.config.backoffMultiplier, attempt - 1);
|
|
154
|
-
return Math.min(delay, this.config.maxDelay);
|
|
155
|
-
}
|
|
156
|
-
}
|
package/src/schema/index.ts
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
export type {
|
|
2
|
-
AnyEventClass,
|
|
3
|
-
MatadorSchema,
|
|
4
|
-
RegisterOptions,
|
|
5
|
-
RuntimeSchemaEntry,
|
|
6
|
-
RuntimeSchemaEntryTuple,
|
|
7
|
-
SchemaEntry,
|
|
8
|
-
SchemaEntryTuple,
|
|
9
|
-
SchemaIssue,
|
|
10
|
-
SchemaPlugin,
|
|
11
|
-
SchemaValidationResult,
|
|
12
|
-
} from './types.js';
|
|
13
|
-
|
|
14
|
-
export { bind, installPlugins, isSchemaEntryTuple } from './types.js';
|
|
15
|
-
|
|
16
|
-
export { SchemaError, SchemaRegistry } from './registry.js';
|