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
1 change: 1 addition & 0 deletions .github/scripts/maven_publish.sh
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ echo "settings.xml written."
echo "=== Step 3: Upload to Sonatype Central Portal ==="

mvn clean deploy -s "${SETTINGS_FILE}" -pl sdk -P publishing -DskipTests --no-transfer-progress
mvn clean deploy -s "${SETTINGS_FILE}" -pl extra-serdes -P publishing -DskipTests --no-transfer-progress
mvn clean deploy -s "${SETTINGS_FILE}" -pl sdk-testing -P publishing -DskipTests --no-transfer-progress
mvn clean deploy -s "${SETTINGS_FILE}" -pl otel-plugin -P publishing -DskipTests --no-transfer-progress

Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ on:
- '.github/workflows/ai-pr-review.yml'
- '.github/prompts/ai-pr-review.md'
- 'sdk/**'
- 'extra-serdes/**'
- 'sdk-testing/**'
- 'sdk-integration-tests/**'
- 'examples/**'
Expand All @@ -38,6 +39,7 @@ on:
- '.github/workflows/ai-pr-review.yml'
- '.github/prompts/ai-pr-review.md'
- 'sdk/**'
- 'extra-serdes/**'
- 'sdk-testing/**'
- 'sdk-integration-tests/**'
- 'examples/**'
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/e2e-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ on:
paths:
- '.github/**' # for testing Github Actions
- 'sdk/**'
- 'extra-serdes/**'
- 'sdk-testing/**'
- 'sdk-integration-tests/**'
- 'examples/**'
Expand All @@ -18,6 +19,7 @@ on:
paths:
- '.github/**'
- 'sdk/**'
- 'extra-serdes/**'
- 'sdk-testing/**'
- 'sdk-integration-tests/**'
- 'examples/**'
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/publish_maven.yml
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ jobs:
run: |
gh release upload "$RELEASE_TAG" \
"sdk/target/aws-durable-execution-sdk-java-${RELEASE_VERSION}.jar" \
"extra-serdes/target/aws-durable-execution-sdk-java-extra-serdes-${RELEASE_VERSION}.jar" \
"sdk-testing/target/aws-durable-execution-sdk-java-testing-${RELEASE_VERSION}.jar" \
"otel-plugin/target/aws-durable-execution-sdk-java-plugin-otel-${RELEASE_VERSION}.jar" \
--clobber
Expand Down
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ Build resilient, long-running AWS Lambda functions that automatically checkpoint
- **Replay Safety** – Functions deterministically resume from checkpoints after interruptions
- **Type Safety** – Full generic type support for step results
- **Data-Driven Concurrency** – Apply a function across a collection with `map()`, with per-item error isolation and configurable completion criteria
- **Optional Lambda Event Models** – Parse SQS, SNS, S3, and other Lambda trigger events with the Java Lambda runtime mappings

## How It Works

Expand Down Expand Up @@ -50,6 +51,20 @@ Your durable function extends `DurableHandler<I, O>` and implements `handleReque
</dependency>
```

For handlers that receive models from `aws-lambda-java-events`, also add the
optional event serialization module:

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

Configure the module with
`DurableConfig.builder().withSerDes(new LambdaEventSerDes()).build()`.

### Your First Durable Function

