Customize SDK behavior by overriding createConfiguration() in your handler:
public class OrderProcessor extends DurableHandler<Order, OrderResult> {
@Override
protected DurableConfig createConfiguration() {
// Custom Lambda client with connection pooling
var lambdaClientBuilder = LambdaClient.builder()
.httpClient(ApacheHttpClient.builder()
.maxConnections(50)
.connectionTimeout(Duration.ofSeconds(30))
.build());
return DurableConfig.builder()
.withLambdaClientBuilder(lambdaClientBuilder)
.withSerDes(new MyCustomSerDes()) // Custom serialization
.withExecutorService(Executors.newFixedThreadPool(10)) // Custom thread pool
.withLoggerConfig(LoggerConfig.withReplayLogging()) // Enable replay logs
.build();
}
@Override
protected OrderResult handleRequest(Order order, DurableContext ctx) {
// Your handler logic
}
}| Option | Description | Default |
|---|---|---|
withLambdaClientBuilder() |
Custom AWS Lambda client | Auto-configured Lambda client |
withSerDes() |
Serializer for step results | Jackson with default settings |
withExecutorService() |
Thread pool for user-defined operations | Cached daemon thread pool |
withLoggerConfig() |
Logger behavior configuration | Suppress logs during replay |
withPollingStrategy() |
Backend polling strategy | Exponential backoff: 1s base, 2x rate, FULL jitter, 10s max |
withCheckpointDelay() |
How often the SDK checkpoints updates | Duration.ofSeconds(0) (as soon as possible) |
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.
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:
DURABLE_EXECUTION_PLUGINS=otel-invocation,com.example.audit
When the variable is unset or blank, the SDK does not perform provider discovery. During DurableConfig construction, the SDK uses ServiceLoader and the thread context class loader to find DurableExecutionPluginProvider implementations. Only named providers create plugins.
Dynamically loaded plugins run first in the order listed in DURABLE_EXECUTION_PLUGINS. Plugins registered through withPlugins(...) follow in configuration order. Both sources are additive: if the same plugin is selected dynamically and registered explicitly, both factories are registered and each produces an instance that receives lifecycle hooks. Duplicate configured provider names, duplicate discovered provider names, missing providers, provider discovery failures, and selected providers that were built against an older SDK and do not implement createPlugin(InvocationInfo) stop configuration with an IllegalStateException.
To distribute a provider in a Lambda layer, package its JAR under java/lib:
my-plugin-layer.zip
`-- java
`-- lib
`-- my-durable-plugin.jar
The provider JAR must contain:
META-INF/services/software.amazon.lambda.durable.plugin.DurableExecutionPluginProvider
The service file contains the provider implementation class name. A provider is itself the per-invocation plugin factory: the SDK calls createPlugin(InvocationInfo) once per Lambda invocation and drops the returned instance when that invocation returns, so the instance can hold its invocation's state in plain fields. A minimal provider looks like:
public final class AuditPluginProvider implements DurableExecutionPluginProvider {
@Override
public String getName() {
return "com.example.audit";
}
@Override
public DurableExecutionPlugin createPlugin(InvocationInfo invocationInfo) {
return new AuditPlugin(invocationInfo.durableExecutionArn());
}
}Provider-specific settings can use namespaced environment variables. If an application shades provider JARs into one artifact, its build must preserve and merge META-INF/services entries.
The SDK includes Filesystem SerDes, which provides
ALWAYS and OVERFLOW modes, URI/hash paths, delegate serializers, and previews. Configure it per operation with serDes(...); keep the global serializer
for normal Lambda input/output. It requires a durable shared mount and external
file retention.
Custom serializers can override serialize(value, SerDesContext) and
deserialize(data, typeToken, SerDesContext) to receive stable execution/entity
identity. Existing implementations inherit defaults that call the original methods.