Skip to content

Commit 9a33a73

Browse files
wan9chicodex
andauthored
refactor(cache): generalize tracked env query plumbing
## Motivation Prepare the env-query tracking path for the stacked `getEnvs({ prefix: "..." })` feature in #472. That feature needs the runner to distinguish different bulk env query kinds instead of treating every `getEnvs` request as a raw glob string. This PR keeps behavior unchanged by introducing an explicit env query type with only the existing glob variant, then threading that type through IPC, the recorder, cache fingerprinting, and reporting. The follow-up PR can add a prefix variant as a small behavior change on top of this plumbing. Co-authored-by: GPT-5 Codex <codex@openai.com>
1 parent c630d3e commit 9a33a73

10 files changed

Lines changed: 183 additions & 103 deletions

File tree

crates/vite_task/src/session/cache/display.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ pub fn format_cache_status_inline(cache_status: &CacheStatus) -> Option<Str> {
187187
format_input_change_str(*kind, path.as_str())
188188
}
189189
FingerprintMismatch::TrackedEnvChanged(mismatch)
190-
| FingerprintMismatch::TrackedEnvGlobChanged { mismatch, .. } => {
190+
| FingerprintMismatch::TrackedEnvQueryChanged { mismatch, .. } => {
191191
vite_str::format!("{mismatch}")
192192
}
193193
};
@@ -249,8 +249,10 @@ mod tests {
249249
#[test]
250250
fn inline_tracked_env_mismatch_preserves_kind() {
251251
let added = CacheStatus::Miss(CacheMiss::FingerprintMismatch(
252-
FingerprintMismatch::TrackedEnvGlobChanged {
253-
pattern: Str::from("PROBE_*"),
252+
FingerprintMismatch::TrackedEnvQueryChanged {
253+
query: crate::session::execute::fingerprint::TrackedEnvQuery::Glob(Str::from(
254+
"PROBE_*",
255+
)),
254256
mismatch: EnvMismatch::Added { name: Str::from("PROBE_C") },
255257
},
256258
));

crates/vite_task/src/session/cache/mod.rs

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,10 @@ use wincode::{
2525
io::{Reader, Writer},
2626
};
2727

28-
use super::execute::{fingerprint::PostRunFingerprint, pipe::StdOutput};
28+
use super::execute::{
29+
fingerprint::{PostRunFingerprint, TrackedEnvQuery},
30+
pipe::StdOutput,
31+
};
2932

3033
/// Cache lookup key identifying a task's execution configuration.
3134
///
@@ -211,9 +214,9 @@ pub enum FingerprintMismatch {
211214
},
212215
/// A runner-aware tool-tracked env var changed between runs.
213216
TrackedEnvChanged(EnvMismatch),
214-
/// A runner-aware tool-tracked env glob's match-set changed between runs.
215-
TrackedEnvGlobChanged {
216-
pattern: Str,
217+
/// A runner-aware tool-tracked bulk env query's match-set changed between runs.
218+
TrackedEnvQueryChanged {
219+
query: TrackedEnvQuery,
217220
mismatch: EnvMismatch,
218221
},
219222
}
@@ -224,8 +227,8 @@ impl From<crate::session::execute::fingerprint::PostRunMismatch> for Fingerprint
224227
match mismatch {
225228
PostRunMismatch::Input { kind, path } => Self::InputChanged { kind, path },
226229
PostRunMismatch::TrackedEnv(mismatch) => Self::TrackedEnvChanged(mismatch),
227-
PostRunMismatch::TrackedEnvGlob { pattern, mismatch } => {
228-
Self::TrackedEnvGlobChanged { pattern, mismatch }
230+
PostRunMismatch::TrackedEnvQuery { query, mismatch } => {
231+
Self::TrackedEnvQueryChanged { query, mismatch }
229232
}
230233
}
231234
}
@@ -252,7 +255,7 @@ pub fn split_path(path: &str) -> (Option<&str>, &str) {
252255
/// its own cache warm across branch switches, and a cache from a different
253256
/// version is simply ignored (it lives in a directory this build never looks
254257
/// at) rather than aborting the run. Bumping the version starts a fresh cache.
255-
const CACHE_SCHEMA_VERSION: u32 = 16;
258+
const CACHE_SCHEMA_VERSION: u32 = 17;
256259

257260
/// Name of the per-version subdirectory (e.g. `v14`) under the task-cache
258261
/// directory that holds the database and output archives for the current

crates/vite_task/src/session/execute/cache_update.rs

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use vite_task_server::Reports;
1111

1212
use super::{
1313
CacheState,
14-
fingerprint::{PathRead, PostRunFingerprint},
14+
fingerprint::{PathRead, PostRunFingerprint, TrackedEnvQuery},
1515
glob,
1616
spawn::ChildOutcome,
1717
};
@@ -37,7 +37,7 @@ struct TrackingOutcome {
3737
}
3838

3939
type TrackedEnvValues = BTreeMap<Str, Option<EnvValueHash>>;
40-
type TrackedEnvGlobValues = BTreeMap<Str, BTreeMap<Str, EnvValueHash>>;
40+
type TrackedEnvQueryValues = BTreeMap<TrackedEnvQuery, BTreeMap<Str, EnvValueHash>>;
4141

4242
/// Decide whether the finished run may be cached, and store it if so.
4343
///
@@ -122,7 +122,7 @@ pub(super) async fn update_cache(
122122
// Collect tool-reported tracked envs for the post-run fingerprint. Env
123123
// names that the user already declared are skipped because their values
124124
// are already part of the spawn fingerprint.
125-
let (tracked_envs, tracked_env_globs) = match collect_tracked_reports(reports, metadata) {
125+
let (tracked_envs, tracked_env_queries) = match collect_tracked_reports(reports, metadata) {
126126
Ok(tracked_reports) => tracked_reports,
127127
Err(err) => {
128128
return (
@@ -142,7 +142,7 @@ pub(super) async fn update_cache(
142142
workspace_root,
143143
&globbed_inputs,
144144
tracked_envs,
145-
tracked_env_globs,
145+
tracked_env_queries,
146146
) {
147147
Ok(fingerprint) => fingerprint,
148148
Err(err) => {
@@ -262,12 +262,12 @@ fn observe_fspy(
262262
fn collect_tracked_reports(
263263
reports: Option<&Reports>,
264264
metadata: &CacheMetadata,
265-
) -> anyhow::Result<(TrackedEnvValues, TrackedEnvGlobValues)> {
265+
) -> anyhow::Result<(TrackedEnvValues, TrackedEnvQueryValues)> {
266266
reports
267267
.map(|reports| {
268268
let tracked_envs = collect_tracked_envs(reports, metadata)?;
269-
let tracked_env_globs = collect_tracked_env_globs(reports)?;
270-
Ok::<_, anyhow::Error>((tracked_envs, tracked_env_globs))
269+
let tracked_env_queries = collect_tracked_env_queries(reports)?;
270+
Ok::<_, anyhow::Error>((tracked_envs, tracked_env_queries))
271271
})
272272
.transpose()
273273
.map(Option::unwrap_or_default)
@@ -332,12 +332,12 @@ fn collect_tracked_envs(
332332
Ok(tracked_envs)
333333
}
334334

335-
/// Select tool-reported env-glob records to embed in the post-run
335+
/// Select tool-reported bulk env query records to embed in the post-run
336336
/// fingerprint. The full match-set is stored as value hashes.
337-
fn collect_tracked_env_globs(reports: &Reports) -> anyhow::Result<TrackedEnvGlobValues> {
338-
let mut tracked_env_globs = BTreeMap::new();
337+
fn collect_tracked_env_queries(reports: &Reports) -> anyhow::Result<TrackedEnvQueryValues> {
338+
let mut tracked_env_queries = BTreeMap::new();
339339

340-
for (pattern, record) in &reports.tracked_get_envs {
340+
for (query, record) in &reports.tracked_get_envs {
341341
let mut matches = BTreeMap::new();
342342
for (name, value) in &record.matches {
343343
let name_str = name
@@ -348,10 +348,15 @@ fn collect_tracked_env_globs(reports: &Reports) -> anyhow::Result<TrackedEnvGlob
348348
})?;
349349
matches.insert(Str::from(name_str), EnvValueHash::new(value_str));
350350
}
351-
tracked_env_globs.insert(Str::from(pattern.as_ref()), matches);
351+
let query = match query {
352+
vite_task_server::EnvQuery::Glob(pattern) => {
353+
TrackedEnvQuery::Glob(Str::from(pattern.as_ref()))
354+
}
355+
};
356+
tracked_env_queries.insert(query, matches);
352357
}
353358

354-
Ok(tracked_env_globs)
359+
Ok(tracked_env_queries)
355360
}
356361

357362
/// Collect output files and create a tar.zst archive in the cache directory.

crates/vite_task/src/session/execute/fingerprint.rs

Lines changed: 58 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,13 @@ use crate::{
2424
session::cache::{EnvMismatch, InputChangeKind},
2525
};
2626

27+
#[derive(
28+
SchemaWrite, SchemaRead, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize,
29+
)]
30+
pub enum TrackedEnvQuery {
31+
Glob(Str),
32+
}
33+
2734
/// Path read access info
2835
#[derive(Debug, Clone, Copy)]
2936
pub struct PathRead {
@@ -44,18 +51,18 @@ pub struct PostRunFingerprint {
4451
/// context that served the original request.
4552
pub tracked_envs: BTreeMap<Str, Option<EnvValueHash>>,
4653

47-
/// Glob-pattern env queries (`getEnvs`) made with `tracked: true`.
48-
/// Outer key is the glob pattern, inner map is the match-set at execution
49-
/// time (name -> value hash). Validated at cache lookup by re-matching
50-
/// against the current env context and comparing the resulting set.
54+
/// Bulk env queries (`getEnvs`) made with `tracked: true`.
55+
/// Outer key is the query, inner map is the match-set at execution time
56+
/// (name -> value hash). Validated at cache lookup by re-matching against
57+
/// the current env context and comparing the resulting set.
5158
///
5259
/// Non-UTF-8 env names are never matched, saved, or treated as errors:
5360
/// they are not returned to the client, so their existence cannot affect
5461
/// task behavior. Values are stricter. A matched env must have a UTF-8
5562
/// value; the JS client errors when querying a matched non-UTF-8 value,
5663
/// and cache-hit validation treats a currently matched non-UTF-8 value as
5764
/// a changed mismatch so stale cached output is not replayed.
58-
pub tracked_env_globs: BTreeMap<Str, BTreeMap<Str, EnvValueHash>>,
65+
pub tracked_env_queries: BTreeMap<TrackedEnvQuery, BTreeMap<Str, EnvValueHash>>,
5966
}
6067

6168
/// A mismatch between the stored post-run fingerprint and the current state.
@@ -65,9 +72,9 @@ pub enum PostRunMismatch {
6572
Input { kind: InputChangeKind, path: RelativePathBuf },
6673
/// A tool-tracked env var changed value, appeared, or disappeared.
6774
TrackedEnv(EnvMismatch),
68-
/// A tool-tracked env glob's match-set changed between runs. Carries the
69-
/// first differing entry in env-name order.
70-
TrackedEnvGlob { pattern: Str, mismatch: EnvMismatch },
75+
/// A tool-tracked bulk env query's match-set changed between runs. Carries
76+
/// the first differing entry in env-name order.
77+
TrackedEnvQuery { query: TrackedEnvQuery, mismatch: EnvMismatch },
7178
}
7279

7380
/// Fingerprint for a single path (file or directory)
@@ -107,14 +114,14 @@ impl PostRunFingerprint {
107114
/// * `base_dir` - Workspace root for resolving relative paths
108115
/// * `globbed_inputs` - Prerun glob fingerprint; paths here are skipped
109116
/// * `tracked_envs` - Tool-requested env vars (name -> value hash), validated on lookup
110-
/// * `tracked_env_globs` - Tool-requested env globs (pattern -> match-set hashes)
117+
/// * `tracked_env_queries` - Tool-requested bulk env queries (query -> match-set hashes)
111118
#[tracing::instrument(level = "debug", skip_all, name = "create_post_run_fingerprint")]
112119
pub fn create(
113120
inferred_path_reads: &HashMap<RelativePathBuf, PathRead>,
114121
base_dir: &AbsolutePath,
115122
globbed_inputs: &BTreeMap<RelativePathBuf, u64>,
116123
tracked_envs: BTreeMap<Str, Option<EnvValueHash>>,
117-
tracked_env_globs: BTreeMap<Str, BTreeMap<Str, EnvValueHash>>,
124+
tracked_env_queries: BTreeMap<TrackedEnvQuery, BTreeMap<Str, EnvValueHash>>,
118125
) -> anyhow::Result<Self> {
119126
let inferred_inputs = inferred_path_reads
120127
.par_iter()
@@ -126,7 +133,7 @@ impl PostRunFingerprint {
126133
})
127134
.collect::<anyhow::Result<HashMap<_, _>>>()?;
128135

129-
Ok(Self { inferred_inputs, tracked_envs, tracked_env_globs })
136+
Ok(Self { inferred_inputs, tracked_envs, tracked_env_queries })
130137
}
131138

132139
/// Validates the fingerprint against current filesystem state and the
@@ -194,19 +201,19 @@ impl PostRunFingerprint {
194201
}
195202
}
196203

197-
for (pattern, stored_matches) in &self.tracked_env_globs {
198-
let current_matches = match match_env_glob(pattern.as_str(), unfiltered_envs)? {
199-
EnvGlobValidation::Matches(matches) => matches,
200-
EnvGlobValidation::NonUtf8Value(mismatch) => {
201-
return Ok(Some(PostRunMismatch::TrackedEnvGlob {
202-
pattern: pattern.clone(),
204+
for (query, stored_matches) in &self.tracked_env_queries {
205+
let current_matches = match match_env_query(query, unfiltered_envs)? {
206+
EnvQueryValidation::Matches(matches) => matches,
207+
EnvQueryValidation::NonUtf8Value(mismatch) => {
208+
return Ok(Some(PostRunMismatch::TrackedEnvQuery {
209+
query: query.clone(),
203210
mismatch,
204211
}));
205212
}
206213
};
207214
if let Some(mismatch) = first_env_glob_mismatch(stored_matches, &current_matches) {
208-
return Ok(Some(PostRunMismatch::TrackedEnvGlob {
209-
pattern: pattern.clone(),
215+
return Ok(Some(PostRunMismatch::TrackedEnvQuery {
216+
query: query.clone(),
210217
mismatch,
211218
}));
212219
}
@@ -216,34 +223,41 @@ impl PostRunFingerprint {
216223
}
217224
}
218225

219-
/// Build the current match-set for `pattern` by enumerating the given env
220-
/// snapshot and keeping UTF-8 names whose representation matches the glob. If
221-
/// a matching env has a non-UTF-8 value, return a changed mismatch so the stale
222-
/// cache entry is not replayed.
223-
fn match_env_glob(
224-
pattern: &str,
226+
/// Build the current match-set for `query` by enumerating the given env
227+
/// snapshot and keeping matching UTF-8 names. If a matching env has a non-UTF-8
228+
/// value, return a changed mismatch so the stale cache entry is not replayed.
229+
fn match_env_query(
230+
query: &TrackedEnvQuery,
231+
envs: &FxHashMap<Arc<OsStr>, Arc<OsStr>>,
232+
) -> anyhow::Result<EnvQueryValidation> {
233+
let TrackedEnvQuery::Glob(pattern) = query;
234+
let glob = vite_glob::env::EnvGlob::new(pattern.as_str())?;
235+
Ok(collect_matching_envs(envs, |name| glob.is_match(name)))
236+
}
237+
238+
fn collect_matching_envs(
225239
envs: &FxHashMap<Arc<OsStr>, Arc<OsStr>>,
226-
) -> anyhow::Result<EnvGlobValidation> {
227-
let glob = vite_glob::env::EnvGlob::new(pattern)?;
240+
is_match: impl Fn(&str) -> bool,
241+
) -> EnvQueryValidation {
228242
let mut matches = BTreeMap::new();
229243
for (name, value) in envs {
230244
let Some(name_str) = name.to_str() else {
231245
continue;
232246
};
233-
if !glob.is_match(name_str) {
247+
if !is_match(name_str) {
234248
continue;
235249
}
236250
let Some(value_str) = value.to_str() else {
237-
return Ok(EnvGlobValidation::NonUtf8Value(EnvMismatch::Changed {
251+
return EnvQueryValidation::NonUtf8Value(EnvMismatch::Changed {
238252
name: Str::from(name_str),
239-
}));
253+
});
240254
};
241255
matches.insert(Str::from(name_str), EnvValueHash::new(value_str));
242256
}
243-
Ok(EnvGlobValidation::Matches(matches))
257+
EnvQueryValidation::Matches(matches)
244258
}
245259

246-
enum EnvGlobValidation {
260+
enum EnvQueryValidation {
247261
Matches(BTreeMap<Str, EnvValueHash>),
248262
NonUtf8Value(EnvMismatch),
249263
}
@@ -516,9 +530,10 @@ mod tests {
516530

517531
#[test]
518532
fn validate_reports_current_non_utf8_tracked_env_glob_value_as_changed() {
519-
let mut tracked_env_globs = BTreeMap::new();
520-
tracked_env_globs.insert(Str::from("PROBE_*"), BTreeMap::new());
521-
let fingerprint = PostRunFingerprint { tracked_env_globs, ..PostRunFingerprint::default() };
533+
let mut tracked_env_queries = BTreeMap::new();
534+
tracked_env_queries.insert(TrackedEnvQuery::Glob(Str::from("PROBE_*")), BTreeMap::new());
535+
let fingerprint =
536+
PostRunFingerprint { tracked_env_queries, ..PostRunFingerprint::default() };
522537

523538
let mut unfiltered_envs = FxHashMap::default();
524539
unfiltered_envs.insert(
@@ -531,22 +546,23 @@ mod tests {
531546
fingerprint.validate(&workspace_root, &unfiltered_envs).expect("validation succeeds");
532547

533548
match mismatch {
534-
Some(PostRunMismatch::TrackedEnvGlob {
535-
pattern,
549+
Some(PostRunMismatch::TrackedEnvQuery {
550+
query,
536551
mismatch: EnvMismatch::Changed { name },
537552
}) => {
538-
assert_eq!(pattern.as_str(), "PROBE_*");
553+
assert_eq!(query, TrackedEnvQuery::Glob(Str::from("PROBE_*")));
539554
assert_eq!(name.as_str(), "PROBE_BAD");
540555
}
541-
other => panic!("expected changed tracked env glob mismatch, got {other:?}"),
556+
other => panic!("expected changed tracked env query mismatch, got {other:?}"),
542557
}
543558
}
544559

545560
#[test]
546561
fn validate_ignores_non_utf8_tracked_env_glob_names() {
547-
let mut tracked_env_globs = BTreeMap::new();
548-
tracked_env_globs.insert(Str::from("PROBE_*"), BTreeMap::new());
549-
let fingerprint = PostRunFingerprint { tracked_env_globs, ..PostRunFingerprint::default() };
562+
let mut tracked_env_queries = BTreeMap::new();
563+
tracked_env_queries.insert(TrackedEnvQuery::Glob(Str::from("PROBE_*")), BTreeMap::new());
564+
let fingerprint =
565+
PostRunFingerprint { tracked_env_queries, ..PostRunFingerprint::default() };
550566

551567
let mut unfiltered_envs = FxHashMap::default();
552568
unfiltered_envs.insert(

0 commit comments

Comments
 (0)