```java
Expand Down
9 changes: 5 additions & 4 deletions RELEASE.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,9 @@ The publication workflow:

1. Verifies that the tag is a semantic version, points to a commit on the
default branch, and matches the Maven version in the tagged POM.
2. Builds, signs, and uploads the SDK, testing library, and OpenTelemetry plugin
to Sonatype Central Portal.
3. Uploads the three JARs to the existing GitHub release.
2. Builds, signs, and uploads the SDK, extra SerDes module, testing library, and
OpenTelemetry plugin to Sonatype Central Portal.
3. Uploads the four JARs to the existing GitHub release.
4. Opens a pull request for the next development version. A final release
increments the patch version, so `2.1.1` produces `2.1.2-SNAPSHOT`. A
prerelease keeps the same base version, so `2.1.1-rc1` produces
Expand All @@ -56,7 +56,8 @@ After **Publish Maven Release** succeeds:
1. Open [Publishing Deployments](https://central.sonatype.com/publishing/deployments)
in Sonatype Central Portal.
2. Find the deployments for the release version and verify that they contain
the expected SDK, testing library, and OpenTelemetry plugin artifacts.
the expected SDK, extra SerDes module, testing library, and OpenTelemetry
plugin artifacts.
3. Click **Publish** for each deployment and wait for publication to complete.
The workflow uses `autoPublish=false`, so this manual action is required.
4. Confirm that the GitHub release contains the expected JARs and that the
Expand Down
5 changes: 5 additions & 0 deletions coverage-report/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@
<artifactId>aws-durable-execution-sdk-java-testing</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>software.amazon.lambda.durable</groupId>
<artifactId>aws-durable-execution-sdk-java-extra-serdes</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>software.amazon.lambda.durable</groupId>
<artifactId>aws-durable-execution-sdk-java-integration-tests</artifactId>
Expand Down
18 changes: 18 additions & 0 deletions docs/advanced/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,24 @@ public class OrderProcessor extends DurableHandler<Order, OrderResult> {

The `withExecutorService()` option configures the thread pool used for running user-defined operations. Internal SDK coordination (checkpoint batching, polling) runs on an SDK-managed thread pool.

### Lambda trigger event inputs

The optional `aws-durable-execution-sdk-java-extra-serdes` module provides
`LambdaEventSerDes`, which applies the official Java Lambda runtime mappings
for `SQSEvent`, `SNSEvent`, `S3Event`, and other supported event models:

```java
@Override
protected DurableConfig createConfiguration() {
return DurableConfig.builder()
.withSerDes(new LambdaEventSerDes())
.build();
}
```

The module delegates non-event values, including generic types, to
`JacksonSerDes`. See the module README for installation details.

### Dynamic plugin loading

Dynamic plugin loading is an opt-in alternative to registering plugins in application code. Put provider JARs on the application class path, then set `DURABLE_EXECUTION_PLUGINS` to an ordered, comma-separated list of provider names:
Expand Down
1 change: 1 addition & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ mvn test -Dtest=CloudBasedIntegrationTest \
| [ErrorHandlingExample](src/main/java/software/amazon/lambda/durable/examples/general/ErrorHandlingExample.java) | Handling `StepFailedException` and `StepInterruptedException` |
| [GenericTypesExample](src/main/java/software/amazon/lambda/durable/examples/general/GenericTypesExample.java) | Working with `List<T>` and `Map<K,V>` |
| [CustomConfigExample](src/main/java/software/amazon/lambda/durable/examples/general/CustomConfigExample.java) | Custom Lambda client and SerDes |
| [LambdaEventSerDesExample](src/main/java/software/amazon/lambda/durable/examples/general/LambdaEventSerDesExample.java) | Deserializing SQS events with the optional extra SerDes module |
| [WaitAtLeastExample](src/main/java/software/amazon/lambda/durable/examples/wait/WaitAtLeastExample.java) | Concurrent `stepAsync()` with `wait()` |
| [WaitAsyncExample](src/main/java/software/amazon/lambda/durable/examples/wait/WaitAsyncExample.java) | Non-blocking `waitAsync()` with concurrent step |
| [RetryInProcessExample](src/main/java/software/amazon/lambda/durable/examples/step/RetryInProcessExample.java) | In-process retry with concurrent operations |
Expand Down
5 changes: 5 additions & 0 deletions examples/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@
<artifactId>aws-durable-execution-sdk-java</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>software.amazon.lambda.durable</groupId>
<artifactId>aws-durable-execution-sdk-java-extra-serdes</artifactId>
<version>${project.version}</version>
</dependency>

<!-- AWS Lambda Java Core -->
<dependency>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package software.amazon.lambda.durable.examples.general;

import com.amazonaws.services.lambda.runtime.events.SQSEvent;
import software.amazon.lambda.durable.DurableConfig;
import software.amazon.lambda.durable.DurableContext;
import software.amazon.lambda.durable.DurableHandler;
import software.amazon.lambda.durable.events.LambdaEventSerDes;

/**
* Example demonstrating Lambda runtime serialization for an SQS event.
*
* <p>The extra SerDes module preserves Lambda event property mappings such as {@code eventSourceARN} when converting
* the durable execution input into {@link SQSEvent}.
*/
public class LambdaEventSerDesExample extends DurableHandler<SQSEvent, String> {

@Override
protected DurableConfig createConfiguration() {
return DurableConfig.builder().withSerDes(new LambdaEventSerDes()).build();
}

@Override
public String handleRequest(SQSEvent input, DurableContext context) {
var message = input.getRecords().get(0);
return context.step(
"read-sqs-message",
String.class,
stepContext -> message.getMessageId() + "|" + message.getBody() + "|" + message.getEventSourceArn());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import static org.junit.jupiter.api.Assertions.*;
import static software.amazon.lambda.durable.TypeToken.get;

import com.amazonaws.services.lambda.runtime.events.SQSEvent;
import java.time.Duration;
import java.util.HashMap;
import java.util.List;
Expand All @@ -24,6 +25,7 @@
import software.amazon.awssdk.services.lambda.model.OperationStatus;
import software.amazon.awssdk.services.sts.StsClient;
import software.amazon.lambda.durable.TypeToken;
import software.amazon.lambda.durable.events.LambdaEventSerDes;
import software.amazon.lambda.durable.examples.general.GenericTypesExample;
import software.amazon.lambda.durable.examples.types.ApprovalRequest;
import software.amazon.lambda.durable.examples.types.GreetingRequest;
Expand Down Expand Up @@ -363,6 +365,32 @@ void testCustomConfigExample() {
assertTrue(stepResult.contains("email_address"));
}

@Test
void testLambdaEventSerDesExample() {
var message = new SQSEvent.SQSMessage();
message.setMessageId("cloud-message-1");
message.setBody("hello from cloud sqs");
message.setEventSourceArn("arn:aws:sqs:us-west-2:123456789012:orders");

var event = new SQSEvent();
event.setRecords(List.of(message));

var runner = CloudDurableTestRunner.create(
arn("lambda-event-ser-des-example"), SQSEvent.class, String.class, lambdaClient)
.withSerDes(new LambdaEventSerDes());
var result = runner.run(event);

assertEquals(ExecutionStatus.SUCCEEDED, result.getStatus());
assertEquals(
"cloud-message-1|hello from cloud sqs|arn:aws:sqs:us-west-2:123456789012:orders", result.getResult());

var operation = runner.getOperation("read-sqs-message");
assertNotNull(operation);
assertEquals(
"cloud-message-1|hello from cloud sqs|arn:aws:sqs:us-west-2:123456789012:orders",
operation.getStepResult(String.class));
}

@Test
void testErrorHandlingExample() {
var runner =
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package software.amazon.lambda.durable.examples.general;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

import com.amazonaws.services.lambda.runtime.events.SQSEvent;
import java.util.List;
import org.junit.jupiter.api.Test;
import software.amazon.lambda.durable.model.ExecutionStatus;
import software.amazon.lambda.durable.testing.LocalDurableTestRunner;

class LambdaEventSerDesExampleTest {

@Test
void deserializesSqsEventWithLambdaRuntimeMappings() {
var message = new SQSEvent.SQSMessage();
message.setMessageId("message-1");
message.setBody("hello from sqs");
message.setEventSourceArn("arn:aws:sqs:us-east-1:123456789012:orders");

var event = new SQSEvent();
event.setRecords(List.of(message));

var result = LocalDurableTestRunner.create(SQSEvent.class, new LambdaEventSerDesExample())
.run(event);

assertEquals(ExecutionStatus.SUCCEEDED, result.getStatus());
assertTrue(result.getResult(String.class).contains("message-1|hello from sqs|"));
assertTrue(result.getResult(String.class).contains("arn:aws:sqs:us-east-1:123456789012:orders"));
}
}
41 changes: 41 additions & 0 deletions extra-serdes/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# AWS Lambda Durable Execution Extra SerDes

The `aws-durable-execution-sdk-java-extra-serdes` module provides
`LambdaEventSerDes`, which uses the Java Lambda runtime serializers for event
models from `aws-lambda-java-events`. This is useful for durable handlers that
receive SQS, SNS, S3, or other supported Lambda trigger events.

The module is optional so applications that do not use Lambda trigger models do
not need the event model and runtime serialization dependencies.

## Installation

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

## Configuration

```java
import software.amazon.lambda.durable.DurableConfig;
import software.amazon.lambda.durable.events.LambdaEventSerDes;

