Skip to content

Commit ac6905e

Browse files
branchseerclaude
andcommitted
refactor: replace concurrency inheritance with VP_RUN_CONCURRENCY_LIMIT env var
Remove the implicit context-based concurrency inheritance. Instead, use the VP_RUN_CONCURRENCY_LIMIT environment variable (which naturally passes through to child processes since VP_* is already untracked). Priority order: --concurrency-limit flag > VP_RUN_CONCURRENCY_LIMIT env > --parallel (unlimited) > default (4). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent d46300c commit ac6905e

File tree

12 files changed

+429
-230
lines changed

12 files changed

+429
-230
lines changed

crates/vite_task/src/session/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -591,6 +591,11 @@ impl<'a> Session<'a> {
591591
&self.envs
592592
}
593593

594+
/// Mutably access the environment map, cloning the `Arc` if shared.
595+
pub fn envs_mut(&mut self) -> &mut FxHashMap<Arc<OsStr>, Arc<OsStr>> {
596+
Arc::make_mut(&mut self.envs)
597+
}
598+
594599
pub const fn cwd(&self) -> &Arc<AbsolutePath> {
595600
&self.cwd
596601
}

crates/vite_task_plan/src/context.rs

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -45,25 +45,19 @@ pub struct PlanContext<'a> {
4545
/// Final resolved global cache config, combining the graph's config with any CLI override.
4646
resolved_global_cache: ResolvedGlobalCacheConfig,
4747

48-
/// Resolved concurrency limit inherited from the parent level.
49-
/// At the root level this defaults to [`crate::DEFAULT_CONCURRENCY_LIMIT`].
50-
resolved_concurrency: usize,
51-
5248
/// The query that caused the current expansion.
5349
/// Used by the skip rule to detect and skip duplicate nested expansions.
5450
parent_query: Arc<TaskQuery>,
5551
}
5652

5753
impl<'a> PlanContext<'a> {
58-
#[expect(clippy::too_many_arguments, reason = "context initialization requires all fields")]
5954
pub fn new(
6055
workspace_path: &'a Arc<AbsolutePath>,
6156
cwd: Arc<AbsolutePath>,
6257
envs: FxHashMap<Arc<OsStr>, Arc<OsStr>>,
6358
callbacks: &'a mut (dyn PlanRequestParser + 'a),
6459
indexed_task_graph: &'a IndexedTaskGraph,
6560
resolved_global_cache: ResolvedGlobalCacheConfig,
66-
resolved_concurrency: usize,
6761
parent_query: Arc<TaskQuery>,
6862
) -> Self {
6963
Self {
@@ -75,7 +69,6 @@ impl<'a> PlanContext<'a> {
7569
indexed_task_graph,
7670
extra_args: Arc::default(),
7771
resolved_global_cache,
78-
resolved_concurrency,
7972
parent_query,
8073
}
8174
}
@@ -143,14 +136,6 @@ impl<'a> PlanContext<'a> {
143136
self.resolved_global_cache = config;
144137
}
145138

146-
pub const fn resolved_concurrency(&self) -> usize {
147-
self.resolved_concurrency
148-
}
149-
150-
pub const fn set_resolved_concurrency(&mut self, concurrency: usize) {
151-
self.resolved_concurrency = concurrency;
152-
}
153-
154139
pub fn parent_query(&self) -> &TaskQuery {
155140
&self.parent_query
156141
}
@@ -175,7 +160,6 @@ impl<'a> PlanContext<'a> {
175160
indexed_task_graph: self.indexed_task_graph,
176161
extra_args: Arc::clone(&self.extra_args),
177162
resolved_global_cache: self.resolved_global_cache,
178-
resolved_concurrency: self.resolved_concurrency,
179163
parent_query: Arc::clone(&self.parent_query),
180164
}
181165
}

