Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ Before using AWS Lambda durable functions, verify:
2. **Runtime environment** is ready:
- For TypeScript/JavaScript: Node.js 22+ (`node --version`)
- For Python: Python 3.11+ (`python --version`. Note that currently only Lambda runtime environments 3.13+ come with the Durable Execution SDK pre-installed. 3.11 is the min supported Python version by the Durable SDK itself, however, you could use OCI to bring your own container image with your own Python runtime + Durable SDK.)
- For Java: Java 17+ (`java --version`)

3. **Deployment capability** exists (one of):
- AWS SAM CLI (`sam --version`) 1.153.1 or higher
Expand All @@ -40,6 +41,7 @@ Override syntax:

- "use Python" → Generate Python code
- "use JavaScript" → Generate JavaScript code
- "use Java" → Generate Java code

When not specified, ALWAYS use TypeScript

Expand Down Expand Up @@ -90,6 +92,24 @@ pip install aws-durable-execution-sdk-python
pip install aws-durable-execution-sdk-python-testing
```

**For Java (Maven):**

Add the dependencies to your `pom.xml`, pinning to a specific released `VERSION` (see [Maven Central](https://mvnrepository.com/artifact/software.amazon.lambda.durable/aws-durable-execution-sdk-java)):

```xml
<dependency>
<groupId>software.amazon.lambda.durable</groupId>
<artifactId>aws-durable-execution-sdk-java</artifactId>
<version>VERSION</version>
</dependency>
<dependency>
<groupId>software.amazon.lambda.durable</groupId>
<artifactId>aws-durable-execution-sdk-java-testing</artifactId>
<version>VERSION</version>
<scope>test</scope>
</dependency>
```

## When to Load Reference Files

Load the appropriate reference file based on what the user is working on:
Expand Down Expand Up @@ -132,6 +152,21 @@ def handler(event: dict, context: DurableContext) -> dict:
return result
```

**Java:**

```java
import software.amazon.lambda.durable.DurableHandler;
import software.amazon.lambda.durable.DurableContext;

public class MyHandler extends DurableHandler<MyInput, MyOutput> {
@Override
public MyOutput handleRequest(MyInput event, DurableContext ctx) {
var result = ctx.step("process", MyOutput.class, stepCtx -> processData(event));
return result;
}
}
```

### Critical Rules

1. **All non-deterministic code MUST be in steps** (Date.now, Math.random, API calls)
Expand All @@ -148,6 +183,19 @@ The Python SDK differs from TypeScript in several key areas:
- **Exceptions**: `ExecutionError` (permanent), `InvocationError` (transient), `CallbackError` (callback failures)
- **Testing**: Use `DurableFunctionTestRunner` class directly - instantiate with handler, use context manager, call `run(input=...)`

### Java API Differences

The Java SDK uses a class-based, type-safe approach:

- **Handler**: Extend `DurableHandler<TInput, TOutput>` and implement `handleRequest(TInput, DurableContext)`
- **Steps**: `ctx.step("name", ResultType.class, stepCtx -> operation())` — a name and result type are always required
- **Generic types**: Use `TypeToken` for generics like `List<T>`: `ctx.step("name", new TypeToken<List<User>>() {}, stepCtx -> ...)`
- **Wait**: `ctx.wait("name", Duration.ofSeconds(n))`
- **Async operations**: `stepAsync()`, `invokeAsync()`, `waitAsync()`, `mapAsync()` return `DurableFuture<T>`; combine with `DurableFuture.allOf(...)` / `DurableFuture.anyOf(...)`
- **Parallel**: `ctx.parallel("name", config)` returns a `ParallelDurableFuture` (AutoCloseable); register branches with `parallel.branch(...)`
- **Exceptions**: `StepFailedException`, `StepInterruptedException`, `CallbackTimeoutException`, `CallbackFailedException`, `WaitForConditionFailedException`, `InvokeFailedException`, `InvokeTimedOutException`, `UnrecoverableDurableExecutionException`, with `DurableExecutionException` as the base class
- **Testing**: Use `LocalDurableTestRunner` (local) or `CloudDurableTestRunner` (cloud) from the testing SDK

### Invocation Requirements