@Override
protected DurableConfig createConfiguration() {
return DurableConfig.builder()
.withSerDes(new LambdaEventSerDes())
.build();
}
```

`LambdaEventSerDes` uses the official `aws-lambda-java-serialization` mappings
for supported Lambda event classes and delegates all other values to
`JacksonSerDes`. A custom delegate can be supplied for non-event values:

```java
new LambdaEventSerDes(customSerDes)
```
84 changes: 84 additions & 0 deletions extra-serdes/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>software.amazon.lambda.durable</groupId>
<artifactId>aws-durable-execution-sdk-java-parent</artifactId>
<version>2.2.1-SNAPSHOT</version>
</parent>

<artifactId>aws-durable-execution-sdk-java-extra-serdes</artifactId>
<packaging>jar</packaging>

<name>AWS Lambda Durable Execution SDK Extra SerDes</name>
<description>Additional serialization support for AWS Lambda event models</description>
<url>https://github.com/aws/aws-durable-execution-sdk-java</url>

<scm>
<connection>scm:git:https://github.com/aws/aws-durable-execution-sdk-java.git</connection>
<developerConnection>scm:git:https://github.com/aws/aws-durable-execution-sdk-java.git</developerConnection>
<url>https://github.com/aws/aws-durable-execution-sdk-java</url>
</scm>

<dependencies>
<dependency>
<groupId>software.amazon.lambda.durable</groupId>
<artifactId>aws-durable-execution-sdk-java</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-lambda-java-events</artifactId>
</dependency>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-lambda-java-serialization</artifactId>
</dependency>

<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar-no-fork</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<executions>
<execution>
<id>attach-javadocs</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Loading
Loading