crates/vite_task_plan/src/error.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,9 @@ pub enum Error {
154154
#[error("Task \"{0}\" not found")]
155155
NoTasksMatched(Str),
156156

157+
#[error("Invalid value for VP_RUN_CONCURRENCY_LIMIT: {0:?}")]
158+
InvalidConcurrencyLimitEnv(Arc<OsStr>),
159+
157160
/// A cycle was detected in the task dependency graph during planning.
158161
///
159162
/// This is caught by `AcyclicGraph::try_from_graph`, which validates that the

crates/vite_task_plan/src/lib.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,6 @@ pub async fn plan_query(
209209
plan_request_parser,
210210
indexed_task_graph,
211211
resolved_global_cache,
212-
DEFAULT_CONCURRENCY_LIMIT,
213212
Arc::clone(&query),
214213
);
215214
plan_query_request(query, plan_options, context).await

crates/vite_task_plan/src/plan.rs

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -677,20 +677,22 @@ pub async fn plan_query_request(
677677
}
678678
// Resolve effective concurrency for this level.
679679
//
680-
// When `Some(n)`, use the explicit value. When `None`, inherit from the
681-
// parent context — unless `--parallel` is set without `--concurrency-limit`,
682-
// in which case use unlimited concurrency.
680+
// Priority (highest to lowest):
681+
// 1. `--concurrency-limit N` CLI flag
682+
// 2. `VP_RUN_CONCURRENCY_LIMIT` env var
683+
// 3. `--parallel` (without the above) → unlimited
684+
// 4. `DEFAULT_CONCURRENCY_LIMIT` (4)
683685
let effective_concurrency = match plan_options.concurrency_limit {
684686
Some(n) => n,
685687
None => {
686688
if plan_options.parallel {
687689
usize::MAX
688690
} else {
689-
context.resolved_concurrency()
691+
concurrency_limit_from_env(context.envs())?
692+
.unwrap_or(crate::DEFAULT_CONCURRENCY_LIMIT)
690693
}
691694
}
692695
};
693-
context.set_resolved_concurrency(effective_concurrency);
694696

695697
let parallel = plan_options.parallel;
696698

@@ -802,6 +804,22 @@ pub async fn plan_query_request(
802804
})
803805
}
804806

