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
Implements the minimum viable slice of the Amazon.Lambda.DurableExecution
SDK: a workflow can run StepAsync and WaitAsync against a real Lambda,
with replay-aware checkpointing wired through to the AWS service.
Public API surface introduced:
- DurableFunction.WrapAsync — entry point that handles the durable
execution envelope (input hydration, output construction, status mapping)
- IDurableContext.StepAsync / WaitAsync (4 Step overloads, 1 Wait)
- StepConfig with serializer hook (retry deferred to follow-up PR)
- ICheckpointSerializer interface
- [DurableExecution] attribute (recognized by future source generator)
- DurableExecutionException base + StepException
Internals:
- DurableExecutionHandler — Task.WhenAny race between user code and
the suspension signal, returning Succeeded/Failed/Pending
- ExecutionState — replay-aware operation lookup and pending checkpoint
buffer
- OperationIdGenerator — deterministic, replay-stable IDs
- TerminationManager — TaskCompletionSource-based suspension trigger
- LambdaDurableServiceClient — wraps AWSSDK.Lambda's checkpoint and
state APIs
Tests:
- 86 unit tests covering enums, exceptions, models, configs,
ID generation, termination, execution state, the handler race,
the context (Step + Wait paths), and the WrapAsync entry point
- 8 end-to-end integration tests deploying real Lambdas via Docker on
the provided.al2023 runtime: StepWaitStep, MultipleSteps, WaitOnly,
LongerWait, ReplayDeterminism, RetrySucceeds, RetryExhausts, StepFails
Out of scope (follow-up PRs):
- IRetryStrategy, ExponentialRetryStrategy, retry decision factories
- DefaultJsonCheckpointSerializer
- DurableLogger replay-suppression (currently returns NullLogger)
- Callbacks, InvokeAsync, ParallelAsync, MapAsync, RunInChildContextAsync,
WaitForConditionAsync — interface intentionally does not declare them
- Annotations source-generator integration
- DurableTestRunner / Amazon.Lambda.DurableExecution.Testing package
- dotnet new lambda.DurableFunction blueprint
stack-info: PR: #2360, branch: GarrettBeatty/stack/2
@@ -968,6 +1079,24 @@ public interface IDurableContext
968
1079
StepConfig? config=null,
969
1080
CancellationTokencancellationToken=default);
970
1081
1082
+
/// <summary>
1083
+
/// Execute a step (simplified overload without IStepContext).
1084
+
/// </summary>
1085
+
Task<T> StepAsync<T>(
1086
+
Func<Task<T>> func,
1087
+
string? name=null,
1088
+
StepConfig? config=null,
1089
+
CancellationTokencancellationToken=default);
1090
+
1091
+
/// <summary>
1092
+
/// Execute a step that returns no value (simplified overload).
1093
+
/// </summary>
1094
+
TaskStepAsync(
1095
+
Func<Task> func,
1096
+
string? name=null,
1097
+
StepConfig? config=null,
1098
+
CancellationTokencancellationToken=default);
1099
+
971
1100
/// <summary>
972
1101
/// Suspend execution for the specified duration.
973
1102
/// Throws ArgumentOutOfRangeException if duration is less than 1 second.
@@ -1579,13 +1708,29 @@ Both approaches produce a self-contained executable that the Lambda custom runti
1579
1708
1580
1709
### NativeAOT compatibility
1581
1710
1582
-
The SDK is AOT-friendly but does not require AOT. The default JSON serialization uses reflection (standard `System.Text.Json` behavior), which works in JIT mode. For NativeAOT deployments, provide a `JsonSerializerContext` via the `ICheckpointSerializer<T>` interface — this avoids all runtime reflection and is fully trim-safe. The SDK itself avoids `Activator.CreateInstance`, `Type.GetType()`, and other reflection patterns, and uses `[DynamicallyAccessedMembers]` trimming annotations where needed.
1711
+
The SDK is AOT-friendly but does not require AOT. The default JSON serialization uses reflection (standard `System.Text.Json` behavior), which works in JIT mode. For NativeAOT deployments, AOT safety is addressed at two levels:
1712
+
1713
+
1.**Entry point (`DurableFunction.WrapAsync`)** — pass a `JsonSerializerContext` that includes type info for your `TInput` and `TOutput` types. The reflection-based overloads are annotated with `[RequiresUnreferencedCode]` / `[RequiresDynamicCode]` so the trimmer will warn if you use them in AOT builds.
1714
+
1715
+
2.**Step checkpoints (`StepConfig.Serializer`)** — for custom or complex types checkpointed by `StepAsync`, provide an `ICheckpointSerializer` via `StepConfig` (e.g., `JsonCheckpointSerializer<T>` backed by a source-generated `JsonTypeInfo<T>`).
1716
+
1717
+
The SDK itself avoids `Activator.CreateInstance`, `Type.GetType()`, and other reflection patterns, and uses `[DynamicallyAccessedMembers]` trimming annotations where needed.
When no `LambdaClientFactory` is specified, the generated code creates a default `AmazonLambdaClient`. For the manual handler path, pass the client directly to `DurableExecutionHandler.RunAsync`.
1849
+
When no `LambdaClientFactory` is specified, the generated code creates a default `AmazonLambdaClient`. For the manual handler path (`DurableFunction.WrapAsync`), pass the client directly via the `IAmazonLambda lambdaClient` overload.
1705
1850
1706
1851
> **Dependency boundaries:**`Amazon.Lambda.Annotations` has **no dependency** on the AWS SDK or on `Amazon.Lambda.DurableExecution`. The Annotations source generator references durable execution types by fully-qualified name strings only — it never takes a compile-time dependency on the durable package. The `[DurableExecution]` attribute is defined in `Amazon.Lambda.DurableExecution`, and the generated code resolves against the user's project references. There is only one source generator (Annotations) — no coordination between multiple generators is needed.
0 commit comments