@amqp-contract/worker 0.9.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +53 -34
- package/dist/index.cjs +205 -245
- package/dist/index.d.cts +136 -271
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +136 -271
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +205 -243
- package/dist/index.mjs.map +1 -1
- package/docs/index.md +123 -685
- package/package.json +8 -8
package/README.md
CHANGED
|
@@ -30,8 +30,9 @@ pnpm add @amqp-contract/worker
|
|
|
30
30
|
### Basic Usage
|
|
31
31
|
|
|
32
32
|
```typescript
|
|
33
|
-
import { TypedAmqpWorker } from "@amqp-contract/worker";
|
|
33
|
+
import { TypedAmqpWorker, RetryableError } from "@amqp-contract/worker";
|
|
34
34
|
import type { Logger } from "@amqp-contract/core";
|
|
35
|
+
import { Future } from "@swan-io/boxed";
|
|
35
36
|
import { contract } from "./contract";
|
|
36
37
|
|
|
37
38
|
// Optional: Create a logger implementation
|
|
@@ -46,14 +47,13 @@ const logger: Logger = {
|
|
|
46
47
|
const worker = await TypedAmqpWorker.create({
|
|
47
48
|
contract,
|
|
48
49
|
handlers: {
|
|
49
|
-
processOrder:
|
|
50
|
-
console.log("Processing order:",
|
|
50
|
+
processOrder: ({ payload }) => {
|
|
51
|
+
console.log("Processing order:", payload.orderId);
|
|
51
52
|
|
|
52
53
|
// Your business logic here
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
// If an exception is thrown, the message is automatically requeued
|
|
54
|
+
return Future.fromPromise(Promise.all([processPayment(payload), updateInventory(payload)]))
|
|
55
|
+
.mapOk(() => undefined)
|
|
56
|
+
.mapError((error) => new RetryableError("Order processing failed", error));
|
|
57
57
|
},
|
|
58
58
|
},
|
|
59
59
|
urls: ["amqp://localhost"],
|
|
@@ -72,25 +72,43 @@ For advanced features like prefetch configuration, batch processing, and **autom
|
|
|
72
72
|
|
|
73
73
|
#### Retry with Exponential Backoff
|
|
74
74
|
|
|
75
|
-
|
|
75
|
+
Retry is configured at the queue level in your contract definition. Add `retry` to your queue definition:
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
import { defineQueue, defineExchange, defineContract } from "@amqp-contract/contract";
|
|
79
|
+
|
|
80
|
+
const dlx = defineExchange("orders-dlx", "topic", { durable: true });
|
|
81
|
+
|
|
82
|
+
// Configure retry at queue level
|
|
83
|
+
const orderQueue = defineQueue("order-processing", {
|
|
84
|
+
deadLetter: { exchange: dlx },
|
|
85
|
+
retry: {
|
|
86
|
+
mode: "ttl-backoff",
|
|
87
|
+
maxRetries: 3, // Retry up to 3 times (default: 3)
|
|
88
|
+
initialDelayMs: 1000, // Start with 1 second delay (default: 1000)
|
|
89
|
+
maxDelayMs: 30000, // Max 30 seconds between retries (default: 30000)
|
|
90
|
+
backoffMultiplier: 2, // Double the delay each time (default: 2)
|
|
91
|
+
jitter: true, // Add randomness to prevent thundering herd (default: true)
|
|
92
|
+
},
|
|
93
|
+
});
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Then use `RetryableError` in your handlers:
|
|
76
97
|
|
|
77
98
|
```typescript
|
|
99
|
+
import { TypedAmqpWorker, RetryableError } from "@amqp-contract/worker";
|
|
100
|
+
import { Future } from "@swan-io/boxed";
|
|
101
|
+
|
|
78
102
|
const worker = await TypedAmqpWorker.create({
|
|
79
103
|
contract,
|
|
80
104
|
handlers: {
|
|
81
|
-
processOrder:
|
|
82
|
-
// If this
|
|
83
|
-
|
|
84
|
-
|
|
105
|
+
processOrder: ({ payload }) =>
|
|
106
|
+
// If this fails with RetryableError, message is automatically retried
|
|
107
|
+
Future.fromPromise(processPayment(payload))
|
|
108
|
+
.mapOk(() => undefined)
|
|
109
|
+
.mapError((error) => new RetryableError("Payment failed", error)),
|
|
85
110
|
},
|
|
86
111
|
urls: ["amqp://localhost"],
|
|
87
|
-
retry: {
|
|
88
|
-
maxRetries: 3, // Retry up to 3 times
|
|
89
|
-
initialDelayMs: 1000, // Start with 1 second delay
|
|
90
|
-
maxDelayMs: 30000, // Max 30 seconds between retries
|
|
91
|
-
backoffMultiplier: 2, // Double the delay each time
|
|
92
|
-
jitter: true, // Add randomness to prevent thundering herd
|
|
93
|
-
},
|
|
94
112
|
});
|
|
95
113
|
```
|
|
96
114
|
|
|
@@ -102,22 +120,24 @@ You can define handlers outside of the worker creation using `defineHandler` and
|
|
|
102
120
|
|
|
103
121
|
## Error Handling
|
|
104
122
|
|
|
105
|
-
Worker handlers
|
|
123
|
+
Worker handlers return `Future<Result<void, HandlerError>>` for explicit error handling:
|
|
106
124
|
|
|
107
125
|
```typescript
|
|
126
|
+
import { RetryableError, NonRetryableError } from "@amqp-contract/worker";
|
|
127
|
+
import { Future, Result } from "@swan-io/boxed";
|
|
128
|
+
|
|
108
129
|
handlers: {
|
|
109
|
-
processOrder:
|
|
110
|
-
//
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
// Message acknowledged automatically on success
|
|
114
|
-
} catch (error) {
|
|
115
|
-
// Exception automatically caught by worker
|
|
116
|
-
// With retry configured: message is retried with exponential backoff
|
|
117
|
-
// Without retry: message is immediately requeued
|
|
118
|
-
throw error;
|
|
130
|
+
processOrder: ({ payload }) => {
|
|
131
|
+
// Validation errors - non-retryable
|
|
132
|
+
if (payload.amount <= 0) {
|
|
133
|
+
return Future.value(Result.Error(new NonRetryableError("Invalid amount")));
|
|
119
134
|
}
|
|
120
|
-
|
|
135
|
+
|
|
136
|
+
// Transient errors - retryable
|
|
137
|
+
return Future.fromPromise(process(payload))
|
|
138
|
+
.mapOk(() => undefined)
|
|
139
|
+
.mapError((error) => new RetryableError("Processing failed", error));
|
|
140
|
+
},
|
|
121
141
|
}
|
|
122
142
|
```
|
|
123
143
|
|
|
@@ -127,9 +147,8 @@ Worker defines error classes:
|
|
|
127
147
|
|
|
128
148
|
- `TechnicalError` - Runtime failures (parsing, processing)
|
|
129
149
|
- `MessageValidationError` - Message fails schema validation
|
|
130
|
-
- `RetryableError` -
|
|
131
|
-
|
|
132
|
-
**Handlers don't need to use these error classes** - just throw standard exceptions. The worker handles retry automatically based on your configuration.
|
|
150
|
+
- `RetryableError` - Signals that the error is transient and should be retried
|
|
151
|
+
- `NonRetryableError` - Signals permanent failure, message goes to DLQ
|
|
133
152
|
|
|
134
153
|
## API
|
|
135
154
|
|