Durable functions **require qualified ARNs** (version, alias, or `$LATEST`):
Expand Down Expand Up @@ -201,4 +249,5 @@ Access to sensitive data (like Lambda and API Gateway logs) is **not** enabled b
- [AWS Lambda durable functions Documentation](https://docs.aws.amazon.com/lambda/latest/dg/durable-functions.html)
- [JavaScript SDK Repository](https://github.com/aws/aws-durable-execution-sdk-js)
- [Python SDK Repository](https://github.com/aws/aws-durable-execution-sdk-python)
- [Java SDK Repository](https://github.com/aws/aws-durable-execution-sdk-java)
- [IAM Policy Reference](https://docs.aws.amazon.com/aws-managed-policy/latest/reference/AWSLambdaBasicDurableExecutionRolePolicy.html)
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Advanced error handling patterns for durable functions, including timeout handli

**Implementation approach:**

1. Use `waitForCallback` (TypeScript) or `wait_for_callback` (Python) with a timeout configuration set in the config argument
1. Use `waitForCallback` (TypeScript), `wait_for_callback` (Python), or `ctx.waitForCallback` (Java) with a timeout configuration set in the config argument
2. Wrap in try-catch to handle timeout errors
3. Check if the error is a timeout
4. Implement fallback logic in a step (e.g., escalate to manager, use default value, retry with different parameters)
Expand All @@ -34,6 +34,30 @@ Advanced error handling patterns for durable functions, including timeout handli
**Important limitation:**
In TypeScript, native setTimeout (and patterns like Promise.race using it) will fail during execution replays. To create a reliable timeout that persists across execution (expands over multi invocations), always use the timeout parameter provided by waitForCallback or waitForCondition

**Java equivalent — `DurableFuture.anyOf`:**
Java has no `Promise.race`, but `DurableFuture.anyOf(...)` waits for the first of several async durable operations to complete, returning that operation's result. For reliable cross-invocation timeouts that persist across replays, always use the timeout configuration in `CallbackConfig` (via `WaitForCallbackConfig`) or `WaitForConditionConfig` rather than any in-process timer.

```java
import software.amazon.lambda.durable.DurableFuture;

// Race multiple async operations - first to complete wins
var primary = ctx.stepAsync("primary-api", Result.class, s -> callPrimaryAPI());
var backup = ctx.stepAsync("backup-api", Result.class, s -> callBackupAPI());

// Block until the first one completes
DurableFuture.anyOf(primary, backup);

// Read whichever finished first
Result result;
try {
result = primary.get();
ctx.getLogger().info("Primary API completed first");
} catch (Exception e) {
result = backup.get();
ctx.getLogger().info("Backup API completed first");
}
```

## Conditional Retry Based on Error Type

**Pattern:** Retry operations selectively based on the type of error encountered.
Expand Down Expand Up @@ -113,3 +137,36 @@ In TypeScript, native setTimeout (and patterns like Promise.race using it) will
- Callback timeouts - external system didn't respond in time
- External system delays - service is slow or unresponsive
- Long-running operations - operation exceeded expected duration

## Java Exception Types

The Java SDK surfaces typed exceptions you can catch to drive fallback logic:

| Exception | Category | Use case |
| --------------------------------- | ----------------- | ------------------------------------------------------- |
| `CallbackTimeoutException` | Timeout | Callback didn't complete within its timeout |
| `CallbackFailedException` | Callback failure | Callback failed or was explicitly rejected |
| `WaitForConditionFailedException` | Condition failure | Condition check failed or max polling attempts exceeded |
| `StepFailedException` | Permanent | Step failed after exhausting retries (business error) |
| `StepInterruptedException` | Transient | Step interrupted before completion (can retry) |
| `DurableExecutionException` | Base | Base class for all SDK exceptions |

```java
import software.amazon.lambda.durable.config.WaitForCallbackConfig;
import software.amazon.lambda.durable.config.CallbackConfig;
import software.amazon.lambda.durable.exception.CallbackTimeoutException;
import software.amazon.lambda.durable.exception.CallbackFailedException;

try {
var decision = ctx.waitForCallback("wait-approval", Decision.class,
(callbackId, s) -> sendApproval(callbackId),
WaitForCallbackConfig.builder()
.callbackConfig(CallbackConfig.builder().timeout(Duration.ofHours(24)).build())
.build());
} catch (CallbackTimeoutException e) {
// Fallback in a step so it is checkpointed
ctx.step("escalate", Void.class, s -> { escalateToManager(); return null; });
} catch (CallbackFailedException e) {
ctx.getLogger().error("Callback failed: {}", e.getMessage());
}
```
Loading
Loading