Skip to content

fix(cli/task): run recursive workspace tasks in parallel#34512

Merged
bartlomieju merged 2 commits into
mainfrom
fix/task-recursive-parallel-27586
May 30, 2026
Merged

fix(cli/task): run recursive workspace tasks in parallel#34512
bartlomieju merged 2 commits into
mainfrom
fix/task-recursive-parallel-27586

Conversation

@bartlomieju

Copy link
Copy Markdown
Member

deno task -r <name> (and deno task --filter <pattern> <name>)
walked workspace members one at a time, so a long-running task in the
first matched member blocked every other member from starting. This was
especially painful for the common dev-server case described in #27586,
where running dev recursively across an apps/server + apps/web
workspace would only ever boot one of them.

The fix flattens all matched packages' resolved tasks into a single
topology and runs them through the existing parallel task runner, so
cross-package siblings execute concurrently while intra-package task
dependencies still order correctly. When concurrent tasks span more
than one workspace member, the colored output prefix becomes
[pkg#task] so identically-named siblings stay distinguishable; if a
member has no name field the folder's directory name is used, except
for the workspace root which falls back to the bare task name.

Fixes #27586

`deno task -r <name>` (and `deno task --filter <pattern> <name>`)
iterated workspace members sequentially, so a long-running task in one
member blocked every other member from starting. The fix flattens all
matched packages' resolved tasks into one topology that runs through
the existing parallel task runner, and extends the colored prefix label
to `[pkg#task]` when concurrent tasks span multiple workspace members
so identically-named siblings stay distinguishable.

@fibibot fibibot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Flattening all matched packages into one shared topology and running it through run_tasks_in_parallel is the right shape: IDs come from the shared sorted.len() so they stay globally unique across packages, and sort_visit's dedup-by-(name, folder_url) now collapses a shared root-task dependency into a single node across members instead of re-running it per package.

One concern on the new test. recursive_parallel deadlocks whenever available_parallelism() (or DENO_JOBS) is < 3. The three dev tasks each block until all three marker files exist, so they must sit in the queue simultaneously — but run_tasks_in_parallel only admits self.concurrency tasks at a time (while queue.len() < self.concurrency). On a 2-core runner the third dev never starts, the first two wait forever, and the test hangs until the 15s timeout fails it. Pin it so it does not depend on host core count: add "envs": { "DENO_JOBS": "3" } to __test__.jsonc.

Holding approval until CI is green — debug builds and linux specs are still running.

The three `dev` tasks block on a filesystem barrier waiting for all
three marker files to exist, so they must run concurrently. On runners
where `available_parallelism()` returns less than 3, only two of the
tasks are admitted into the queue and the test deadlocks until the 15s
timeout. Pin `DENO_JOBS=3` so the test is independent of host core
count.

@lunadogbot lunadogbot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The DENO_JOBS=3 pin in __test__.jsonc (commit 6b39371) addresses my earlier concern about the recursive_parallel test deadlocking on available_parallelism() < 3 runners — the three filesystem-barrier dev tasks now reliably get admitted into the queue regardless of host core count.

Architecture is clean:

  • sort_tasks_topo switches from building a per-package Vec to appending into a shared &mut Vec, so sort_visit's dedup-by-(name, folder_url) collapses a shared root-task dependency into a single node across members rather than re-running per package.
  • run_all_tasks replaces the sequential per-package for-loop with one call to the existing run_tasks_in_parallel. NotFound semantics correctly shift to "error only when the combined sorted Vec is empty", which is the right shape for recursive/filtered mode.
  • [pkg#task] label expansion is gated on whether concurrent tasks actually span more than one folder URL, so single-package recursive runs keep the old [task] form. Root-package tasks also render as bare [task] because task_label_name returns None for the workspace root — visible in dependencies_shadowed_root_name.out.

User-visible behavior change worth flagging in release notes (not a blocker): recursive task output is now non-deterministic in ordering — reflected in all the filter/*.out files and recursive_order/task.out switching to UNORDERED_START/END. Anyone string-matching deno task -r output in CI may need to adjust.

CI is fully green.

LGTM.

@bartlomieju
bartlomieju merged commit 77a1e24 into main May 30, 2026
136 checks passed
@bartlomieju
bartlomieju deleted the fix/task-recursive-parallel-27586 branch May 30, 2026 07:00
littledivy pushed a commit to crowlKats/deno that referenced this pull request Jun 10, 2026
)

`deno task -r <name>` (and `deno task --filter <pattern> <name>`)
walked workspace members one at a time, so a long-running task in the
first matched member blocked every other member from starting. This was
especially painful for the common dev-server case described in denoland#27586,
where running `dev` recursively across an `apps/server` + `apps/web`
workspace would only ever boot one of them.

The fix flattens all matched packages' resolved tasks into a single
topology and runs them through the existing parallel task runner, so
cross-package siblings execute concurrently while intra-package task
dependencies still order correctly. When concurrent tasks span more
than one workspace member, the colored output prefix becomes
`[pkg#task]` so identically-named siblings stay distinguishable; if a
member has no `name` field the folder's directory name is used, except
for the workspace root which falls back to the bare task name.

Fixes denoland#27586
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Long running tasks in parallel

3 participants