diff --git a/Docs/durable-execution-design.md b/Docs/durable-execution-design.md index 6df424c5f..402d689af 100644 --- a/Docs/durable-execution-design.md +++ b/Docs/durable-execution-design.md @@ -1279,10 +1279,9 @@ public class CallbackConfig /// public TimeSpan HeartbeatTimeout { get; set; } = TimeSpan.Zero; - /// - /// Custom serializer for callback result. - /// - public ICheckpointSerializer? Serializer { get; set; } + // Note: there is no Serializer property here. Custom serializers are + // supplied via the AOT-safe CreateCallbackAsync(..., ICheckpointSerializer, ...) + // overload, matching the pattern established by StepAsync. } /// @@ -1307,14 +1306,14 @@ public class InvokeConfig public TimeSpan Timeout { get; set; } = TimeSpan.Zero; /// - /// Custom serializer for the payload. + /// Optional tenant identifier propagated to the chained invocation. + /// Matches the tenantId field on Python/JS/Java InvokeConfig. /// - public ICheckpointSerializer? PayloadSerializer { get; set; } + public string? TenantId { get; set; } - /// - /// Custom serializer for the result. - /// - public ICheckpointSerializer? ResultSerializer { get; set; } + // Note: payload and result serializers are supplied via the AOT-safe + // InvokeAsync(..., ICheckpointSerializer, ICheckpointSerializer, ...) + // overload, matching the pattern established by StepAsync. } /// @@ -1429,10 +1428,9 @@ public class CompletionConfig /// public class ChildContextConfig { - /// - /// Custom serializer for the child context's return value. - /// - public ICheckpointSerializer? Serializer { get; set; } + // Note: there is no Serializer property here. Custom serializers are + // supplied via the AOT-safe RunInChildContextAsync(..., ICheckpointSerializer, ...) + // overload, matching the pattern established by StepAsync. /// /// Operation sub-type label for observability (e.g., in test runner output). @@ -1473,34 +1471,54 @@ public class WaitForConditionConfig public interface IBatchResult { /// - /// All items (succeeded and failed). + /// All items, in original index order. /// IReadOnlyList> All { get; } /// - /// Only successful items. + /// Items whose Status is Succeeded. /// IReadOnlyList> Succeeded { get; } /// - /// Only failed items. + /// Items whose Status is Failed. /// IReadOnlyList> Failed { get; } /// - /// Get all successful results. Throws if any failed. + /// Items still in flight when the batch resolved (CompletionConfig short-circuit). + /// + IReadOnlyList> Started { get; } + + /// + /// Get all successful results in original index order. Throws if any failed. /// IReadOnlyList GetResults(); /// - /// Throw an exception if any item failed. + /// Get all errors from failed items. + /// + IReadOnlyList GetErrors(); + + /// + /// Throw a single aggregated exception if any item failed. /// void ThrowIfError(); /// - /// Why the operation completed. + /// True if any item is in the Failed state. + /// + bool HasFailure { get; } + + /// + /// Why the batch resolved. /// CompletionReason CompletionReason { get; } + + int SuccessCount { get; } + int FailureCount { get; } + int StartedCount { get; } + int TotalCount { get; } } public interface IBatchItem @@ -1511,7 +1529,29 @@ public interface IBatchItem DurableExecutionException? Error { get; } } -public enum BatchItemStatus { Succeeded, Failed, Cancelled } +/// +/// Status of an individual item in a batch result. +/// Mirrors the wire-state observed at the time the batch resolved — items still +/// running when a CompletionConfig short-circuits remain in . +/// +public enum BatchItemStatus +{ + /// + /// The branch ran to completion and produced a result. + /// + Succeeded, + + /// + /// The branch ran to completion and threw. + /// + Failed, + + /// + /// The branch was still in flight when the batch's CompletionConfig + /// resolved (e.g., FirstSuccessful returned before this branch finished). + /// + Started +} public enum CompletionReason { AllCompleted, MinSuccessfulReached, FailureToleranceExceeded } /// diff --git a/Libraries/src/Amazon.Lambda.DurableExecution/Internal/Operation.cs b/Libraries/src/Amazon.Lambda.DurableExecution/Internal/Operation.cs index 473c7a3b2..3befbf7d8 100644 --- a/Libraries/src/Amazon.Lambda.DurableExecution/Internal/Operation.cs +++ b/Libraries/src/Amazon.Lambda.DurableExecution/Internal/Operation.cs @@ -137,4 +137,25 @@ internal static class OperationStatuses public const string Cancelled = "CANCELLED"; public const string Ready = "READY"; public const string Stopped = "STOPPED"; + public const string TimedOut = "TIMED_OUT"; +} + +/// +/// Wire-format string constants. Subtypes are +/// observability labels mapped from the user-facing context method that +/// produced the operation. The service does not interpret them; downstream +/// consumers (test runner, traces, console) display them as-is. +/// +internal static class OperationSubTypes +{ + public const string Step = "Step"; + public const string Wait = "Wait"; + public const string Callback = "Callback"; + public const string WaitForCallback = "WaitForCallback"; + public const string Invoke = "Invoke"; + public const string WaitForCondition = "WaitForCondition"; + public const string Parallel = "Parallel"; + public const string ParallelBranch = "ParallelBranch"; + public const string Map = "Map"; + public const string MapIteration = "MapIteration"; } diff --git a/Libraries/src/Amazon.Lambda.DurableExecution/Internal/StepOperation.cs b/Libraries/src/Amazon.Lambda.DurableExecution/Internal/StepOperation.cs index 54e52005d..2a0182d7e 100644 --- a/Libraries/src/Amazon.Lambda.DurableExecution/Internal/StepOperation.cs +++ b/Libraries/src/Amazon.Lambda.DurableExecution/Internal/StepOperation.cs @@ -171,7 +171,7 @@ private async Task ExecuteFunc(int attemptNumber, CancellationToken cancellat Id = OperationId, Type = OperationTypes.Step, Action = "START", - SubType = "Step", + SubType = OperationSubTypes.Step, Name = Name }; @@ -196,7 +196,7 @@ await EnqueueAsync(new SdkOperationUpdate Id = OperationId, Type = OperationTypes.Step, Action = "SUCCEED", - SubType = "Step", + SubType = OperationSubTypes.Step, Name = Name, Payload = SerializeResult(result) }, cancellationToken); @@ -233,7 +233,7 @@ await EnqueueAsync(new SdkOperationUpdate Id = OperationId, Type = OperationTypes.Step, Action = "RETRY", - SubType = "Step", + SubType = OperationSubTypes.Step, Name = Name, Error = ToSdkError(ex), StepOptions = new SdkStepOptions { NextAttemptDelaySeconds = delaySeconds } @@ -248,7 +248,7 @@ await EnqueueAsync(new SdkOperationUpdate Id = OperationId, Type = OperationTypes.Step, Action = "FAIL", - SubType = "Step", + SubType = OperationSubTypes.Step, Name = Name, Error = ToSdkError(ex) }, cancellationToken); diff --git a/Libraries/src/Amazon.Lambda.DurableExecution/Internal/WaitOperation.cs b/Libraries/src/Amazon.Lambda.DurableExecution/Internal/WaitOperation.cs index 59254827d..364ab05c3 100644 --- a/Libraries/src/Amazon.Lambda.DurableExecution/Internal/WaitOperation.cs +++ b/Libraries/src/Amazon.Lambda.DurableExecution/Internal/WaitOperation.cs @@ -48,7 +48,7 @@ await EnqueueAsync(new SdkOperationUpdate Id = OperationId, Type = OperationTypes.Wait, Action = "START", - SubType = "Wait", + SubType = OperationSubTypes.Wait, Name = Name, WaitOptions = new SdkWaitOptions { WaitSeconds = _waitSeconds } }, cancellationToken);