Repository navigation
[Async stack 2/6] asyncio composition and durable timers - #358
Draft
andystaples wants to merge 6 commits into
Draft
andystaples wants to merge 6 commits into
andystaples wants to merge 6 commits into
Conversation
This was referenced Nov 11, 2026
andystaples
marked this pull request as draft
November 12, 2026 16:30
andystaples
force-pushed
the
andystaples-async-stack-2-asyncio-composition
branch
from
November 12, 2026 16:48
1b5625c to
1e5bf2f
Compare
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Use immutable dispatch-local task creation hashes so native TaskGroup traversal cannot depend on worker-local object addresses before scheduling shared-Future callbacks. Preserve existing identity equality and cancellation-wakeup ordering. Cover direct and gather-wrapped shared futures, out-of-order activity completion, fresh dispatch executors, and replay in separate worker processes. Document pinned worker environments and existing-history upgrade risks. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Convert loop deadlines with exact rational arithmetic so durable timers never fire before the requested loop time, and add exact-oracle regression tests. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
andystaples
force-pushed
the
andystaples-async-stack-2-asyncio-composition
branch
from
November 12, 2026 20:32
0cfb7b3 to
44eccfc
Compare
…sync-stack-2-asyncio-composition Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> # Conflicts: # docs/async-orchestrators.md
…sync-stack-2-asyncio-composition Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Report every unimplemented asyncio loop API through one actionable error table, tolerate waiters finalized after dispatch close, use replay-safe lookups for cancellation ordering, normalize timezone-aware clocks for loop timers, and document why task hashing is load-bearing and which private asyncio hooks the loop relies on. Document timer resolution and that tasks must be created with asyncio.create_task. Remove changelog entries for unreleased behavior. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
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.
Warning
Do not merge this PR, or any other PR in this stack, on its own. The stack was split out of the closed #293 to make review easier. Once every PR in the stack is approved, the whole stack will be squash-merged together.
Stack layer 2/6, built on the coroutine dispatch layer (#357).
Summary
This layer adds general
asynciocomposition and durable timers to the replay-safe event loop that coroutine orchestrators run on.Task composition
_DurableAsyncTask. A task is retired once its result or exception has been observed, so replaying discardedgatherbatches and handled failures does not keep decoded payloads or tracebacks alive.TaskGroupiterates a set of tasks to cancel them, so this keeps cancellation order independent of memory addresses; the reason is documented in code.TaskGroup,gather,wait_for, andasyncio.timeoutcompose over durable work:TaskGroupis cancelled stay the same across replays.asyncio.Taskconstructed directly rather than throughasyncio.create_task) no longer raises during garbage collection. Directly constructed tasks are documented as unsupported.Durable timers
call_laterandcall_atare backed by durable timers and return_DurableTimerHandle, which compares by identity.asyncio.sleepand timeouts use the existing chunking for long timers.Unsupported work
NotImplementedError.asyncio.waitandasyncio.as_completedare not rejected at run time; the contract documents them as unsupported because their ordering is not replay-stable.Docs (
docs/async-orchestrators.md)loop.time()are effectively zero, and that tasks must be created withasyncio.create_taskor a task group.Code docs
asyncio_loop.pymodule docstring, and clarifies theTask.wait()docstring forasyncio.create_taskand task groups.Scope
Left for later layers
close()from layer 1/6, extended only to release the new timer and cancellation state.OrchestrationContext. The coldAsyncOrchestrationContextarrives in layer 4/6 ([Async stack 4/6] Add cold async orchestration context #361), which updates these tests to use it.Tests
tests/durabletask/test_asyncio_orchestrators.py, covering: