Add ParallelAsync for concurrent branch execution (DOTNET-8662)#2375
Draft
GarrettBeatty wants to merge 1 commit into
Draft
Add ParallelAsync for concurrent branch execution (DOTNET-8662)#2375GarrettBeatty wants to merge 1 commit into
GarrettBeatty wants to merge 1 commit into
Conversation
|
|
||
| COPY bin/publish/ ${LAMBDA_TASK_ROOT} | ||
|
|
||
| ENTRYPOINT ["/var/task/bootstrap"] |
|
|
||
| COPY bin/publish/ ${LAMBDA_TASK_ROOT} | ||
|
|
||
| ENTRYPOINT ["/var/task/bootstrap"] |
|
|
||
| COPY bin/publish/ ${LAMBDA_TASK_ROOT} | ||
|
|
||
| ENTRYPOINT ["/var/task/bootstrap"] |
|
|
||
| COPY bin/publish/ ${LAMBDA_TASK_ROOT} | ||
|
|
||
| ENTRYPOINT ["/var/task/bootstrap"] |
|
|
||
| COPY bin/publish/ ${LAMBDA_TASK_ROOT} | ||
|
|
||
| ENTRYPOINT ["/var/task/bootstrap"] |
|
|
||
| COPY bin/publish/ ${LAMBDA_TASK_ROOT} | ||
|
|
||
| ENTRYPOINT ["/var/task/bootstrap"] |
19c0128 to
fa13eef
Compare
464c591 to
d308c3b
Compare
fa13eef to
b7a06b4
Compare
d308c3b to
be4c3ad
Compare
b7a06b4 to
08b2095
Compare
ad4d208 to
3acbed5
Compare
Base automatically changed from
gcbeatty/durable-wave0
to
gcbeatty/durable-child-context
May 20, 2026 17:46
4d97473 to
8a6c41c
Compare
Base automatically changed from
gcbeatty/durable-child-context
to
feature/durablefunction
May 23, 2026 15:58
08b2095 to
8664e8c
Compare
Adds parallel branch execution to the .NET Durable Execution SDK.
ParallelAsync runs N branches concurrently with configurable concurrency
limits and completion policies, returning an IBatchResult<T> with
per-branch status and error information.
Per-branch checkpoint payloads are serialized via the ILambdaSerializer
registered on ILambdaContext.Serializer (typically configured through
LambdaBootstrapBuilder.Create(handler, serializer)), matching the
StepAsync / RunInChildContextAsync pattern. There are no separate
reflection / AOT-safe overload pairs: the AOT story is determined
entirely by which serializer the user registers with the runtime.
Public surface:
- IDurableContext.ParallelAsync<T> (2 overloads: Func[] vs
DurableBranch<T>[])
- DurableBranch<T> record (Name + Func)
- ParallelConfig (MaxConcurrency, CompletionConfig, NestingType)
- CompletionConfig with factories AllSuccessful() / FirstSuccessful() /
AllCompleted(); ToleratedFailureCount / ToleratedFailurePercentage
(validated 0.0-1.0)
- IBatchResult<T> with All / Succeeded / Failed / Started accessors,
GetResults, GetErrors, ThrowIfError, HasFailure, CompletionReason,
count properties
- IBatchItem<T> with Index, Name, Status, Result, Error
- BatchItemStatus { Succeeded, Failed, Started }
- CompletionReason { AllCompleted, MinSuccessfulReached,
FailureToleranceExceeded }
- NestingType (Nested default; Flat throws NotSupportedException - reserved)
- ParallelException (carries IBatchResult; future-subclassable)
Internal:
- ParallelOperation<T> orchestrator dispatches branches with optional
semaphore-bounded concurrency. Each branch runs as a
ChildContextOperation<T> with deterministic ID via
OperationIdGenerator.CreateChild.
- Branch failures aggregated as IBatchItem<T> entries; orchestrator
throws ParallelException only when CompletionConfig signals
FailureToleranceExceeded.
- Parent CONTEXT checkpoint records summary (CompletionReason +
per-branch index/name/status); branch results live on per-branch
CONTEXT checkpoints.
- ExecutionState now thread-safe (lock around reads/writes of
_operations, _visitedOperations, _isReplaying). Required for
concurrent branch replay; affects all operations but no regressions.
- ParallelOperation awaits Task.WhenAll(inFlight) before disposing
the semaphore so cancellation/exception during dispatch lets
in-flight branches settle cleanly.
- Reuses OperationSubTypes.Parallel / OperationSubTypes.ParallelBranch
from Wave 0.
Adds 31 unit tests + 6 integration tests covering CompletionConfig
matrix, MaxConcurrency, FirstSuccessful short-circuit, replay
determinism, mixed-status replay, cancellation, and concurrency
stress.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
8664e8c to
e4da00c
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
#2216
What
Adds parallel branch execution to
Amazon.Lambda.DurableExecution.ParallelAsyncruns N branches concurrently with configurable concurrency limits and completion policies, returning anIBatchResult<T>with per-branch status and error information. The sharedIBatchResult<T>family is reused byMapAsyncin Wave 2.Public API:
IDurableContext.ParallelAsync<T>(Func[], ...)IDurableContext.ParallelAsync<T>(DurableBranch<T>[], ...)DurableBranch<T>(Name, Func)ParallelConfigMaxConcurrency,CompletionConfig,NestingType.CompletionConfigAllSuccessful(),FirstSuccessful(),AllCompleted(). ValidatedMinSuccessful/ToleratedFailureCount/ToleratedFailurePercentage(0.0–1.0).IBatchResult<T>All/Succeeded/Failed/Started,GetResults,GetErrors,ThrowIfError,HasFailure,CompletionReason, count properties.IBatchItem<T>Index,Name,Status,Result,Error).BatchItemStatusSucceeded/Failed/Started.CompletionReasonAllCompleted/MinSuccessfulReached/FailureToleranceExceeded.NestingTypeNested(default);Flatreserved for a follow-up PR (throwsNotSupportedExceptiontoday).ParallelExceptionCompletionConfigsignalsFailureToleranceExceeded; carries theIBatchResult<T>.Per-branch checkpoint payloads are serialized via the
ILambdaSerializerregistered onILambdaContext.Serializer— same pattern asStepAsync/RunInChildContextAsyncfrom #2370. There are no separate reflection / AOT-safe overload pairs: the AOT story is determined entirely by which serializer the user registers with the runtime (e.g.,SourceGeneratorLambdaJsonSerializer<TContext>).How
Internal/ParallelOperation<T>orchestrates branch dispatch on top of the existing child-context plumbing from #2370:ChildContextOperation<T>with a deterministic ID viaOperationIdGenerator.CreateChild, so concurrent branches get stable, namespaced IDs across replays. OptionalMaxConcurrencyis enforced with aSemaphoreSlim.CompletionConfig.FirstSuccessfulshort-circuits as soon as one branch succeeds (remaining branches surface inStarted).MinSuccessful/ToleratedFailureCount/ToleratedFailurePercentageresolve toAllCompleted,MinSuccessfulReached, orFailureToleranceExceededviaCompletionReason.Faileditems inIBatchResult<T>; the orchestrator only throwsParallelExceptionwhenCompletionConfigsignalsFailureToleranceExceeded. Successful and tolerated-failure batches return normally so the user can inspectresult.GetResults()/result.GetErrors().ParallelOperationawaitsTask.WhenAll(inFlight)before disposing the semaphore so a mid-dispatch cancellation or exception lets in-flight branches settle cleanly — no orphaned tasks holding semaphore permits.ExecutionStateis now thread-safe: a single lock guards reads/writes of_operations,_visitedOperations, and_isReplaying. Required so concurrent branches can replay without tearing the visited-set or seeing an inconsistentIsReplayingflag. Affects all operations — no behavioral regressions.ChildContextOperationreplays from its own checkpoint independently; the parentParalleloperation reconstructs theIBatchResult<T>view from the branches' terminal states.OperationSubTypes.ParallelandOperationSubTypes.ParallelBranchfrom Wave 0 (doc updates #2372).Stacked on top of #2372.
Fixes DOTNET-8662.
Testing
31 new unit tests in
ParallelOperationTests.csand supporting fixtures:CompletionConfigmatrix:AllSuccessful,AllCompleted,FirstSuccessful,MinSuccessful,ToleratedFailureCount,ToleratedFailurePercentage— both pass and fail thresholds.MaxConcurrencyenforced via semaphore; unbounded when null; cancel-mid-dispatch leaves no orphan branches.ExecutionStateaccess regression test (parallel writers do not corrupt the visited-set).SUCCEEDED+FAILED+STARTED),FirstSuccessfulwith all-fail, named vs. unnamed branches.IBatchResult<T>accessors andGetResults/GetErrors/ThrowIfErrorsemantics.NestingType.FlatthrowsNotSupportedException(placeholder for follow-up).6 new integration tests build successfully (require AWS credentials to run).
Build clean: 0 warnings, 0 errors on net8.0 and net10.0.
Out of scope (follow-up PRs)
MapAsync— sameIBatchResult<T>machinery, but driven by an input collection.NestingType.Flat— virtual contexts sharing a parent (~30% fewer checkpoint operations); placeholder today.DurableLoggerreplay-suppression (currentlyNullLogger).[DurableExecution]attribute.DurableTestRunner/Amazon.Lambda.DurableExecution.Testingpackage.dotnet new lambda.DurableFunctionblueprint.