You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Control how steps behave when interrupted mid-execution:
140
+
141
+
| Semantic | Behavior | Use Case |
142
+
|----------|----------|----------|
143
+
|`AT_LEAST_ONCE_PER_RETRY` (default) | Re-executes step if interrupted before completion | Idempotent operations (database upserts, API calls with idempotency keys) |
144
+
|`AT_MOST_ONCE_PER_RETRY`| Never re-executes; throws `StepInterruptedException` if interrupted | Non-idempotent operations (sending emails, charging payments) |
145
+
146
+
```java
147
+
// Default: at-least-once (step may re-run if interrupted)
148
+
var result = ctx.step("idempotent-update", Result.class,
149
+
() -> database.upsert(record));
150
+
151
+
// At-most-once: step will not re-run if interrupted
152
+
var result = ctx.step("send-email", Result.class,
153
+
() -> emailService.send(notification),
154
+
StepConfig.builder()
155
+
.semantics(StepSemantics.AT_MOST_ONCE_PER_RETRY)
156
+
.build());
157
+
```
158
+
159
+
With `AT_MOST_ONCE_PER_RETRY`, if a step starts but the function is interrupted before the result is checkpointed, the SDK throws `StepInterruptedException` on replay instead of re-executing. Handle this to implement your own recovery logic.
160
+
106
161
### Generic Types
107
162
108
163
For complex generic types like `List<User>`, use `TypeToken`:
@@ -112,9 +167,65 @@ var users = ctx.step("fetch-users", new TypeToken<List<User>>() {},
112
167
() -> userService.getAllUsers());
113
168
```
114
169
170
+
## Configuration
171
+
172
+
Customize SDK behavior by overriding `createConfiguration()` in your handler:
.withExecutorService(Executors.newFixedThreadPool(10)) // Custom thread pool
189
+
.build();
190
+
}
191
+
```
192
+
193
+
| Option | Description | Default |
194
+
|--------|-------------|---------|
195
+
|`withDurableExecutionClient()`| Client for checkpoint operations | Auto-configured Lambda client |
196
+
|`withSerDes()`| Serializer for step results | Jackson with default settings |
197
+
|`withExecutorService()`| Thread pool for async step execution | Cached daemon thread pool |
198
+
199
+
## Error Handling
200
+
201
+
The SDK throws specific exceptions to help you handle different failure scenarios:
202
+
203
+
| Exception | When Thrown | How to Handle |
204
+
|-----------|-------------|---------------|
205
+
|`StepFailedException`| Step exhausted all retry attempts | Catch to implement fallback logic or let execution fail |
206
+
|`StepInterruptedException`|`AT_MOST_ONCE` step was interrupted before completion | Implement manual recovery (check if operation completed externally) |
207
+
|`NonDeterministicExecutionException`| Code changed between original execution and replay | Fix code to maintain determinism; don't change step order/names |
208
+
209
+
```java
210
+
try {
211
+
var result = ctx.step("charge-payment", Payment.class,
212
+
() -> paymentService.charge(amount),
213
+
StepConfig.builder()
214
+
.semantics(StepSemantics.AT_MOST_ONCE_PER_RETRY)
215
+
.build());
216
+
} catch (StepInterruptedException e) {
217
+
// Step started but we don't know if it completed
218
+
// Check payment status externally before retrying
219
+
var status = paymentService.checkStatus(transactionId);
220
+
if (status.isPending()) {
221
+
throw e; // Let it fail - manual intervention needed
222
+
}
223
+
}
224
+
```
225
+
115
226
## Testing
116
227
117
-
The SDK includes testing utilities for fast, local development without deploying to AWS.
228
+
The SDK includes testing utilities for both local development and cloud-based integration testing.
118
229
119
230
### Installation
120
231
@@ -133,7 +244,7 @@ The SDK includes testing utilities for fast, local development without deploying
133
244
@Test
134
245
void testOrderProcessing() {
135
246
var handler =newOrderProcessor();
136
-
var runner =LocalDurableTestRunner.create(Order.class, handler::handleRequest);
247
+
var runner =LocalDurableTestRunner.create(Order.class, handler);
137
248
138
249
var result = runner.runUntilComplete(newOrder("order-123", items));
|[GenericTypesExample](./examples/src/main/java/com/amazonaws/lambda/durable/examples/GenericTypesExample.java)| Working with generic types |
330
+
|[CustomConfigExample](./examples/src/main/java/com/amazonaws/lambda/durable/examples/CustomConfigExample.java)| Custom Lambda client and SerDes |
331
+
|[WaitAtLeastExample](./examples/src/main/java/com/amazonaws/lambda/durable/examples/WaitAtLeastExample.java)| Concurrent stepAsync() with wait() |
332
+
|[RetryInProcessExample](./examples/src/main/java/com/amazonaws/lambda/durable/examples/RetryInProcessExample.java)| In-process retry with concurrent operations |
333
+
|[WaitAtLeastInProcessExample](./examples/src/main/java/com/amazonaws/lambda/durable/examples/WaitAtLeastInProcessExample.java)| Wait completes before async step (no suspension) |
0 commit comments