Skip to content
Draft
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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,6 @@ global.json

**/cdk.out/**
**/.DS_Store

# JetBrains Rider per-project cache
**/*.lscache
329 changes: 265 additions & 64 deletions Docs/durable-execution-design.md

Large diffs are not rendered by default.

62 changes: 61 additions & 1 deletion Libraries/Libraries.sln
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 18
VisualStudioVersion = 18.5.11709.299 stable
VisualStudioVersion = 18.5.11709.299
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{AAB54E74-20B1-42ED-BC3D-CE9F7BC7FD12}"
EndProject
Expand Down Expand Up @@ -155,6 +155,14 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ResponseStreamingFunctionHa
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AspNetCoreStreamingApiGatewayTest", "test\Amazon.Lambda.RuntimeSupport.Tests\AspNetCoreStreamingApiGatewayTest\AspNetCoreStreamingApiGatewayTest.csproj", "{0768FA72-CF49-2B59-BC4C-E4CE579E5D93}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Amazon.Lambda.DurableExecution", "src\Amazon.Lambda.DurableExecution\Amazon.Lambda.DurableExecution.csproj", "{9097B5A4-E100-47FD-A676-0B666A36FAFF}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Amazon.Lambda.DurableExecution.Tests", "test\Amazon.Lambda.DurableExecution.Tests\Amazon.Lambda.DurableExecution.Tests.csproj", "{57150BA6-3826-431F-8F58-B1D11FAFC5D4}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Amazon.Lambda.DurableExecution.IntegrationTests", "test\Amazon.Lambda.DurableExecution.IntegrationTests\Amazon.Lambda.DurableExecution.IntegrationTests.csproj", "{CA132CAB-FF4F-4312-B3A3-66DE9D360F27}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Amazon.Lambda.DurableExecution.AotPublishTest", "test\Amazon.Lambda.DurableExecution.AotPublishTest\Amazon.Lambda.DurableExecution.AotPublishTest.csproj", "{16B1B1CC-3AFC-4DC7-8DB6-D14AE12924A2}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -969,6 +977,54 @@ Global
{0768FA72-CF49-2B59-BC4C-E4CE579E5D93}.Release|x64.Build.0 = Release|Any CPU
{0768FA72-CF49-2B59-BC4C-E4CE579E5D93}.Release|x86.ActiveCfg = Release|Any CPU
{0768FA72-CF49-2B59-BC4C-E4CE579E5D93}.Release|x86.Build.0 = Release|Any CPU
{9097B5A4-E100-47FD-A676-0B666A36FAFF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9097B5A4-E100-47FD-A676-0B666A36FAFF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9097B5A4-E100-47FD-A676-0B666A36FAFF}.Debug|x64.ActiveCfg = Debug|Any CPU
{9097B5A4-E100-47FD-A676-0B666A36FAFF}.Debug|x64.Build.0 = Debug|Any CPU
{9097B5A4-E100-47FD-A676-0B666A36FAFF}.Debug|x86.ActiveCfg = Debug|Any CPU
{9097B5A4-E100-47FD-A676-0B666A36FAFF}.Debug|x86.Build.0 = Debug|Any CPU
{9097B5A4-E100-47FD-A676-0B666A36FAFF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9097B5A4-E100-47FD-A676-0B666A36FAFF}.Release|Any CPU.Build.0 = Release|Any CPU
{9097B5A4-E100-47FD-A676-0B666A36FAFF}.Release|x64.ActiveCfg = Release|Any CPU
{9097B5A4-E100-47FD-A676-0B666A36FAFF}.Release|x64.Build.0 = Release|Any CPU
{9097B5A4-E100-47FD-A676-0B666A36FAFF}.Release|x86.ActiveCfg = Release|Any CPU
{9097B5A4-E100-47FD-A676-0B666A36FAFF}.Release|x86.Build.0 = Release|Any CPU
{57150BA6-3826-431F-8F58-B1D11FAFC5D4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{57150BA6-3826-431F-8F58-B1D11FAFC5D4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{57150BA6-3826-431F-8F58-B1D11FAFC5D4}.Debug|x64.ActiveCfg = Debug|Any CPU
{57150BA6-3826-431F-8F58-B1D11FAFC5D4}.Debug|x64.Build.0 = Debug|Any CPU
{57150BA6-3826-431F-8F58-B1D11FAFC5D4}.Debug|x86.ActiveCfg = Debug|Any CPU
{57150BA6-3826-431F-8F58-B1D11FAFC5D4}.Debug|x86.Build.0 = Debug|Any CPU
{57150BA6-3826-431F-8F58-B1D11FAFC5D4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{57150BA6-3826-431F-8F58-B1D11FAFC5D4}.Release|Any CPU.Build.0 = Release|Any CPU
{57150BA6-3826-431F-8F58-B1D11FAFC5D4}.Release|x64.ActiveCfg = Release|Any CPU
{57150BA6-3826-431F-8F58-B1D11FAFC5D4}.Release|x64.Build.0 = Release|Any CPU
{57150BA6-3826-431F-8F58-B1D11FAFC5D4}.Release|x86.ActiveCfg = Release|Any CPU
{57150BA6-3826-431F-8F58-B1D11FAFC5D4}.Release|x86.Build.0 = Release|Any CPU
{CA132CAB-FF4F-4312-B3A3-66DE9D360F27}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CA132CAB-FF4F-4312-B3A3-66DE9D360F27}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CA132CAB-FF4F-4312-B3A3-66DE9D360F27}.Debug|x64.ActiveCfg = Debug|Any CPU
{CA132CAB-FF4F-4312-B3A3-66DE9D360F27}.Debug|x64.Build.0 = Debug|Any CPU
{CA132CAB-FF4F-4312-B3A3-66DE9D360F27}.Debug|x86.ActiveCfg = Debug|Any CPU
{CA132CAB-FF4F-4312-B3A3-66DE9D360F27}.Debug|x86.Build.0 = Debug|Any CPU
{CA132CAB-FF4F-4312-B3A3-66DE9D360F27}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CA132CAB-FF4F-4312-B3A3-66DE9D360F27}.Release|Any CPU.Build.0 = Release|Any CPU
{CA132CAB-FF4F-4312-B3A3-66DE9D360F27}.Release|x64.ActiveCfg = Release|Any CPU
{CA132CAB-FF4F-4312-B3A3-66DE9D360F27}.Release|x64.Build.0 = Release|Any CPU
{CA132CAB-FF4F-4312-B3A3-66DE9D360F27}.Release|x86.ActiveCfg = Release|Any CPU
{CA132CAB-FF4F-4312-B3A3-66DE9D360F27}.Release|x86.Build.0 = Release|Any CPU
{16B1B1CC-3AFC-4DC7-8DB6-D14AE12924A2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{16B1B1CC-3AFC-4DC7-8DB6-D14AE12924A2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{16B1B1CC-3AFC-4DC7-8DB6-D14AE12924A2}.Debug|x64.ActiveCfg = Debug|Any CPU
{16B1B1CC-3AFC-4DC7-8DB6-D14AE12924A2}.Debug|x64.Build.0 = Debug|Any CPU
{16B1B1CC-3AFC-4DC7-8DB6-D14AE12924A2}.Debug|x86.ActiveCfg = Debug|Any CPU
{16B1B1CC-3AFC-4DC7-8DB6-D14AE12924A2}.Debug|x86.Build.0 = Debug|Any CPU
{16B1B1CC-3AFC-4DC7-8DB6-D14AE12924A2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{16B1B1CC-3AFC-4DC7-8DB6-D14AE12924A2}.Release|Any CPU.Build.0 = Release|Any CPU
{16B1B1CC-3AFC-4DC7-8DB6-D14AE12924A2}.Release|x64.ActiveCfg = Release|Any CPU
{16B1B1CC-3AFC-4DC7-8DB6-D14AE12924A2}.Release|x64.Build.0 = Release|Any CPU
{16B1B1CC-3AFC-4DC7-8DB6-D14AE12924A2}.Release|x86.ActiveCfg = Release|Any CPU
{16B1B1CC-3AFC-4DC7-8DB6-D14AE12924A2}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -1045,6 +1101,10 @@ Global
{80594C21-C6EB-469E-83CC-68F9F661CA5E} = {1DE4EE60-45BA-4EF7-BE00-B9EB861E4C69}
{E404A7AC-812B-BC03-CA76-02C0BC2BA7F9} = {B5BD0336-7D08-492C-8489-42C987E29B39}
{0768FA72-CF49-2B59-BC4C-E4CE579E5D93} = {B5BD0336-7D08-492C-8489-42C987E29B39}
{9097B5A4-E100-47FD-A676-0B666A36FAFF} = {AAB54E74-20B1-42ED-BC3D-CE9F7BC7FD12}
{57150BA6-3826-431F-8F58-B1D11FAFC5D4} = {1DE4EE60-45BA-4EF7-BE00-B9EB861E4C69}
{CA132CAB-FF4F-4312-B3A3-66DE9D360F27} = {1DE4EE60-45BA-4EF7-BE00-B9EB861E4C69}
{16B1B1CC-3AFC-4DC7-8DB6-D14AE12924A2} = {1DE4EE60-45BA-4EF7-BE00-B9EB861E4C69}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {503678A4-B8D1-4486-8915-405A3E9CF0EB}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@
<EnableTrimAnalyzer>true</EnableTrimAnalyzer>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<WarningsAsErrors>IL2026,IL2067,IL2075,IL3050</WarningsAsErrors>
<!-- DurableExecution intentionally consumes the preview ILambdaContext.Serializer
API. The whole package is in development (0.x), so suppressing project-wide
is appropriate; downstream users still see AWSLAMBDA001 in their own code. -->
<NoWarn>$(NoWarn);AWSLAMBDA001</NoWarn>
</PropertyGroup>

<ItemGroup>
Expand Down

This file was deleted.

30 changes: 30 additions & 0 deletions Libraries/src/Amazon.Lambda.DurableExecution/BatchItemStatus.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
namespace Amazon.Lambda.DurableExecution;

/// <summary>
/// Status of an individual item in a <see cref="IBatchResult{T}"/>.
/// </summary>
/// <remarks>
/// Mirrors the wire-state of the per-branch checkpoint at the moment the batch
/// resolved. Items that finished produce <see cref="Succeeded"/> or
/// <see cref="Failed"/>; items still in flight when the batch's
/// <see cref="CompletionConfig"/> short-circuits remain in <see cref="Started"/>.
/// </remarks>
public enum BatchItemStatus
{
/// <summary>
/// The branch ran to completion and produced a result.
/// </summary>
Succeeded,

/// <summary>
/// The branch ran to completion and threw.
/// </summary>
Failed,

/// <summary>
/// The branch was still in flight when the batch's <see cref="CompletionConfig"/>
/// resolved (e.g., <see cref="CompletionConfig.FirstSuccessful"/> returned
/// before this branch finished).
/// </summary>
Started
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
namespace Amazon.Lambda.DurableExecution;

/// <summary>
/// Configuration for a child context.
/// </summary>
/// <remarks>
/// A child context is a logical sub-workflow with its own deterministic
/// operation-ID space, persisted as a <c>CONTEXT</c> operation. Use
/// <see cref="IDurableContext.RunInChildContextAsync{T}(System.Func{IDurableContext, System.Threading.Tasks.Task{T}}, string?, ChildContextConfig?, System.Threading.CancellationToken)"/>
/// (and overloads) to run code inside one.
/// </remarks>
public sealed class ChildContextConfig
{
/// <summary>
/// Operation sub-type label for observability (e.g. <c>"WaitForCallback"</c>).
/// Surfaces on the wire <c>OperationUpdate.SubType</c> field.
/// </summary>
public string? SubType { get; set; }

/// <summary>
/// Optional function to transform exceptions thrown by the child context's
/// user function before they surface to the caller. Useful for wrapping
/// low-level errors into domain-specific exceptions.
/// </summary>
/// <remarks>
/// Applied when the user function throws (the mapped exception propagates
/// to the caller of <c>RunInChildContextAsync</c>) and on replay of a
/// <c>FAILED</c> child context (the constructed
/// <see cref="ChildContextException"/> is mapped before being thrown).
/// </remarks>
public Func<Exception, Exception>? ErrorMapping { get; set; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
namespace Amazon.Lambda.DurableExecution;

/// <summary>
/// Defines completion criteria for parallel/map operations.
/// </summary>
/// <remarks>
/// Construct via the static factories (<see cref="AllSuccessful"/>,
/// <see cref="AllCompleted"/>, <see cref="FirstSuccessful"/>) or set the
/// individual properties directly. Multiple criteria combine: the operation
/// resolves as soon as any criterion is met (success short-circuit) or violated
/// (failure short-circuit).
/// </remarks>
public sealed class CompletionConfig
{
private double? _toleratedFailurePercentage;

/// <summary>
/// Minimum number of <see cref="BatchItemStatus.Succeeded"/> items required
/// before the operation resolves successfully. <c>null</c> = no minimum.
/// </summary>
public int? MinSuccessful { get; set; }

/// <summary>
/// Maximum tolerated <see cref="BatchItemStatus.Failed"/> count. When the
/// failure count <i>strictly exceeds</i> this value, the operation resolves
/// with <see cref="CompletionReason.FailureToleranceExceeded"/>.
/// <c>null</c> = no count-based failure threshold.
/// </summary>
public int? ToleratedFailureCount { get; set; }

/// <summary>
/// Maximum tolerated failure ratio, expressed as a value in the range
/// <c>0.0</c> to <c>1.0</c> (inclusive). For example, <c>0.25</c> means
/// "tolerate up to 25% failures; fail when the failure ratio strictly
/// exceeds 25%". <c>null</c> = no ratio-based failure threshold.
/// </summary>
/// <exception cref="System.ArgumentOutOfRangeException">
/// Thrown by the setter if the value is outside <c>[0.0, 1.0]</c>.
/// </exception>
public double? ToleratedFailurePercentage
{
get => _toleratedFailurePercentage;
set
{
if (value is { } v && (v < 0.0 || v > 1.0))
{
throw new ArgumentOutOfRangeException(nameof(value), v,
"ToleratedFailurePercentage must be a ratio in [0.0, 1.0].");
}
_toleratedFailurePercentage = value;
}
}

/// <summary>
/// All items must succeed. Equivalent to
/// <see cref="ToleratedFailureCount"/> = 0. The default for
/// <see cref="ParallelConfig.CompletionConfig"/>.
/// </summary>
public static CompletionConfig AllSuccessful() => new() { ToleratedFailureCount = 0 };

/// <summary>
/// Run every branch regardless of failures; surface failures per-item via
/// <see cref="IBatchResult{T}.Failed"/>. Resolution does not auto-throw —
/// the caller can inspect the result and call
/// <see cref="IBatchResult{T}.ThrowIfError"/> if they want strict-success
/// behavior.
/// </summary>
public static CompletionConfig AllCompleted() => new();

/// <summary>
/// Resolve as soon as one branch succeeds. Remaining in-flight branches are
/// reported as <see cref="BatchItemStatus.Started"/>.
/// </summary>
public static CompletionConfig FirstSuccessful() => new() { MinSuccessful = 1 };
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
namespace Amazon.Lambda.DurableExecution;

/// <summary>
/// Why a batch operation (<see cref="IDurableContext.ParallelAsync{T}(IReadOnlyList{System.Func{IDurableContext, System.Threading.Tasks.Task{T}}}, string?, ParallelConfig?, System.Threading.CancellationToken)"/>
/// or future Map) resolved.
/// </summary>
public enum CompletionReason
{
/// <summary>
/// Every branch finished — no <see cref="CompletionConfig"/> short-circuit
/// was triggered. Branches may be a mix of <see cref="BatchItemStatus.Succeeded"/>
/// and <see cref="BatchItemStatus.Failed"/>.
/// </summary>
AllCompleted,

/// <summary>
/// <see cref="CompletionConfig.MinSuccessful"/> branches succeeded; remaining
/// branches were left in <see cref="BatchItemStatus.Started"/>.
/// </summary>
MinSuccessfulReached,

/// <summary>
/// <see cref="CompletionConfig.ToleratedFailureCount"/> or
/// <see cref="CompletionConfig.ToleratedFailurePercentage"/> was exceeded.
/// The batch is considered failed and surfaces a
/// <see cref="ParallelException"/> when awaited.
/// </summary>
FailureToleranceExceeded
}
13 changes: 13 additions & 0 deletions Libraries/src/Amazon.Lambda.DurableExecution/DurableBranch.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
namespace Amazon.Lambda.DurableExecution;

/// <summary>
/// A named branch for
/// <see cref="IDurableContext.ParallelAsync{T}(IReadOnlyList{DurableBranch{T}}, string?, ParallelConfig?, System.Threading.CancellationToken)"/>.
/// Names appear in execution traces and on the wire <c>OperationUpdate.Name</c>
/// field, and surface on <see cref="IBatchItem{T}.Name"/>.
/// </summary>
/// <typeparam name="T">The branch's result type.</typeparam>
/// <param name="Name">Human-readable branch name. Required.</param>
/// <param name="Func">The user function executed inside the branch's
/// child context.</param>
public sealed record DurableBranch<T>(string Name, Func<IDurableContext, Task<T>> Func);
Loading
Loading