Serialization¶
Checkpointed data transformation¶
The SDK serializes durable operation results to checkpoint storage. When your durable function replays the SDK uses those stored results rather than re-running the code wrapped inside the operation. A SerDes (serializer/deserializer) is the object that serializes the operation result, and deserializes it during replay.
Each SDK ships a default SerDes that handles the most common types. You only need a custom SerDes when the default SerDes cannot handle your data types, or when you need special behavior such as encryption, compression or writing to external storage.
The following example uses no SerDes configuration. The SDK serializes and deserializes the step result automatically.
import { DurableContext, withDurableExecution } from "@aws/durable-execution-sdk-js";
interface Order {
id: string;
total: string;
}
export const handler = withDurableExecution(
async (event: unknown, context: DurableContext) => {
// No SerDes config — the SDK serializes and deserializes the result automatically.
const order = await context.step("fetch-order", async (): Promise<Order> => {
return { id: "order-123", total: "99.99" };
});
return order;
},
);
from aws_durable_execution_sdk_python import (
DurableContext,
StepContext,
durable_execution,
durable_step,
)
@durable_step
def fetch_order(ctx: StepContext) -> dict:
return {"id": "order-123", "total": "99.99"}
@durable_execution
def handler(event: dict, context: DurableContext) -> dict:
# No SerDes config — the SDK serializes and deserializes the result automatically.
order = context.step(fetch_order())
return order
import java.util.Map;
import software.amazon.lambda.durable.DurableContext;
import software.amazon.lambda.durable.DurableHandler;
import software.amazon.lambda.durable.StepContext;
public class Walkthrough extends DurableHandler<Object, Map<String, String>> {
@Override
public Map<String, String> handleRequest(Object input, DurableContext context) {
// No SerDes config — the SDK serializes and deserializes the result automatically.
Map<String, String> order = context.step(
"fetch-order",
Map.class,
(StepContext ctx) -> Map.of("id", "order-123", "total", "99.99")
);
return order;
}
}
package main
import (
"github.com/aws/aws-durable-execution-sdk-go/durable"
)
type Order struct {
ID string `json:"id"`
Total string `json:"total"`
}
func handler(ctx durable.Context, _ any) (Order, error) {
// No serdes option: the SDK serializes and deserializes the result automatically.
return durable.Step(ctx, "fetch-order", func(durable.StepContext) (Order, error) {
return Order{ID: "order-123", Total: "99.99"}, nil
})
}
func main() { durable.Start(handler) }
using Amazon.Lambda.Core;
using Amazon.Lambda.DurableExecution;
public class WalkthroughExample
{
public Task<DurableExecutionInvocationOutput> Handler(
DurableExecutionInvocationInput input, ILambdaContext context)
=> DurableFunction.WrapAsync<object, Order>(Workflow, input, context);
private async Task<Order> Workflow(object input, IDurableContext ctx)
{
// No serializer configuration on the operation — the SDK serializes and
// deserializes the result with the ILambdaSerializer registered on
// ILambdaContext.Serializer.
Order order = await ctx.StepAsync(
async (_, _) => new Order("order-123", "99.99"),
name: "fetch-order");
return order;
}
}
public record Order(string Id, string Total);
Round-trip serialization¶
The SDK round-trips an operation result by serializing and then deserializing it before returning it on the first run. This round trip ensures that the first run returns the same value that replay reconstructs from the operation's checkpoint.
A serializer/deserializer pair should preserve the value across a round trip.
deserialize(serialize(value)) should be equivalent to value. If it is not,
the round trip will change the operation result.
Lambda handler serialization¶
The Durable Execution SDK SerDes only applies to durable operation results. It does not affect the final return value of your Lambda handler, which Lambda serializes separately.
Lambda serializes handler return values with JSON.stringify. The same types that work
with defaultSerdes work as handler return values.
Lambda serializes handler return values with json.dumps. Types like datetime and
Decimal are safe inside steps because ExtendedTypeSerDes handles them, but returning
them directly from your handler raises
TypeError: Object of type X is not JSON serializable. This is a common error when
working with Amazon DynamoDB results. Convert those values to JSON-safe types before
returning from the handler.
Lambda serializes handler return values with Jackson. The same JacksonSerDes
configuration applies to both step results and handler return values when you use
DurableHandler.
The SDK decodes the handler's input and encodes its return value with
encoding/json, with HTML escaping disabled. A Serdes set with WithSerdes or
ConfigureSerdes does not affect them. It applies only to durable operation results.
The registered ILambdaSerializer serializes everything: durable operation results
and the handler's final return value. There is no separate serializer for operation
results, so the same configuration always applies to both.
Default serialization¶
Each SDK uses a default SerDes when you do not provide one.
The default is defaultSerdes, which uses JSON.stringify to serialize and
JSON.parse to deserialize. It handles any value that JSON.stringify accepts.
The default is ExtendedTypeSerDes. It uses plain JSON for primitives (None, str,
int, float, bool, and lists of primitives) and an envelope format for everything
else. The envelope format preserves the exact Python type through the round-trip.
Supported types beyond primitives: datetime, date, Decimal, UUID, bytes,
bytearray, memoryview, tuple, list, dict, and BatchResult.
The default is JacksonSerDes, which uses Jackson's ObjectMapper. It supports Java 8
time types, serializes dates as ISO-8601 strings, and ignores unknown properties during
deserialization.
Pass a custom ObjectMapper to the JacksonSerDes constructor to override the default
configuration.
The default is JSONSerdes, which encodes with encoding/json and HTML escaping
disabled, so <, >, and & stay literal in the stored JSON. Map keys are sorted
and a nil value encodes as null. It handles any value encoding/json accepts. A
type that does not round-trip through encoding/json needs a custom Serdes.
There is no per-operation default SerDes. Register a single ILambdaSerializer at the
host boundary and the SDK uses it for every durable operation result.
Use DefaultLambdaJsonSerializer (from Amazon.Lambda.Serialization.SystemTextJson)
for reflection-based serialization. For AOT or trim-friendly functions, use
SourceGeneratorLambdaJsonSerializer<TContext> and register your JsonSerializerContext.
SerDes interface definition¶
import { Serdes, SerdesContext } from "@aws/durable-execution-sdk-js";
// Serdes<T> interface
interface SerdesInterface<T> {
serialize: (value: T | undefined, context: SerdesContext) => Promise<string | undefined>;
deserialize: (data: string | undefined, context: SerdesContext) => Promise<T | undefined>;
}
// SerdesContext
interface SerdesContextShape {
entityId: string;
durableExecutionArn: string;
}
Parameters:
serializeAn async function that receives the value and aSerdesContext, and returnsPromise<string | undefined>.deserializeAn async function that receives the serialized string and aSerdesContext, and returnsPromise<T | undefined>.
Both methods are async so that implementations can interact with external services such as S3 or KMS.
SerdesContext fields:
entityIdThe operation ID for the current step or operation.durableExecutionArnThe ARN of the current durable execution.
from abc import ABC, abstractmethod
from dataclasses import dataclass
from typing import Generic, TypeVar
T = TypeVar("T")
@dataclass(frozen=True)
class SerDesContext:
operation_id: str = ""
durable_execution_arn: str = ""
class SerDes(ABC, Generic[T]):
@abstractmethod
def serialize(self, value: T, serdes_context: SerDesContext) -> str: ...
@abstractmethod
def deserialize(self, data: str, serdes_context: SerDesContext) -> T: ...
Parameters:
serialize(value, serdes_context)Converts the value to a string.deserialize(data, serdes_context)Converts the string back to the original type.
SerDesContext fields:
operation_idThe operation ID for the current step or operation.durable_execution_arnThe ARN of the current durable execution.
import software.amazon.lambda.durable.TypeToken;
import software.amazon.lambda.durable.serde.SerDes;
// SerDes interface
interface SerDesInterface {
String serialize(Object value);
<T> T deserialize(String data, TypeToken<T> typeToken);
}
Parameters:
serialize(Object value)Converts the value to a JSON string. Returnsnullifvalueisnull.deserialize(String data, TypeToken<T> typeToken)Converts the JSON string back to typeT. Returnsnullifdataisnull.
Use TypeToken<T> to capture generic type information that Java erases at runtime. For
example: new TypeToken<List<String>>() {}.
The interface is Serdes. Its methods are Marshal and Unmarshal, which work
with byte slices and take a context.Context for external calls.
type Serdes interface {
Marshal(ctx context.Context, meta SerdesContext, v any) ([]byte, error)
Unmarshal(ctx context.Context, meta SerdesContext, data []byte, v any) error
}
type SerdesContext struct {
// OperationID is the positional ID of the operation being serialized
// (e.g., "1", "1-2-3").
OperationID string
// DurableExecutionArn is the ARN of the current durable execution.
DurableExecutionArn string
// Has unexported fields.
}
Methods:
Marshal(ctx, meta, v)Converts the value to bytes.Unmarshal(ctx, meta, data, v)Fills the pointervfrom the bytes.
SerdesContext fields:
OperationIDThe operation ID for the current step or operation.DurableExecutionArnThe ARN of the current durable execution.
The interface takes any, so one Serdes serves every result type. For a serdes
written for one type, use SerdesOf. A serdes failure is permanent by default.
Return RetryableSerdesError to make it transient.
.NET has no per-operation SerDes interface. Serialization is controlled by the single
ILambdaSerializer registered on ILambdaContext.Serializer.
// .NET has no per-operation SerDes interface. Serialization for every durable
// operation is handled by the single ILambdaSerializer registered on
// ILambdaContext.Serializer. Implement this interface to customize serialization.
public interface ILambdaSerializer
{
T Deserialize<T>(Stream requestStream);
void Serialize<T>(T response, Stream responseStream);
}
Methods:
Serialize<T>(T response, Stream responseStream)Writes the serialized form ofresponsetoresponseStream.Deserialize<T>(Stream requestStream)ReadsrequestStreamand returns the deserialized value of typeT.
The same ILambdaSerializer handles every durable operation result and the handler's
return value.
Custom SerDes example¶
Implement the SerDes interface when the default cannot handle your types, or when you need special behavior such as encryption or compression.
import { Serdes, SerdesContext } from "@aws/durable-execution-sdk-js";
interface Order {
id: string;
total: string;
}
const orderSerdes: Serdes<Order> = {
serialize: async (value: Order | undefined, _ctx: SerdesContext) => {
if (value === undefined) return undefined;
return JSON.stringify(value);
},
deserialize: async (data: string | undefined, _ctx: SerdesContext) => {
if (data === undefined) return undefined;
return JSON.parse(data) as Order;
},
};
import com.fasterxml.jackson.databind.ObjectMapper;
import software.amazon.lambda.durable.TypeToken;
import software.amazon.lambda.durable.exception.SerDesException;
import software.amazon.lambda.durable.serde.SerDes;
public class OrderSerDes implements SerDes {
private final ObjectMapper mapper = new ObjectMapper();
@Override
public String serialize(Object value) {
try {
return mapper.writeValueAsString(value);
} catch (Exception e) {
throw new SerDesException("Serialization failed", e);
}
}
@Override
public <T> T deserialize(String data, TypeToken<T> typeToken) {
try {
return mapper.readValue(data, mapper.getTypeFactory().constructType(typeToken.getType()));
} catch (Exception e) {
throw new SerDesException("Deserialization failed", e);
}
}
}
Implement Marshal and Unmarshal on a type. For a serdes that handles one result
type, SerdesOf is shorter, as the next example shows.
package main
import (
"context"
"encoding/json"
"github.com/aws/aws-durable-execution-sdk-go/durable"
)
type Order struct {
ID string `json:"id"`
Total string `json:"total"`
}
// orderSerdes implements durable.Serdes for Order results.
type orderSerdes struct{}
func (orderSerdes) Marshal(_ context.Context, _ durable.SerdesContext, v any) ([]byte, error) {
return json.Marshal(v)
}
func (orderSerdes) Unmarshal(_ context.Context, _ durable.SerdesContext, data []byte, v any) error {
return json.Unmarshal(data, v)
}
Implement ILambdaSerializer and register it at the host boundary. The custom
serializer applies to every durable operation result, not a single operation.
using System.Text.Json;
using Amazon.Lambda.Core;
// .NET has no per-operation SerDes. Serialization is controlled by the single
// ILambdaSerializer registered on ILambdaContext.Serializer. To customize how
// durable operation results are serialized, implement ILambdaSerializer and
// register it at the host boundary instead of passing a SerDes per operation.
public class OrderLambdaSerializer : ILambdaSerializer
{
private static readonly JsonSerializerOptions Options = new()
{
PropertyNameCaseInsensitive = true,
};
public T Deserialize<T>(Stream requestStream)
{
using var reader = new StreamReader(requestStream);
string json = reader.ReadToEnd();
return JsonSerializer.Deserialize<T>(json, Options)!;
}
public void Serialize<T>(T response, Stream responseStream)
{
string json = JsonSerializer.Serialize(response, Options);
using var writer = new StreamWriter(responseStream, leaveOpen: true);
writer.Write(json);
writer.Flush();
}
}
Custom SerDes on durable operations¶
Pass a SerDes instance in the configuration object for the operation you want to customize. The SDK uses that SerDes for that operation only. Other operations in the same handler continue to use the default.
StepConfig¶
import {
DurableContext,
Serdes,
SerdesContext,
StepConfig,
withDurableExecution,
} from "@aws/durable-execution-sdk-js";
interface Order {
id: string;
total: string;
}
const orderSerdes: Serdes<Order> = {
serialize: async (value: Order | undefined, _ctx: SerdesContext) =>
value !== undefined ? JSON.stringify(value) : undefined,
deserialize: async (data: string | undefined, _ctx: SerdesContext) =>
data !== undefined ? (JSON.parse(data) as Order) : undefined,
};
const stepConfig: StepConfig<Order> = { serdes: orderSerdes };
export const handler = withDurableExecution(
async (event: unknown, context: DurableContext) => {
const order = await context.step(
"fetch-order",
async (): Promise<Order> => ({ id: "order-123", total: "99.99" }),
stepConfig,
);
return order;
},
);
import json
from aws_durable_execution_sdk_python import (
DurableContext,
StepContext,
durable_execution,
durable_step,
)
from aws_durable_execution_sdk_python.config import StepConfig
from aws_durable_execution_sdk_python.serdes import SerDes, SerDesContext
class OrderSerDes(SerDes[dict]):
def serialize(self, value: dict, ctx: SerDesContext) -> str:
return json.dumps(value)
def deserialize(self, data: str, ctx: SerDesContext) -> dict:
return json.loads(data)
@durable_step
def fetch_order(ctx: StepContext) -> dict:
return {"id": "order-123", "total": "99.99"}
@durable_execution
def handler(event: dict, context: DurableContext) -> dict:
order = context.step(
fetch_order(),
config=StepConfig(serdes=OrderSerDes()),
)
return order
import java.util.Map;
import software.amazon.lambda.durable.DurableContext;
import software.amazon.lambda.durable.DurableHandler;
import software.amazon.lambda.durable.StepContext;
import software.amazon.lambda.durable.config.StepConfig;
import software.amazon.lambda.durable.serde.JacksonSerDes;
public class StepConfigExample extends DurableHandler<Object, Map<String, String>> {
@Override
public Map<String, String> handleRequest(Object input, DurableContext context) {
StepConfig config = StepConfig.builder()
.serDes(new JacksonSerDes())
.build();
Map<String, String> order = context.step(
"fetch-order",
Map.class,
(StepContext ctx) -> Map.of("id", "order-123", "total", "99.99"),
config
);
return order;
}
}
Pass WithStepSerdes to override the serializer for one step's result. Other
operations keep the default. The other per-operation options are WithChildSerdes,
WithInvokePayloadSerdes, WithInvokeResultSerdes, and WithTypedBranchSerdes. A
per-operation option takes precedence over the handler default set with WithSerdes
or ConfigureSerdes.
package main
import (
"context"
"encoding/json"
"github.com/aws/aws-durable-execution-sdk-go/durable"
)
type Order struct {
ID string `json:"id"`
Total string `json:"total"`
}
var orderSerdes = durable.SerdesOf(
func(_ context.Context, _ durable.SerdesContext, o Order) ([]byte, error) {
return json.Marshal(o)
},
func(_ context.Context, _ durable.SerdesContext, data []byte) (Order, error) {
var o Order
err := json.Unmarshal(data, &o)
return o, err
},
)
func handler(ctx durable.Context, _ any) (Order, error) {
return durable.Step(ctx, "fetch-order", func(durable.StepContext) (Order, error) {
return Order{ID: "order-123", Total: "99.99"}, nil
}, durable.WithStepSerdes(orderSerdes))
}
func main() { durable.Start(handler) }
StepConfig does not expose a serializer. The step result is serialized with the
ILambdaSerializer registered at the host boundary. Register a custom
ILambdaSerializer there to change how step results are serialized.
using Amazon.Lambda.Core;
using Amazon.Lambda.DurableExecution;
public class StepConfigExample
{
public Task<DurableExecutionInvocationOutput> Handler(
DurableExecutionInvocationInput input, ILambdaContext context)
=> DurableFunction.WrapAsync<object, Order>(Workflow, input, context);
private async Task<Order> Workflow(object input, IDurableContext ctx)
{
// StepConfig has no serializer slot. The step result is serialized with
// the ILambdaSerializer registered on ILambdaContext.Serializer. To
// customize serialization, register a custom ILambdaSerializer at the
// host boundary instead of setting a per-step SerDes.
var config = new StepConfig
{
RetryStrategy = RetryStrategy.Exponential(maxAttempts: 3),
};
Order order = await ctx.StepAsync(
async (_, _) => new Order("order-123", "99.99"),
name: "fetch-order",
config: config);
return order;
}
}
public record Order(string Id, string Total);
CallbackConfig¶
The callback SerDes controls how the SDK deserializes the payload that an external system sends when it completes the callback.
In TypeScript, the callback SerDes only needs deserialize. The serialize method is
not used because the external system provides the payload directly.
import {
DurableContext,
Serdes,
SerdesContext,
withDurableExecution,
} from "@aws/durable-execution-sdk-js";
interface ApprovalResult {
approved: boolean;
reason: string;
}
// Callbacks only need deserialization — the external system sends the payload.
const approvalSerdes: Omit<Serdes<ApprovalResult>, "serialize"> = {
deserialize: async (data: string | undefined, _ctx: SerdesContext) =>
data !== undefined ? (JSON.parse(data) as ApprovalResult) : undefined,
};
export const handler = withDurableExecution(
async (event: unknown, context: DurableContext) => {
const [approval, callbackId] = await context.createCallback("await-approval", {
serdes: approvalSerdes,
});
// Send callbackId to the external system here.
console.log("Callback ID:", callbackId);
const result = await approval;
return result;
},
);
import json
from aws_durable_execution_sdk_python import (
DurableContext,
durable_execution,
)
from aws_durable_execution_sdk_python.config import CallbackConfig
from aws_durable_execution_sdk_python.serdes import SerDes, SerDesContext
class ApprovalSerDes(SerDes[dict]):
def serialize(self, value: dict, ctx: SerDesContext) -> str:
return json.dumps(value)
def deserialize(self, data: str, ctx: SerDesContext) -> dict:
return json.loads(data)
@durable_execution
def handler(event: dict, context: DurableContext) -> dict:
callback = context.create_callback(
"await-approval",
config=CallbackConfig(serdes=ApprovalSerDes()),
)
# Send callback.callback_id to the external system here.
result = callback.result()
return result
import java.util.Map;
import software.amazon.lambda.durable.DurableCallbackFuture;
import software.amazon.lambda.durable.DurableContext;
import software.amazon.lambda.durable.DurableHandler;
import software.amazon.lambda.durable.config.CallbackConfig;
import software.amazon.lambda.durable.serde.JacksonSerDes;
public class CallbackConfigExample extends DurableHandler<Object, Map<String, Object>> {
@Override
public Map<String, Object> handleRequest(Object input, DurableContext context) {
CallbackConfig config = CallbackConfig.builder()
.serDes(new JacksonSerDes())
.build();
DurableCallbackFuture<Map<String, Object>> callback =
context.createCallback("await-approval", Map.class, config);
// Send callback.getCallbackId() to the external system here.
return callback.get();
}
}
The default callback deserializer is RawSerdes, which returns the submitted bytes
unchanged. So by default the result type must be string, []byte, or
json.RawMessage. Any other type fails with a *SerdesError. Pass
WithCallbackSerdes(JSONSerdes) to decode a JSON payload into a struct.
For a callback payload, the per-operation WithCallbackSerdes takes precedence over
the handler-wide Deserializer. When neither is set, the payload passes through
RawSerdes. Set the handler-wide Deserializer with WithCallbackDeserializer, or
from inside the handler with the CallbackDeserializer field of ConfigureSerdes.
A Deserializer has one method, Unmarshal(data []byte, v any) error. The
handler-level WithSerdes does not apply to callback payloads. WaitForCallback
accepts the same WithCallbackSerdes option and decodes the payload once, at the
WaitForCallback operation.
package main
import (
"github.com/aws/aws-durable-execution-sdk-go/durable"
)
type ApprovalResult struct {
Approved bool `json:"approved"`
Reason string `json:"reason"`
}
func handler(ctx durable.Context, _ any) (ApprovalResult, error) {
// JSONSerdes decodes the payload the external system sends into the struct.
approval, err := durable.CreateCallback[ApprovalResult](ctx, "await-approval",
durable.WithCallbackSerdes(durable.JSONSerdes))
if err != nil {
return ApprovalResult{}, err
}
// Send approval.ID() to the external system here.
ctx.Logger().Info("Callback ID", "callbackId", approval.ID())
return approval.Result(ctx)
}
func main() { durable.Start(handler) }
CallbackConfig does not expose a serializer. The payload the external system delivers
is deserialized with the ILambdaSerializer registered at the host boundary. Register a
custom ILambdaSerializer there to change how the callback payload is deserialized.
using Amazon.Lambda.Core;
using Amazon.Lambda.DurableExecution;
public class CallbackConfigExample
{
public Task<DurableExecutionInvocationOutput> Handler(
DurableExecutionInvocationInput input, ILambdaContext context)
=> DurableFunction.WrapAsync<object, ApprovalResult>(Workflow, input, context);
private async Task<ApprovalResult> Workflow(object input, IDurableContext ctx)
{
// CallbackConfig has no serializer slot. The callback payload delivered by
// the external system is deserialized with the ILambdaSerializer registered
// on ILambdaContext.Serializer. To customize deserialization, register a
// custom ILambdaSerializer at the host boundary.
var config = new CallbackConfig
{
Timeout = TimeSpan.FromHours(1),
};
ICallback<ApprovalResult> callback =
await ctx.CreateCallbackAsync<ApprovalResult>("await-approval", config);
// Send callback.CallbackId to the external system here.
return await callback.GetResultAsync();
}
}
public record ApprovalResult(bool Approved, string Reason);
MapConfig & ParallelConfig¶
Map and parallel perations support two SerDes fields that apply at different levels.
import {
BatchResult,
DurableContext,
MapConfig,
Serdes,
SerdesContext,
withDurableExecution,
} from "@aws/durable-execution-sdk-js";
interface ProcessedItem {
id: string;
status: string;
}
const itemSerdes: Serdes<ProcessedItem> = {
serialize: async (value: ProcessedItem | undefined, _ctx: SerdesContext) =>
value !== undefined ? JSON.stringify(value) : undefined,
deserialize: async (data: string | undefined, _ctx: SerdesContext) =>
data !== undefined ? (JSON.parse(data) as ProcessedItem) : undefined,
};
const mapConfig: MapConfig<string, ProcessedItem> = {
itemSerdes,
};
export const handler = withDurableExecution(
async (event: unknown, context: DurableContext) => {
const items = ["a", "b", "c"];
const result: BatchResult<ProcessedItem> = await context.map(
"process-items",
items,
async (_ctx, item) => ({ id: item, status: "done" }),
mapConfig,
);
return result.getResults();
},
);
itemSerdesserializes each item result.serdesserializes the aggregatedBatchResult.ParallelConfighas the same two fields.
import json
from aws_durable_execution_sdk_python import (
DurableContext,
durable_execution,
)
from aws_durable_execution_sdk_python.config import MapConfig
from aws_durable_execution_sdk_python.serdes import SerDes, SerDesContext
class ItemSerDes(SerDes[dict]):
def serialize(self, value: dict, ctx: SerDesContext) -> str:
return json.dumps(value)
def deserialize(self, data: str, ctx: SerDesContext) -> dict:
return json.loads(data)
@durable_execution
def handler(event: dict, context: DurableContext) -> list:
items = ["a", "b", "c"]
result = context.map(
items,
lambda ctx, item, idx, arr: {"id": item, "status": "done"},
config=MapConfig(item_serdes=ItemSerDes()),
)
return result.get_results()
item_serdesserializes each item result.serdesserializes the aggregatedBatchResult.- When you provide only
serdes, the SDK uses it for both for backward compatibility.ParallelConfighas the same two fields.
import java.util.List;
import java.util.Map;
import software.amazon.lambda.durable.DurableContext;
import software.amazon.lambda.durable.DurableHandler;
import software.amazon.lambda.durable.config.MapConfig;
import software.amazon.lambda.durable.model.MapResult;
import software.amazon.lambda.durable.serde.JacksonSerDes;
public class MapConfigExample extends DurableHandler<Object, List<Map<String, String>>> {
@Override
public List<Map<String, String>> handleRequest(Object input, DurableContext context) {
MapConfig config = MapConfig.builder()
.serDes(new JacksonSerDes())
.build();
List<String> items = List.of("a", "b", "c");
MapResult<Map<String, String>> result = context.map(
"process-items",
items,
Map.class,
(item, index, ctx) -> Map.of("id", item, "status", "done"),
config
);
return result.succeeded();
}
}
serDesapplies to each item result.- Java
ParallelConfigdoes not have aserDesfield.
Two batch options apply to both Map and Parallel:
WithBatchSerdesserializes each item result. Default: the handler serdes.WithBatchResultSerdesserializes the aggregatedBatchResult. Without it, the SDK stores theBatchResultthrough the item serdes.
package main
import (
"context"
"encoding/json"
"github.com/aws/aws-durable-execution-sdk-go/durable"
)
type ProcessedItem struct {
ID string `json:"id"`
Status string `json:"status"`
}
var itemSerdes = durable.SerdesOf(
func(_ context.Context, _ durable.SerdesContext, p ProcessedItem) ([]byte, error) {
return json.Marshal(p)
},
func(_ context.Context, _ durable.SerdesContext, data []byte) (ProcessedItem, error) {
var p ProcessedItem
err := json.Unmarshal(data, &p)
return p, err
},
)
func handler(ctx durable.Context, _ any) ([]ProcessedItem, error) {
items := []string{"a", "b", "c"}
result, err := durable.Map(ctx, "process-items", items,
func(_ durable.Context, item string, _ int) (ProcessedItem, error) {
return ProcessedItem{ID: item, Status: "done"}, nil
},
durable.WithBatchSerdes(itemSerdes),
)
if err != nil {
return nil, err
}
return result.Results(), nil
}
func main() { durable.Start(handler) }
MapConfig does not expose a serializer, and there is no separate item-level
serializer. Each item result is serialized with the ILambdaSerializer registered at
the host boundary. ParallelConfig likewise has no serializer field.
using Amazon.Lambda.Core;
using Amazon.Lambda.DurableExecution;
public class MapConfigExample
{
public Task<DurableExecutionInvocationOutput> Handler(
DurableExecutionInvocationInput input, ILambdaContext context)
=> DurableFunction.WrapAsync<object, IReadOnlyList<ProcessedItem>>(Workflow, input, context);
private async Task<IReadOnlyList<ProcessedItem>> Workflow(object input, IDurableContext ctx)
{
// MapConfig has no serializer slot. Each item result is serialized with the
// ILambdaSerializer registered on ILambdaContext.Serializer. To customize
// serialization, register a custom ILambdaSerializer at the host boundary.
var config = new MapConfig<string>
{
MaxConcurrency = 3,
};
var items = new[] { "a", "b", "c" };
IBatchResult<ProcessedItem> result = await ctx.MapAsync(
items,
async (itemCtx, item, index, all, ct) =>
await itemCtx.StepAsync(
async (_, _) => new ProcessedItem(item, "done"),
name: $"process-{index}"),
name: "process-items",
config: config);
return result.GetResults();
}
}
public record ProcessedItem(string Id, string Status);
Built-in SerDes helpers¶
createClassSerdes(cls) creates a Serdes<T> that preserves class instances through
the round-trip. It deserializes by calling Object.assign(new cls(), JSON.parse(data)),
so class methods are available on the deserialized value. The constructor must take no
required parameters. Private fields and getters are not preserved.
import {
DurableContext,
createClassSerdes,
withDurableExecution,
} from "@aws/durable-execution-sdk-js";
class Order {
id: string = "";
total: string = "";
label() {
return `Order ${this.id} — ${this.total}`;
}
}
const orderSerdes = createClassSerdes(Order);
export const handler = withDurableExecution(
async (event: unknown, context: DurableContext) => {
const order = await context.step(
"fetch-order",
async () => Object.assign(new Order(), { id: "order-123", total: "99.99" }),
{ serdes: orderSerdes },
);
// order.label() works — class methods are preserved after deserialization
return order.label();
},
);
PassThroughSerDes stores the value as-is (the value must already be a string).
JsonSerDes uses json.dumps and json.loads without the envelope format that
ExtendedTypeSerDes adds for complex types.
from aws_durable_execution_sdk_python import (
DurableContext,
StepContext,
durable_execution,
durable_step,
)
from aws_durable_execution_sdk_python.config import StepConfig
from aws_durable_execution_sdk_python.serdes import JsonSerDes, PassThroughSerDes
# PassThroughSerDes — stores the value as-is (must already be a string)
# JsonSerDes — standard json.dumps / json.loads, no envelope format
@durable_step
def fetch_raw(ctx: StepContext) -> str:
return '{"id":"order-123"}'
@durable_execution
def handler(event: dict, context: DurableContext) -> str:
raw = context.step(
fetch_raw(),
config=StepConfig(serdes=PassThroughSerDes()),
)
return raw
The built-in JacksonSerDes handles class instances via Jackson's ObjectMapper. You
can create your own passthrough SerDes like this:
import software.amazon.lambda.durable.DurableContext;
import software.amazon.lambda.durable.DurableHandler;
import software.amazon.lambda.durable.StepContext;
import software.amazon.lambda.durable.TypeToken;
import software.amazon.lambda.durable.config.StepConfig;
import software.amazon.lambda.durable.exception.SerDesException;
import software.amazon.lambda.durable.serde.SerDes;
// A pass-through SerDes stores the value as-is (already a JSON string).
class PassThroughSerDes implements SerDes {
@Override
public String serialize(Object value) {
return (String) value;
}
@Override
public <T> T deserialize(String data, TypeToken<T> typeToken) {
@SuppressWarnings("unchecked")
T result = (T) data;
return result;
}
}
public class PassThroughSerdesExample extends DurableHandler<Object, String> {
@Override
public String handleRequest(Object input, DurableContext context) {
StepConfig config = StepConfig.builder()
.serDes(new PassThroughSerDes())
.build();
return context.step(
"fetch-raw",
String.class,
(StepContext ctx) -> "{\"id\":\"order-123\"}",
config
);
}
}
JSONSerdes is the default. RawSerdes stores the value as-is. The value must be a
string, []byte, or json.RawMessage. SerdesOf is a generic adapter that turns
typed marshal and unmarshal functions into a Serdes. The SDK needs no class
serdes, because a struct's methods are available on any value after a JSON round
trip.
package main
import (
"github.com/aws/aws-durable-execution-sdk-go/durable"
)
// RawSerdes stores the value as-is (it must be a string, []byte, or json.RawMessage).
// JSONSerdes is the default: encoding/json without HTML escaping.
func handler(ctx durable.Context, _ any) (string, error) {
return durable.Step(ctx, "fetch_raw", func(durable.StepContext) (string, error) {
return `{"id":"order-123"}`, nil
}, durable.WithStepSerdes(durable.RawSerdes))
}
func main() { durable.Start(handler) }
DefaultLambdaJsonSerializer handles class and record instances via System.Text.Json.
You can create a pass-through serializer that stores string values as-is by implementing
ILambdaSerializer and registering it at the host boundary:
using System.Text;
using Amazon.Lambda.Core;
// A pass-through serializer stores string values as-is (already a JSON string)
// instead of re-encoding them as a JSON string literal. .NET has no per-operation
// SerDes, so this behavior is expressed as a custom ILambdaSerializer registered
// on ILambdaContext.Serializer at the host boundary. It applies to every durable
// operation result in the handler.
public class PassThroughLambdaSerializer : ILambdaSerializer
{
public T Deserialize<T>(Stream requestStream)
{
using var reader = new StreamReader(requestStream);
string data = reader.ReadToEnd();
// Return the raw payload unchanged when T is string.
return (T)(object)data;
}
public void Serialize<T>(T response, Stream responseStream)
{
// Write the value as-is; the value is expected to already be a JSON string.
string data = (string)(object)response!;
var bytes = Encoding.UTF8.GetBytes(data);
responseStream.Write(bytes, 0, bytes.Length);
}
}
FileSystem serdes¶
The FileSystem serdes stores data on the filesystem and keeps a file pointer in the checkpoint. This can be useful when you do not want to store your data in the checkpoint itself, for example when your payload exceeds the durable execution checkpoint size limit. The persisted value lives on the filesystem and the SDK reads it on replay.
The FileSystem serdes requires a filesystem that persists across invocations and that every Lambda execution environment can read. In AWS Lambda, this means:
- Amazon Elastic File System (Amazon EFS). Serverless file system that scales automatically with your workloads.
- Amazon S3 Files. Serverless file system for mounting an Amazon S3 bucket. Amazon S3 Files provides access to Amazon S3 objects as files using standard file system operations such as read and write on the local mount path.
Do not use Lambda's /tmp directory
Lambda's /tmp filesystem is local to a single execution environment. A
different execution environment may run the next invocation, so the SDK cannot
find files written to /tmp on replay and deserialization fails. Always mount
a persistent, shared filesystem.
Pass the FileSystem serdes to a single operation through StepConfig,
CallbackConfig, MapConfig, or ParallelConfig. Other operations in the
same handler continue to use the default serdes.
import {
DurableContext,
withDurableExecution,
createFileSystemSerdes,
} from "@aws/durable-execution-sdk-js";
export const handler = withDurableExecution(
async (event: { customerId: string }, context: DurableContext) => {
const fsSerdes = createFileSystemSerdes("/mnt/s3");
// Pass the FileSystem serdes to one step. Other operations in this handler
// keep using the default JSON serdes.
const profile = await context.step(
"load-profile",
async () => loadLargeProfile(event.customerId),
{ serdes: fsSerdes },
);
return { name: profile.name };
},
);
declare function loadLargeProfile(
customerId: string,
): Promise<{ name: string }>;
Pass the FileSystem serdes to a single operation through StepConfig,
CallbackConfig, MapConfig, or ParallelConfig. Other operations in the
same handler continue to use the default serdes.
from aws_durable_execution_sdk_python import (
DurableContext,
StepContext,
durable_execution,
durable_step,
)
from aws_durable_execution_sdk_python.config import StepConfig
from aws_durable_execution_sdk_python.filesystem_serdes import FileSystemSerDes
@durable_step
def fetch_order(ctx: StepContext) -> dict:
return {"id": "order-123", "total": "99.99"}
@durable_execution
def handler(event: dict, context: DurableContext) -> dict:
fs_serdes = FileSystemSerDes("/mnt/s3")
# Pass the FileSystem serdes to one step. Other operations in this handler
# keep using the default serdes.
order = context.step(
fetch_order(),
config=StepConfig(serdes=fs_serdes),
)
return order
Coming soon. See aws-durable-execution-sdk-java#463.
Create a FileSystem serdes with NewFileSystemSerdes and pass it to one operation
with an option such as WithStepSerdes. The file holds the value as JSON. The
checkpoint holds an envelope with the full file path.
package main
import (
"github.com/aws/aws-durable-execution-sdk-go/durable"
)
type Event struct {
CustomerID string `json:"customerId"`
}
type Profile struct {
Name string `json:"name"`
}
func handler(ctx durable.Context, event Event) (Profile, error) {
fsSerdes := durable.NewFileSystemSerdes("/mnt/s3")
// Pass the FileSystem serdes to one step. Other operations in this handler
// keep using the default JSON serdes.
profile, err := durable.Step(ctx, "load-profile", func(durable.StepContext) (Profile, error) {
return loadLargeProfile(event.CustomerID)
}, durable.WithStepSerdes(fsSerdes))
if err != nil {
return Profile{}, err
}
return Profile{Name: profile.Name}, nil
}
func loadLargeProfile(customerID string) (Profile, error) {
return Profile{Name: "Customer " + customerID}, nil
}
func main() { durable.Start(handler) }
Not available. The .NET SDK has no FileSystem serdes. To keep large payloads out of the checkpoint, write them to a persistent store (such as Amazon S3) inside a step and checkpoint a pointer instead of the payload.
Create a FileSystem serdes¶
Create a FileSystem serdes and pass it to an operation's config.
createFileSystemSerdes(basePath, config?) returns a Serdes<unknown>.
import {
Serdes,
FileSystemSerdesConfig,
} from "@aws/durable-execution-sdk-js";
export type CreateFileSystemSerdes = (
basePath: string,
config?: FileSystemSerdesConfig,
) => Serdes<unknown>;
Parameters:
basePathDirectory where the SDK writes data files. Set this to your filesystem mount point.config(optional)FileSystemSerdesConfigcontrolling storage mode, path encoding, and preview generation.
Returns: A Serdes that reads and writes JSON files under basePath.
FileSystemSerDes(base_path, config?) is a SerDes subclass you instantiate.
from aws_durable_execution_sdk_python.filesystem_serdes import (
FileSystemSerDes,
FileSystemSerDesConfig,
)
from aws_durable_execution_sdk_python.serdes import SerDes
fs_serdes: SerDes = FileSystemSerDes("/mnt/s3")
Parameters:
base_pathDirectory where the SDK writes data files. Set this to your filesystem mount point.config(optional)FileSystemSerDesConfigcontrolling storage mode, path encoding, and preview generation.
Returns: A SerDes instance that reads and writes JSON files under base_path.
Coming soon.
NewFileSystemSerdes(basePath, cfg...) returns a Serdes. The config is a trailing
variadic argument. Omit it for the defaults. The SDK uses only the first config.
Parameters:
basePathDirectory where the SDK writes data files. Set this to your filesystem mount point.cfg(optional) AFileSystemSerdesConfigcontrolling storage mode, path encoding, and preview generation.
Returns: A Serdes that reads and writes JSON files under basePath.
Not available.
FileSystemSerdesConfig¶
import {
FileSystemSerdesMode,
FileSystemPathEncoding,
} from "@aws/durable-execution-sdk-js";
export interface FileSystemSerdesConfig {
storageMode?: FileSystemSerdesMode;
pathEncoding?: FileSystemPathEncoding;
generatePreview?: (value: unknown) => Record<string, unknown> | undefined;
}
Fields:
storageMode(optional) AFileSystemSerdesModevalue. Default:FileSystemSerdesMode.ALWAYS.pathEncoding(optional) AFileSystemPathEncodingvalue. Default:FileSystemPathEncoding.URI.generatePreview(optional) Function that returns a preview object. The SDK stores the preview inline in the checkpoint envelope alongside the file pointer. Use thebuildPreviewhelper or write your own.
from aws_durable_execution_sdk_python.filesystem_serdes import (
FileSystemSerDesConfig,
FileSystemSerDesMode,
FileSystemPathEncoding,
)
config = FileSystemSerDesConfig(
storage_mode=FileSystemSerDesMode.ALWAYS,
path_encoding=FileSystemPathEncoding.URI,
generate_preview=None,
)
Fields:
storage_mode(optional) AFileSystemSerDesModevalue. Default:FileSystemSerDesMode.ALWAYS.path_encoding(optional) AFileSystemPathEncodingvalue. Default:FileSystemPathEncoding.URI.generate_preview(optional) Callable that returns a preview dict orNone. The SDK stores the preview inline in the checkpoint envelope alongside the file pointer. Use thebuild_previewhelper or write your own.serdes(optional)SerDesinstance that serializes values before writing them to the filesystem. Default:ExtendedTypeSerDes.
Coming soon.
type FileSystemSerdesConfig struct {
// Mode controls when data is written to the filesystem. Default is
// FileSystemSerdesModeAlways.
Mode FileSystemSerdesMode
// PathEncoding controls the directory and file names of offloaded
// values. Default is FileSystemPathEncodingURI, the readable layout.
PathEncoding FileSystemPathEncoding
// GeneratePreview, when set, is called with each value that is written
// to a file, and its non-nil result is stored in the checkpoint
// envelope next to the file reference as the "preview" member.
GeneratePreview func(value any) map[string]any
// Has unexported fields.
}
Fields:
ModeAFileSystemSerdesModevalue. Default:FileSystemSerdesModeAlways.PathEncodingAFileSystemPathEncodingvalue. Default:FileSystemPathEncodingURI.GeneratePreviewA function that returns a preview map. The SDK stores the result inline in the checkpoint envelope next to the file reference. Use theBuildPreviewhelper or write your own.
The FileSystem serdes always encodes values with encoding/json and HTML escaping
disabled, as JSONSerdes does.
Not available.
Storage modes¶
The storageMode enumerated field controls when the SDK writes to the filesystem.
FileSystemSerdesMode.ALWAYS writes every value to a file. The checkpoint stores
only the file pointer.
FileSystemSerdesMode.OVERFLOW uses the standard durable execution checkpoint
store and only writes to the filesystem when the value would exceed the durable
execution checkpoint size limit. See
AWS Lambda service quotas.
import {
createFileSystemSerdes,
FileSystemSerdesMode,
} from "@aws/durable-execution-sdk-js";
// Small payloads stay inline in the checkpoint. The SDK only writes to the
// filesystem when the checkpoint envelope would exceed the size limit.
export const overflowFileSystemSerdes = createFileSystemSerdes("/mnt/s3", {
storageMode: FileSystemSerdesMode.OVERFLOW,
});
from aws_durable_execution_sdk_python.filesystem_serdes import (
FileSystemSerDes,
FileSystemSerDesConfig,
FileSystemSerDesMode,
)
# In OVERFLOW mode, small payloads stay inline in the checkpoint and no file
# is written. The SDK only writes to the filesystem when the serialized value
# exceeds the checkpoint size limit.
overflow_fs_serdes = FileSystemSerDes(
"/mnt/s3",
FileSystemSerDesConfig(storage_mode=FileSystemSerDesMode.OVERFLOW),
)
Coming soon.
In overflow mode, the FileSystem serdes writes the value to a file when the
inline envelope exceeds 255KB. GeneratePreview is not called for a value
stored inline.
package main
import (
"github.com/aws/aws-durable-execution-sdk-go/durable"
)
// Small payloads stay inline in the checkpoint. The SDK only writes to the
// filesystem when the checkpoint envelope would exceed the size limit.
var overflowFileSystemSerdes = durable.NewFileSystemSerdes("/mnt/s3", durable.FileSystemSerdesConfig{
Mode: durable.FileSystemSerdesModeOverflow,
})
Not available.
Path encoding¶
The pathEncoding field controls how the durable execution ARN and the entity ID
become the on-disk directory and file names.
FileSystemPathEncoding.URI builds a per-execution directory from the function
name, execution name, and invocation ID parsed from the ARN, and encodes the entity
ID with encodeURIComponent for the file name. Names stay readable when you read
files directly from the mount. A very long entity ID may exceed the filesystem's
per-name length limit, commonly 255 bytes.
FileSystemPathEncoding.HASH replaces the ARN and the entity ID with their SHA-256
hex digests. Names are a fixed 64 characters and are always filesystem-safe
regardless of the input characters or length. Choose HASH when entity IDs may
contain characters that are unsafe in a file name, such as /, or may be long
enough to exceed the name-length limit.
import {
createFileSystemSerdes,
FileSystemPathEncoding,
} from "@aws/durable-execution-sdk-js";
// Hash the ARN and the entity ID into fixed-length, filesystem-safe segments.
// Use HASH when entity IDs may contain unsafe characters or be very long.
export const hashedFileSystemSerdes = createFileSystemSerdes("/mnt/s3", {
pathEncoding: FileSystemPathEncoding.HASH,
});
from aws_durable_execution_sdk_python.filesystem_serdes import (
FileSystemSerDes,
FileSystemSerDesConfig,
FileSystemPathEncoding,
)
# Hash the ARN and the entity ID into fixed-length, filesystem-safe segments.
# Use HASH when entity IDs may contain unsafe characters or be very long.
hashed_fs_serdes = FileSystemSerDes(
"/mnt/s3",
FileSystemSerDesConfig(path_encoding=FileSystemPathEncoding.HASH),
)
Coming soon.
FileSystemPathEncodingURI, the default, writes
<functionName>/<executionName>/<invocationId>/<operationID>.json under the base
path. It takes the first three segments from the execution ARN. It percent-encodes
each segment, so bytes other than letters, digits, -, _, ., and ~ become
%XX.
FileSystemPathEncodingHash writes <arnHash>/<operationID>.json. arnHash is the
hex encoding of the first 16 bytes of the SHA-256 digest of the execution ARN, so it
is 32 characters long. FileSystemPathEncodingHash does not hash or encode the
operation ID. The SDK generates operation IDs such as 1 or 1-2-3, so the file
name is always safe.
The checkpoint envelope stores the full file path. So the SDK reads a value back correctly after you change the encoding.
Not available.
Preview and PII masking¶
When the FileSystem serdes writes to a file, the checkpoint envelope only contains
the file pointer, so the GetDurableExecution API and the AWS Console cannot show
the actual data for that operation. Configure generatePreview to embed a small
object inline so you can see the data without reading the file.
Use buildPreview to compute the preview from a PreviewConfig. The config
selects which fields to include, exclude, or mask. Masking replaces a field's value
with *** or a custom string. The preview object is capped at 4096 bytes by
default.
import {
createFileSystemSerdes,
buildPreview,
PreviewMode,
} from "@aws/durable-execution-sdk-js";
// The checkpoint envelope stores the file pointer plus a small preview object,
// so the console can show id and status without reading the full file. The
// email field is visible but masked, so PII does not land in the checkpoint.
export const previewFileSystemSerdes = createFileSystemSerdes("/mnt/s3", {
generatePreview: (value) =>
buildPreview(value, {
mode: PreviewMode.EXCLUDE_ALL,
include: [{ name: "id" }, { name: "status" }],
mask: [{ name: "email" }],
}),
});
PreviewConfig fields:
modeEitherPreviewMode.INCLUDE_ALLorPreviewMode.EXCLUDE_ALL. Sets the starting point before applyingexcludeandmask.include(optional) Fields to add when starting fromEXCLUDE_ALL.exclude(optional) Fields to remove. Always wins overmask.mask(optional) Fields whose values becomemaskString. A masked field is visible in the preview unless it is also excluded.maskString(optional) Replacement value for masked fields. Default:***.maxPreviewBytes(optional) Maximum size in bytes for the preview object when JSON-serialized. Default:4096.
Each PreviewField selector has a name and an optional match. Use
FieldMatchMode.ANYWHERE (default) to match the field name at any depth in
the object tree, or FieldMatchMode.PATH to match an exact dot-notation path
from the root.
Selectors cannot address field names that contain a dot because a dot is indistinguishable from a path separator. The preview merges array elements into a plain object at the array's path rather than preserving array structure.
from aws_durable_execution_sdk_python.filesystem_serdes import (
FileSystemSerDes,
FileSystemSerDesConfig,
)
from aws_durable_execution_sdk_python.preview import (
PreviewConfig,
PreviewField,
PreviewMode,
build_preview,
)
# The checkpoint envelope stores the file pointer plus a small preview object,
# so the console can show id and status without reading the full file. The
# email field is visible but masked, so PII does not land in the checkpoint.
preview_fs_serdes = FileSystemSerDes(
"/mnt/s3",
FileSystemSerDesConfig(
generate_preview=lambda value: build_preview(
value,
PreviewConfig(
mode=PreviewMode.EXCLUDE_ALL,
include=[PreviewField(name="id"), PreviewField(name="status")],
mask=[PreviewField(name="email")],
),
),
),
)
PreviewConfig fields:
modeEitherPreviewMode.INCLUDE_ALLorPreviewMode.EXCLUDE_ALL. Sets the starting point before applyingexcludeandmask.include(optional) Fields to add when starting fromEXCLUDE_ALL.exclude(optional) Fields to remove. Always wins overmask.mask(optional) Fields whose values becomemask_string. A masked field is visible in the preview unless it is also excluded.mask_string(optional) Replacement value for masked fields. Default:"***".max_preview_bytes(optional) Maximum size in bytes for the preview object when JSON-serialized. Default:4096.
Each PreviewField selector has a name and an optional match. Use
FieldMatchMode.ANYWHERE (default) to match the field name at any depth in
the object tree, or FieldMatchMode.PATH to match an exact dot-notation path
from the root.
Selectors cannot address field names that contain a dot because a dot is indistinguishable from a path separator. The preview merges array elements into a plain object at the array's path rather than preserving array structure.
Coming soon.
package main
import (
"github.com/aws/aws-durable-execution-sdk-go/durable"
)
// The checkpoint envelope stores the file pointer plus a small preview object,
// so the console can show id and status without reading the full file. The
// email field is visible but masked, so PII does not land in the checkpoint.
var previewFileSystemSerdes = durable.NewFileSystemSerdes("/mnt/s3", durable.FileSystemSerdesConfig{
GeneratePreview: func(value any) map[string]any {
return durable.BuildPreview(value, durable.PreviewConfig{
Mode: durable.PreviewExcludeAll,
Include: []durable.PreviewField{{Name: "id"}, {Name: "status"}},
Mask: []durable.PreviewField{{Name: "email"}},
})
},
})
PreviewConfig fields:
ModeEitherPreviewIncludeAllorPreviewExcludeAll. Sets the starting point beforeExcludeandMask. Default:PreviewIncludeAll.IncludeFields to add when starting fromPreviewExcludeAll.ExcludeFields to remove. Always wins overIncludeandMask.MaskFields whose values becomeMaskString. A masked field is visible under either mode unless it is also excluded.MaskStringReplacement for masked fields. Default:***.MaxPreviewBytesMaximum JSON size of the preview in bytes. Default:4096. When the cap leaves fields out, the preview carries the key$truncatedset totrue.MaxDepthHow many nested objects and slices are traversed. Default:32.
Each PreviewField has a Name and a Match. Use FieldMatchAnywhere, the
default, to match the field name at any depth, or FieldMatchPath for an exact
dot-path from the root. A field name that contains a dot cannot be selected. The
preview merges the fields of slice elements under the slice's path.
Not available.
Set as the default for the handler¶
When you want every step result, child-context result, invoke result, and
waitForCondition result in the handler to use the FileSystem serdes, configure it
once with configureSerdes.
import {
DurableContext,
withDurableExecution,
createFileSystemSerdes,
} from "@aws/durable-execution-sdk-js";
export const handler = withDurableExecution(
async (event: { customerId: string }, context: DurableContext) => {
// Configure once. Every step result, child-context result, invoke result,
// and waitForCondition result in this handler is now stored on /mnt/s3.
context.configureSerdes({
defaultSerdes: createFileSystemSerdes("/mnt/s3"),
});
const profile = await context.step("load-profile", async () => {
return loadLargeProfile(event.customerId);
});
return { name: profile.name };
},
);
declare function loadLargeProfile(
customerId: string,
): Promise<{ name: string }>;
Create a single FileSystemSerDes instance and a shared StepConfig, then
pass it to every operation in the handler.
from aws_durable_execution_sdk_python import (
DurableContext,
StepContext,
durable_execution,
durable_step,
)
from aws_durable_execution_sdk_python.config import StepConfig
from aws_durable_execution_sdk_python.filesystem_serdes import FileSystemSerDes
@durable_step
def fetch_order(ctx: StepContext) -> dict:
return {"id": "order-123", "total": "99.99"}
@durable_step
def fetch_preferences(ctx: StepContext) -> dict:
return {"theme": "dark", "language": "en"}
@durable_execution
def handler(event: dict, context: DurableContext) -> dict:
# Create once and pass to every operation in the handler.
fs_serdes = FileSystemSerDes("/mnt/s3")
config = StepConfig(serdes=fs_serdes)
order = context.step(fetch_order(), config=config)
preferences = context.step(fetch_preferences(), config=config)
return {"order": order, "preferences": preferences}
Coming soon. See aws-durable-execution-sdk-java#463.
Set the FileSystem serdes handler-wide from inside the handler with
ConfigureSerdes, or at construction with durable.Start(handler, WithSerdes(...)).
ConfigureSerdes must run identically on every invocation, including replays. The
handler serdes also serves Map and Parallel item results. It does not apply to
callback payloads.
package main
import (
"github.com/aws/aws-durable-execution-sdk-go/durable"
)
type Event struct {
CustomerID string `json:"customerId"`
}
type Profile struct {
Name string `json:"name"`
}
func handler(ctx durable.Context, event Event) (Profile, error) {
// Configure once. Every step result, child-context result, invoke result,
// and WaitForCondition result in this handler is now stored on /mnt/s3.
if err := durable.ConfigureSerdes(ctx, durable.SerdesConfig{
Serdes: durable.NewFileSystemSerdes("/mnt/s3"),
}); err != nil {
return Profile{}, err
}
profile, err := durable.Step(ctx, "load-profile", func(durable.StepContext) (Profile, error) {
return loadLargeProfile(event.CustomerID)
})
if err != nil {
return Profile{}, err
}
return Profile{Name: profile.Name}, nil
}
func loadLargeProfile(customerID string) (Profile, error) {
return Profile{Name: "Customer " + customerID}, nil
}
func main() { durable.Start(handler) }
Not available. The single ILambdaSerializer you register at the host boundary is
already applied to every durable operation result in the handler.
Serialization errors¶
When serialization or deserialization fails, each SDK raises or throws a specific exception type. See Serialization errors for the exception hierarchy and how to handle serialization failures.
See also¶
- StepConfig Step serialization configuration
- CallbackConfig Callback serialization configuration
- MapConfig Map serialization configuration
- Serialization errors Serialization error types