Repository navigation
Conversation
- Cancel the workflow token synchronously at teardown, before disposal. Termination delivered the cancel via a threadpool continuation that could run after disposal and be silently dropped. - Clean up abandoned work: keep the workflow token readable after dispose, reject new durable operations once the invocation ends, and observe the abandoned workflow task's fault. - Lambda Managed Instances only: apply the invocation deadline from RemainingTime. Cancel workflow work and state hydration at the deadline, bound checkpoint cleanup, and throw TimeoutException so the invocation is retried rather than recorded as a durable failure. On-demand behavior is unchanged. - Add multi-concurrency unit tests for isolation, peer timeouts, replay alongside a live peer, and teardown ordering. Refs #2604
There was a problem hiding this comment.
🟡 Changes recommended
Critical deadline and cancellation races can leave invocations running indefinitely, produce incorrect outcomes, and allow work or checkpoint writes after teardown.
12 open issues
Recheck cancellation after checkpoint waits before running step code · New Prevent queued workflows from starting after the deadline · New Check the deadline even when the workflow task wins · New Suppress timeout-induced failure and retry checkpoints · New Bound hydration when the client ignores cancellation · New Recheck the deadline after the final checkpoint drain · New Stop dispatching checkpoint batches after cancellation · New Derive deadline expiration from the deadline token · New Coordinate teardown with in-progress cancellation delivery · New Signal deadlines independently of blocking cancellation callbacks · New Contain exceptions from cancellation callbacks · New Treat an expired invocation deadline as an immediate timeout · New
What changed in this PR
Adds deadline and teardown handling to Amazon.Lambda.DurableExecution for Lambda Managed Instances, addressing part of #2604’s shared-process execution concerns.
Changes:
- Adds invocation-scoped deadlines, timeout reporting, and bounded checkpoint cleanup.
- Cancels abandoned workflow work and rejects new durable operations after teardown.
- Adds concurrency and lifecycle tests, cancellation guidance, and patch release notes.
| File | Description |
|---|---|
Libraries/test/Amazon.Lambda.DurableExecution.Tests/LambdaManagedInstancesTests.cs |
Tests invocation isolation, replay, deadlines, and teardown. |
Libraries/src/Amazon.Lambda.DurableExecution/Internal/WorkflowCancellation.cs |
Tracks cancellation, teardown, and LMI deadlines. |
Libraries/src/Amazon.Lambda.DurableExecution/Internal/DurableInvocationOptions.cs |
Provides test overrides for LMI detection and cleanup budgets. |
Libraries/src/Amazon.Lambda.DurableExecution/Internal/CheckpointBatcher.cs |
Adds timed draining and worker abandonment. |
Libraries/src/Amazon.Lambda.DurableExecution/DurableFunction.cs |
Integrates deadlines, hydration cancellation, and cleanup. |
Libraries/src/Amazon.Lambda.DurableExecution/DurableExecutionHandler.cs |
Races workflow execution against deadlines and observes abandoned faults. |
Libraries/src/Amazon.Lambda.DurableExecution/DurableContext.cs |
Guards operation entry points against teardown. |
Libraries/src/Amazon.Lambda.DurableExecution/docs/core/cancellation.md |
Documents LMI cancellation behavior and cooperative-code limits. |
.autover/changes/46bb3a38-e1f3-48d1-ab9b-10a36475e901.json |
Records the patch release changes. |
Review effort: Balanced
- Contain exceptions thrown by user cancellation callbacks so they cannot crash the shared process from the deadline timer or replace the outcome during teardown. - Signal the deadline before cancelling, so a blocking user callback cannot stop the handler from seeing it; unregister rather than dispose the deadline registration. - Read IsDeadlineExceeded from the deadline token, independent of which cancellation reason came first. - Let the deadline decide the outcome whichever task wins the race. - Treat an already-expired LMI invocation as an immediate timeout. - Do not checkpoint FAIL or RETRY for exceptions raised after the deadline in steps, child contexts and condition checks. - Re-check cancellation before running a step body, child function or condition check, and when the queued workflow actually starts. - Re-check the deadline after the final drain, bound hydration even when the client ignores cancellation, and stop the batcher from sending further batches once it is shut down. - Tests: add a regression test per fix and a hang guard that does not throw TimeoutException, so a hang can no longer pass a deadline test.
There was a problem hiding this comment.
🟡 Changes recommended
Cancellation callbacks can indefinitely block teardown or terminate the process, while deadline races can persist incorrect failure or retry checkpoints.
12 open issues
Cancellation callbacks can exceed the cleanup timeout · New Concurrent branches can checkpoint deadline failures as permanent · New Workflow-token cancellation can bypass the cancellation filter · New Post-body serialization failures can persist after timeout · New Condition checks can record outcomes after deadline cancellation · New Deadline-token callback exceptions can escape the timer · New Recheck cancellation after checkpoint waits before running step code Cleanup budget ignores time already spent after the deadline · New Output mapping can succeed after the deadline expires · New Caught cancellation can leave request faults unobserved · New Abandonment leaves pending batch-item completions unresolved · New Race test does not trigger or verify the claimed deadline fault · New
11 resolved since last review
Treat an expired invocation deadline as an immediate timeout Contain exceptions from cancellation callbacks Signal deadlines independently of blocking cancellation callbacks Coordinate teardown with in-progress cancellation delivery Derive deadline expiration from the deadline token Stop dispatching checkpoint batches after cancellation Recheck the deadline after the final checkpoint drain Bound hydration when the client ignores cancellation Suppress timeout-induced failure and retry checkpoints Check the deadline even when the workflow task wins Prevent queued workflows from starting after the deadline
Review effort: Balanced
- Treat an OCE as cancellation when the workflow token is cancelled even if the linked token has not been notified yet (step, child context, condition check), so a body that never ran is never recorded as failed. - Map/parallel: check the workflow token directly before recording the parent outcome, matching IncrementalParallelOperation. - Condition checks: do not record RETRY/FAIL/SUCCEED for a check that returned after the LMI deadline. - Own the deadline timer so a throwing deadline-token callback (for example from the service client) cannot crash the process. - Checkpoint batcher: cancel an abandoned flush with CancelAsync so blocking client callbacks cannot stall the drain, and release pending EnqueueAsync awaiters on abandonment. - Use one combined deadline + cleanup budget for the final drain. - Report a timeout when output serialization crosses the deadline. - Always observe an abandoned hydration request's fault. - Tests: regression test per fix; the deadline fault-race test registers on the deadline token so it exercises the race again; add a teardown-path test for throwing cancellation callbacks.
There was a problem hiding this comment.
🟡 Changes recommended
Blocking cancellation callbacks can hang teardown and deadline handling, while deadline races can allow expired workflows to start, succeed, or persist outcomes.
7 open issues
Recheck deadline after state serialization · New Check the monotonic deadline, not timer callback state · New Publish deadline state independently of callbacks · New Run cancellation callbacks off the teardown thread · New Recheck cancellation after checkpoint waits before running step code Observe faults after every abandonment cancellation · New Register callbacks before triggering the test deadline · New
11 resolved since last review
Deadline-token callback exceptions can escape the timer Condition checks can record outcomes after deadline cancellation Post-body serialization failures can persist after timeout Workflow-token cancellation can bypass the cancellation filter Concurrent branches can checkpoint deadline failures as permanent Cancellation callbacks can exceed the cleanup timeout Race test does not trigger or verify the claimed deadline fault Abandonment leaves pending batch-item completions unresolved Caught cancellation can leave request faults unobserved Output mapping can succeed after the deadline expires Cleanup budget ignores time already spent after the deadline
Review effort: Balanced
Third round of Copilot review findings, addressed with one rule instead of per-site patches: the SDK never runs cancellation callbacks it does not own (user code, the service client) on its own threads, and it publishes its own deadline and teardown state before any foreign code runs. - Every cancel the SDK starts uses CancellationTokenSource.CancelAsync: tokens report cancellation immediately, callbacks run on the threadpool, and their exceptions are logged. BeginTeardown can no longer block in a user callback, in any mode. - A short lock around starting the cancel ensures every caller returns only after the token reports cancellation. - Sources are disposed only after callback delivery finishes, so no callback is skipped. - The deadline is anchored to a monotonic timestamp taken when RemainingTime was read. IsDeadlineExceeded and admission compare the clock, and fire the deadline themselves if the timer is late. - Firing the deadline records the reason, closes admission and signals DeadlineTask before cancelling the workflow token, then the client's deadline token. Hydration races DeadlineTask, not the client token. - Map/parallel parents check the deadline before recording an outcome. - Condition checks re-check the deadline after serializing state, on the RETRY, SUCCEED and max-attempts FAIL paths. - Checkpoint batcher always observes an abandoned item's fault. - Tests: deadline-dependent tests fire the deadline themselves after setup (TriggerDeadline) instead of racing a real 50 ms timer, and a test-only option leaves the timer unarmed to model a late timer.
There was a problem hiding this comment.
🟡 Changes recommended
Critical deadline-handling races can leave hydration uncancelled and allow step, child, and condition callbacks to start after expiration.
6 open issues
Check the deadline before starting a child function · New Check the deadline before starting an at-most-once step · New Check the deadline before starting a replayed condition check · New Initialize deadline cancellation before signaling the timer task · New Recheck cancellation after checkpoint waits before running step code Make the deadline-during-step test deterministic · New
6 resolved since last review
Run cancellation callbacks off the teardown thread Publish deadline state independently of callbacks Check the monotonic deadline, not timer callback state Recheck deadline after state serialization Register callbacks before triggering the test deadline Observe faults after every abandonment cancellation
Review effort: Balanced
- Start the deadline-token and workflow-token cancels before signalling DeadlineTask, so a waiter that ends the invocation and disposes at once always finds the deadline cancel under way. Dispose and starting a cancel now coordinate under one lock: a cancel is either already started (and waited for) or never starts on a released source. - Check the clock-based deadline immediately before running a step body, a child context function or a condition check, alongside the token check, so an awaited START or a replay state restore that crosses the deadline before its timer runs cannot start user code. - Tests: barrier test disposing right after DeadlineTask is signalled; unarmed-timer tests for step and child START waits and condition replay; the step-deadline wrapper test now fires a controllable deadline after the step is entered instead of racing a real 100 ms deadline.
There was a problem hiding this comment.
🟡 Changes recommended
A moderate issue remains in deadline handling because hydration faults after the deadline can escape as SDK errors instead of being consistently translated into the promised timeout exception.
1 open issue
Review effort: Lite
| catch (OperationCanceledException ex) when (workflowCancellation.IsDeadlineExceeded) | ||
| { | ||
| var (operations, marker) = await serviceClient.GetExecutionStateAsync( | ||
| invocationInput.DurableExecutionArn, checkpointToken, nextMarker); | ||
| state.AddOperations(operations); | ||
| nextMarker = marker; | ||
| throw CreateDeadlineException(invocationInput.DurableExecutionArn, ex); |


Summary
Makes
Amazon.Lambda.DurableExecutionsafe to run on Lambda Managed Instances (LMI), where several invocations share one process and Lambda does not stop an invocation that times out. Addresses the source-review findings in #2604.1. Cancellation lost at teardown (all modes)
TerminationManagercompletes its task withRunContinuationsAsynchronously, soWorkflowCancellationreceived the cancel through a threadpool continuation. If the wrapper disposed the cancellation source first, the continuation hitObjectDisposedException, which was swallowed, and the cancel was lost. The wrapper now cancels synchronously (BeginTeardown) before draining and disposing, andDisposealso cancels before releasing the source.2. Abandoned work after the invocation ends (all modes)
When a workflow suspends, user work it started but did not await (a step still running, a losing parallel branch) kept running. On LMI the process stays live, so that work runs alongside other invocations.
ObjectDisposedException.IDurableContextoperation entry point rejects new operations after teardown withOperationCanceledException.UnobservedTaskExceptionin another invocation.3. Invocation deadline (LMI only)
Only when
AWS_LAMBDA_MAX_CONCURRENCYis set; on-demand behavior is unchanged.ILambdaContext.RemainingTime. Zero or negative means no deadline, so an unconfiguredTestLambdaContextdoesn't cancel immediately.GetExecutionStateAsync) is cancelled.WrapAsyncthrowsTimeoutException. This escapes as an invocation error, the same outcome as an on-demand timeout, and is never recorded as aFaileddurable execution, even if user code catches the cancellation and throws its own exception.Testing
LambdaManagedInstancesTests(23 tests) driveDurableFunctionconcurrently with an in-memory service client:Amazon.Lambda.DurableExecution.Tests(524) andAmazon.Lambda.DurableExecution.Testing.Tests(154) pass on net8.0 and net10.0. The new tests passed 10 repeated runs.Not covered
docs/core/cancellation.md.IDurableParallelbranches added to an existing incremental parallel after teardown aren't gated at entry; their child operations are.