807+
/// Parse `VP_RUN_CONCURRENCY_LIMIT` from the environment variables.
808+
///
809+
/// Returns `Ok(None)` if the variable is not set.
810+
/// Returns `Err` if the variable is set but cannot be parsed as a positive integer.
811+
#[expect(clippy::result_large_err, reason = "Error type is shared across all plan functions")]
812+
fn concurrency_limit_from_env(
813+
envs: &FxHashMap<Arc<OsStr>, Arc<OsStr>>,
814+
) -> Result<Option<usize>, Error> {
815+
let Some(value) = envs.get(OsStr::new("VP_RUN_CONCURRENCY_LIMIT")) else {
816+
return Ok(None);
817+
};
818+
let s = value.to_str().ok_or_else(|| Error::InvalidConcurrencyLimitEnv(Arc::clone(value)))?;
819+
let n: usize = s.parse().map_err(|_| Error::InvalidConcurrencyLimitEnv(Arc::clone(value)))?;
820+
Ok(Some(n.max(1)))
821+
}
822+
805823
#[cfg(test)]
806824
mod tests {
807825
use std::collections::BTreeSet;

crates/vite_task_plan/tests/plan_snapshots/fixtures/parallel-and-concurrency/snapshots.toml

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,19 @@ args = ["run", "-r", "--parallel", "build"]
3131
name = "parallel with concurrency-limit"
3232
args = ["run", "-r", "--parallel", "--concurrency-limit", "3", "build"]
3333

34-
# Nested vt run inherits parent concurrency
34+
# Nested vt run with explicit --concurrency-limit
3535
[[plan]]
36-
name = "nested inherits parent concurrency"
37-
args = ["run", "--concurrency-limit", "2", "build"]
36+
name = "nested with explicit concurrency-limit"
37+
args = ["run", "build-with-concurrency"]
3838

39-
# Nested vt run with explicit --concurrency-limit overrides parent
39+
# VP_RUN_CONCURRENCY_LIMIT env var sets concurrency
4040
[[plan]]
41-
name = "nested overrides parent concurrency"
42-
args = ["run", "--concurrency-limit", "2", "build-with-concurrency"]
41+
name = "env var sets concurrency"
42+
args = ["run", "-r", "build"]
43+
env = { VP_RUN_CONCURRENCY_LIMIT = "7" }
44+
45+
# --concurrency-limit flag takes priority over VP_RUN_CONCURRENCY_LIMIT env
46+
[[plan]]
47+
name = "cli flag overrides env var"
48+
args = ["run", "-r", "--concurrency-limit", "3", "build"]
49+
env = { VP_RUN_CONCURRENCY_LIMIT = "7" }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
---
2+
source: crates/vite_task_plan/tests/plan_snapshots/main.rs
3+
expression: "&plan_json"
4+
info:
5+
args:
6+
- run
7+
- "-r"
8+
- "--concurrency-limit"
9+
- "3"
10+
- build
11+
input_file: crates/vite_task_plan/tests/plan_snapshots/fixtures/parallel-and-concurrency
12+
---
13+
{
14+
"graph": [
15+
{
16+
"key": [
17+
"<workspace>/",
18+
"build"
19+
],
20+
"node": {
21+
"task_display": {
22+
"package_name": "test-workspace",
23+
"task_name": "build",
24+
"package_path": "<workspace>/"
25+
},
26+
"items": []
27+
},
28+
"neighbors": []
29+
},
30+
{
31+
"key": [
32+
"<workspace>/packages/a",
33+
"build"
34+
],
35+
"node": {
36+
"task_display": {
37+
"package_name": "@test/a",
38+
"task_name": "build",
39+
"package_path": "<workspace>/packages/a"
40+
},
41+
"items": [
42+
{
43+
"execution_item_display": {
44+
"task_display": {
45+
"package_name": "@test/a",
46+
"task_name": "build",
47+
"package_path": "<workspace>/packages/a"
48+
},
49+
"command": "echo building a",
50+
"and_item_index": null,
51+
"cwd": "<workspace>/packages/a"
52+
},
53+
"kind": {
54+
"Leaf": {
55+
"InProcess": {
56+
"kind": {
57+
"Echo": {
58+
"strings": [
59+
"building",
60+
"a"
61+
],
62+
"trailing_newline": true
63+
}
64+
}
65+
}
66+
}
67+
}
68+
}
69+
]
70+
},
71+
"neighbors": [
72+
[
73+
"<workspace>/packages/b",
74+
"build"
75+
]
76+
]
77+
},
78+
{
79+
"key": [
80+
"<workspace>/packages/b",
81+
"build"
82+
],
83+
"node": {
84+
"task_display": {
85+
"package_name": "@test/b",
86+
"task_name": "build",
87+
"package_path": "<workspace>/packages/b"
88+
},
89+
"items": [
90+
{
91+
"execution_item_display": {
92+
"task_display": {
93+
"package_name": "@test/b",
94+
"task_name": "build",
95+
"package_path": "<workspace>/packages/b"
96+
},
97+
"command": "echo building b",
98+
"and_item_index": null,
99+
"cwd": "<workspace>/packages/b"
100+
},
101+
"kind": {
102+
"Leaf": {
103+
"InProcess": {
104+
"kind": {
105+
"Echo": {
106+
"strings": [
107+
"building",
108+
"b"
109+
],
110+
"trailing_newline": true
111+
}
112+
}
113+
}
114+
}
115+
}
116+
}
117+
]
118+
},
119+
"neighbors": [
120+
[
121+
"<workspace>/packages/c",
122+
"build"
123+
]
124+
]
125+
},
126+
{
127+
"key": [
128+
"<workspace>/packages/c",
129+
"build"
130+
],
131+
"node": {
132+
"task_display": {
133+
"package_name": "@test/c",
134+
"task_name": "build",
135+
"package_path": "<workspace>/packages/c"
136+
},
137+
"items": [
138+
{
139+
"execution_item_display": {
140+
"task_display": {
141+
"package_name": "@test/c",
142+
"task_name": "build",
143+
"package_path": "<workspace>/packages/c"
144+
},
145+
"command": "echo building c",
146+
"and_item_index": null,
147+
"cwd": "<workspace>/packages/c"
148+
},
149+
"kind": {
150+
"Leaf": {
151+
"InProcess": {
152+
"kind": {
153+
"Echo": {
154+
"strings": [
155+
"building",
156+
"c"
157+
],
158+
"trailing_newline": true
159+
}
160+
}
161+
}
162+
}
163+
}
164+
}
165+
]
166+
},
167+
"neighbors": []
168+
}
169+
],
170+
"concurrency_limit": 3
171+
}

0 commit comments

Comments
 (0)