Repository navigation
fix: complete shared and duplicate whenAll tasks - #383
Open
wangbill (YunchuWang) wants to merge 2 commits into
Open
wangbill (YunchuWang) wants to merge 2 commits into
wangbill (YunchuWang) wants to merge 2 commits into
Conversation
Notify every dependent composite and count duplicate task positions without repeating activities. Release completed race subscriptions and preserve callback errors, wait-all failures, and direct-child winner identity. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 007ee1b8-76d7-478f-a825-ec91e732300f
Contributor
There was a problem hiding this comment.
🔵 Needs a closer look
Core task-graph changes alter observable replay branching and require final human validation.
1 open finding
What changed in this PR
Fixes task composition so shared and duplicate tasks complete reliably.
Changes:
- Adds multi-parent completion notifications and duplicate tracking.
- Detaches completed
whenAnygroups from losing children. - Adds regression coverage and documentation.
| File | Description |
|---|---|
README.md |
Documents shared tasks and callback errors. |
CHANGELOG.md |
Adds release note. |
src/task/task.ts |
Implements multi-parent notification. |
src/task/composite-task.ts |
Tracks unique children and occurrences. |
src/task/completable-task.ts |
Notifies all parents. |
src/task/when-all-task.ts |
Handles duplicate references. |
src/task/when-any-task.ts |
Uses multi-parent notification. |
src/task/retry-task-base.ts |
Updates retry failure notification. |
src/task/timer-task.ts |
Updates cancellation documentation. |
src/task/index.ts |
Revises whenAll API documentation. |
test/composite-task.spec.ts |
Adds task-graph boundary tests. |
test/task-composition.spec.ts |
Adds public integration regressions. |
test/when-all-task.spec.ts |
Uses a real composite parent. |
test/long-timer-replay-regressions.spec.ts |
Updates nested-race expectations. |
🧠 Review effort: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Comment on lines
+9
to
+13
| * Returns a task that completes after every provided task completes or fails. | ||
| * Results preserve input order and duplicate task references. Tasks may be shared between groups. | ||
| * | ||
| * @param tasks the tasks to wait for | ||
| * @returns {WhenAllTask} a task that completes when all of the provided tasks complete or when one of the tasks fail | ||
| * @returns {WhenAllTask} a task containing the ordered results, or an aggregate of child failures |
kaibocai (kaibocai)
approved these changes
Oct 9, 2026
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.

Summary
What changed?
whenAnygroups from their remaining children. Notify other dependents even when one completion callback throws, preserving a single thrown error and aggregating multiple callback errors.Why is this change needed?
whenAll([a, b])andwhenAll([a, c])previously competed fora's parent subscription. The first group could remain pending after all three activities completed.whenAll([t, t])previously counted one completion against two array positions and remained pending after its only activity completed.Issues / work items
Project checklist
CHANGELOG.mdwhenAnygraphs can select a different direct child and take different branches during replay. Internal task-parent bookkeeping also changes.AI-assisted code disclosure (required)
Was an AI tool used? (select one)
If AI was used:
AI verification (required if AI was used):
Testing
Automated tests
RUNNINGafter all activity completions were committed.npm test --workspace @microsoft/durabletask-js -- --runTestsByPath test\composite-task.spec.ts test\task-composition.spec.ts test\when-all-task.spec.ts test\when-any-task.spec.ts test\long-timer-replay-regressions.spec.ts --silent: passed, 84 tests in five suites.npm run test:unit --workspace @microsoft/durabletask-js -- --silent: passed, 1,719 tests in 87 core suites.npm run build:core: passed, including the core TypeScript build.npm run example -- .\examples\azure-managed\unit-testing\index.ts: passed, all six in-memory demos.git --no-pager diff --check origin/main...HEAD: passed.Changed-file checks ran in PowerShell:
Manual validation (only if runtime/behavior changed)
InMemoryOrchestrationBackend,TestOrchestrationClient, andTestOrchestrationWorker.COMPLETED, output[["A","B"],["A","C"]], three actual activity invocations.COMPLETED, output["V","V"], one actual activity invocation.task-composition.spec.tsuses a reused worker/client and committed-history fences.composite-task.spec.tscovers all six completion orders, mixed/precompleted duplicates, shared nested composites, wait-all failure order, race identity/detachment, cancellation callback errors, and policy/custom retry terminal paths.Notes for reviewers
whenAnyreturns its winning direct child, not a flattened descendant. ForfirstRace = whenAny([timer, work])andsecondRace = whenAny([firstRace, timer]), timer completion now notifiesfirstRace; its notification can makesecondRaceselectfirstRace. Previously the timer's parent was overwritten, andfirstRacecould later selectwork. The existing shared-timer regression is updated to assert this intentional observable change. Historical winner ordering or unchanged replay branching for shared graphs is not claimed.whenAllwhose result collection throws still does not notify its own parents. Other composites observing that child are nevertheless notified; multiple callback errors surface in anAggregateError._parentstate now uses a real composite parent and a notification spy. Supported orchestrator signatures, activity scheduling, wait-all failure aggregation, and protocol/backend behavior are unchanged.