Add WaitForConditionAsync polling primitive (DOTNET-8665)#2376
Draft
GarrettBeatty wants to merge 1 commit into
Draft
Add WaitForConditionAsync polling primitive (DOTNET-8665)#2376GarrettBeatty 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"] |
464c591 to
d308c3b
Compare
7f91202 to
3fa06ce
Compare
d308c3b to
be4c3ad
Compare
3fa06ce to
67f0c0c
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
67f0c0c to
feec401
Compare
Adds service-mediated polling to the .NET Durable Execution SDK. WaitForConditionAsync repeatedly evaluates a check function with configurable wait strategy between attempts; each iteration is its own Lambda invocation (suspended via STEP+RETRY checkpoints carrying NextAttemptDelaySeconds), so polling does not consume compute time. Public surface: - IDurableContext.WaitForConditionAsync<TState> (single overload; the per-iteration state checkpoint is serialized via the ILambdaSerializer registered on ILambdaContext.Serializer, configured via LambdaBootstrapBuilder.Create(handler, serializer)) - IConditionCheckContext (Logger + AttemptNumber) - WaitForConditionConfig<TState> (required InitialState + WaitStrategy) - IWaitStrategy<TState> with Decide(state, attempt) returning WaitDecision - WaitDecision (readonly record struct, ShouldContinue + Delay, Stop() / ContinueAfter(TimeSpan) factories) - WaitStrategy factories: Exponential / Linear / Fixed / FromDelegate, each accepting an optional Func<TState, bool> isDone predicate - WaitForConditionException with AttemptsExhausted and LastState (preserved across both live execution and replay) Internal: - WaitForConditionOperation<TState> wire format = STEP + SubType "WaitForCondition". Each polling iteration emits Action=/proxy/https/github.com/aws/aws-lambda-dotnet/pull/RETRY with the new state in payload and NextAttemptDelaySeconds for the service to schedule the next invocation. - Serialization is delegated to the registered ILambdaSerializer via Stream-based Serialize<T>/Deserialize<T> calls; no AOT trim attributes on the public API. Mirrors StepOperation/ChildContextOperation. - Strategies signal max-attempts exhausted by throwing WaitForConditionException directly from Decide(); the operation enriches with LastState before checkpointing FAIL. - LastState survives FAIL replay: serialized into FAIL payload at write time, deserialized in BuildFailureException with warning-logged fallback for legacy/corrupt data. - ExponentialBackoff helper extracted for sharing with ExponentialRetryStrategy. Math is byte-for-byte identical. - Reuses OperationSubTypes.WaitForCondition from Wave 0. Defaults: 60 attempts / 5s initial / 300s max / 1.5x rate / Full jitter - distinct from RetryStrategy.Default and matching Python/JS/Java reference SDKs. (Note: Python returns success on max-attempts; .NET/Java/JS throw - documented in design doc.) Adds 41 unit tests + 5 integration tests covering each wait strategy, isDone predicate paths, max-attempts exhaustion, user-check exceptions, replay determinism, exponential backoff bounds, and corrupt-payload fallback logging. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
feec401 to
299ac83
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 service-mediated polling to
Amazon.Lambda.DurableExecution.WaitForConditionAsyncrepeatedly evaluates a check function with a configurable wait strategy between attempts; each iteration runs in its own Lambda invocation (suspended viaSTEP+RETRYcheckpoints carryingNextAttemptDelaySeconds), so polling does not consume compute time while waiting between checks.Public API:
IDurableContext.WaitForConditionAsync<TState>(...)maxAttemptsis exhausted. Per-iteration state is serialized via theILambdaSerializerregistered onILambdaContext.Serializer(same pattern asStepAsync/RunInChildContextAsync).IConditionCheckContextLogger,AttemptNumber.WaitForConditionConfig<TState>InitialState+WaitStrategy.IWaitStrategy<TState>Decide(state, attempt)returningWaitDecision.WaitDecisionShouldContinue,Delay, factoriesStop()/ContinueAfter(TimeSpan).WaitStrategyfactoriesExponential,Linear,Fixed,FromDelegate— each accepting an optionalFunc<TState, bool> isDonepredicate.WaitForConditionExceptionmaxAttemptsis exhausted; carriesAttemptsExhaustedandLastState(preserved across both live execution and replay).How
Internal/WaitForConditionOperation<TState>runs as aSTEPwithSubType = "WaitForCondition":Decide(state, attempt)returns eitherStop()(success — return state) orContinueAfter(delay)(suspend until next attempt). Continuing emitsAction=/proxy/https/github.com/aws/aws-lambda-dotnet/pull/RETRY%3C/code> with the new state in the payload andNextAttemptDelaySecondsset so the durable execution service schedules the next invocation without consuming compute time.WaitForConditionExceptiondirectly fromDecide(); the operation enriches withLastStatebefore checkpointing FAIL.LastStatesurvives FAIL replay: serialized into the FAIL payload at write time and deserialized inBuildFailureExceptionwith a warning-logged fallback for legacy / corrupt data.ILambdaSerializervia stream-basedSerialize<T>/Deserialize<T>calls — no AOT trim attributes on the public API. MirrorsStepOperation/ChildContextOperation.ExponentialBackoffhelper is extracted for sharing between this operation andExponentialRetryStrategy. Math is byte-for-byte identical to the existing implementation.OperationSubTypes.WaitForConditionfrom Wave 0 (doc updates #2372).Defaults: 60 attempts, 5s initial delay, 300s max delay, 1.5x rate, Full jitter — distinct from
RetryStrategy.Defaultand matching Python/JS/Java reference SDKs.Stacked on top of #2372.
Fixes DOTNET-8665.
Testing
41 new unit tests covering each wait strategy, the
isDonepredicate paths, max-attempts exhaustion, user-check exceptions, replay determinism, exponential-backoff bounds, and corrupt-payload fallback logging:WaitStrategyfactory:Exponential,Linear,Fixed,FromDelegate— both with and withoutisDonepredicate.WaitForConditionExceptionthrown after max-attempts;AttemptsExhausted+LastStatepopulated.SUCCEEDEDreturns cached state,FAILEDreconstructsWaitForConditionExceptionwithLastState,STARTEDre-suspends.LastStatesurvives FAIL replay; corrupt FAIL payload falls back gracefully with a warning.ExponentialBackoffshared math: byte-for-byte identical toExponentialRetryStrategy(regression test).5 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/ continued parallel-suite work.DurableLoggerreplay-suppression (currentlyNullLogger).[DurableExecution]attribute.DurableTestRunner/Amazon.Lambda.DurableExecution.Testingpackage.dotnet new lambda.DurableFunctionblueprint.