22
33pub mod display;
44
5- use std:: { fmt:: Display , fs:: File , io:: Write , sync:: Arc , time:: Duration } ;
5+ use std:: { collections :: BTreeMap , fmt:: Display , fs:: File , io:: Write , sync:: Arc , time:: Duration } ;
66
77use bincode:: { Decode , Encode , decode_from_slice, encode_to_vec} ;
88// Re-export display functions for convenience
99pub use display:: { format_cache_status_inline, format_cache_status_summary} ;
1010use rusqlite:: { Connection , OptionalExtension as _, config:: DbConfig } ;
1111use serde:: { Deserialize , Serialize } ;
1212use tokio:: sync:: Mutex ;
13- use vite_path:: { AbsolutePath , AbsolutePathBuf } ;
13+ use vite_path:: { AbsolutePath , AbsolutePathBuf , RelativePathBuf } ;
14+ use vite_task_graph:: config:: ResolvedInputConfig ;
1415use vite_task_plan:: cache_metadata:: { CacheMetadata , ExecutionCacheKey , SpawnFingerprint } ;
1516
1617use super :: execute:: {
1718 fingerprint:: { PostRunFingerprint , PostRunFingerprintMismatch } ,
1819 spawn:: StdOutput ,
1920} ;
2021
22+ /// Pre-run fingerprint computed at execution time.
23+ /// Contains spawn fingerprint, input configuration, and explicit input file hashes.
24+ #[ derive( Debug , Encode , Decode , Serialize , PartialEq , Eq , Clone ) ]
25+ pub struct PreRunFingerprint {
26+ /// The spawn fingerprint (command, args, cwd, envs)
27+ pub spawn_fingerprint : SpawnFingerprint ,
28+ /// Resolved input configuration that affects cache behavior.
29+ pub input_config : ResolvedInputConfig ,
30+ /// Base directory for glob patterns, relative to workspace root.
31+ /// This is where the task is defined (package path).
32+ pub glob_base : RelativePathBuf ,
33+ /// Hashes of explicit input files computed from positive globs.
34+ /// Files matching negative globs are already filtered out.
35+ /// Path is relative to workspace root, value is xxHash3_64 of file content.
36+ pub globbed_inputs : BTreeMap < RelativePathBuf , u64 > ,
37+ }
38+
2139/// Command cache value, for validating post-run fingerprint after the spawn fingerprint is matched,
2240/// and replaying the std outputs if validated.
2341#[ derive( Debug , Encode , Decode , Serialize ) ]
@@ -85,23 +103,23 @@ impl ExecutionCache {
85103 0 => {
86104 // fresh new db
87105 conn. execute (
88- "CREATE TABLE spawn_fingerprint_cache (key BLOB PRIMARY KEY, value BLOB);" ,
106+ "CREATE TABLE pre_run_fingerprint_cache (key BLOB PRIMARY KEY, value BLOB);" ,
89107 ( ) ,
90108 ) ?;
91109 conn. execute (
92110 "CREATE TABLE execution_key_to_fingerprint (key BLOB PRIMARY KEY, value BLOB);" ,
93111 ( ) ,
94112 ) ?;
95- conn. execute ( "PRAGMA user_version = 4 " , ( ) ) ?;
113+ conn. execute ( "PRAGMA user_version = 6 " , ( ) ) ?;
96114 }
97- 1 ..=3 => {
115+ 1 ..=5 => {
98116 // old internal db version. reset
99117 conn. set_db_config ( DbConfig :: SQLITE_DBCONFIG_RESET_DATABASE , true ) ?;
100118 conn. execute ( "VACUUM" , ( ) ) ?;
101119 conn. set_db_config ( DbConfig :: SQLITE_DBCONFIG_RESET_DATABASE , false ) ?;
102120 }
103- 4 => break , // current version
104- 5 .. => {
121+ 6 => break , // current version
122+ 7 .. => {
105123 return Err ( anyhow:: anyhow!( "Unrecognized database version: {}" , user_version) ) ;
106124 }
107125 }
@@ -116,26 +134,58 @@ impl ExecutionCache {
116134 Ok ( ( ) )
117135 }
118136
119- /// Try to hit cache with spawn fingerprint.
137+ /// Try to hit cache with pre-run fingerprint (spawn + globbed inputs) .
120138 /// Returns `Ok(Ok(cache_value))` on cache hit, `Ok(Err(cache_miss))` on miss.
139+ ///
140+ /// # Arguments
141+ /// * `cache_metadata` - Cache metadata from plan stage
142+ /// * `globbed_inputs` - Hashes of explicit input files computed from positive globs
143+ /// * `base_dir` - Workspace root for validating post-run fingerprint
121144 pub async fn try_hit (
122145 & self ,
123146 cache_metadata : & CacheMetadata ,
147+ globbed_inputs : BTreeMap < RelativePathBuf , u64 > ,
124148 base_dir : & AbsolutePath ,
125149 ) -> anyhow:: Result < Result < CommandCacheValue , CacheMiss > > {
126150 let spawn_fingerprint = & cache_metadata. spawn_fingerprint ;
127151 let execution_cache_key = & cache_metadata. execution_cache_key ;
152+ let input_config = & cache_metadata. input_config ;
153+
154+ // Convert absolute glob_base to relative for cache key
155+ let glob_base = cache_metadata
156+ . glob_base
157+ . strip_prefix ( base_dir)
158+ . map_err ( |e| anyhow:: anyhow!( "failed to strip prefix from glob_base: {e}" ) ) ?
159+ . ok_or_else ( || {
160+ anyhow:: anyhow!(
161+ "glob_base {:?} is not inside workspace {:?}" ,
162+ cache_metadata. glob_base,
163+ base_dir
164+ )
165+ } ) ?;
166+
167+ // Build pre-run fingerprint combining spawn fingerprint, input config, and globbed inputs
168+ let pre_run_fingerprint = PreRunFingerprint {
169+ spawn_fingerprint : spawn_fingerprint. clone ( ) ,
170+ input_config : input_config. clone ( ) ,
171+ glob_base,
172+ globbed_inputs,
173+ } ;
128174
129- // Try to directly find the cache by spawn fingerprint first
130- if let Some ( cache_value) = self . get_by_spawn_fingerprint ( spawn_fingerprint) . await ? {
131- // Validate post-run fingerprint
132- if let Some ( post_run_fingerprint_mismatch) =
133- cache_value. post_run_fingerprint . validate ( base_dir) ?
134- {
135- // Found the cache with the same spawn fingerprint, but the post-run fingerprint mismatches
136- return Ok ( Err ( CacheMiss :: FingerprintMismatch (
137- FingerprintMismatch :: PostRunFingerprintMismatch ( post_run_fingerprint_mismatch) ,
138- ) ) ) ;
175+ // Try to directly find the cache by pre-run fingerprint first
176+ if let Some ( cache_value) = self . get_by_pre_run_fingerprint ( & pre_run_fingerprint) . await ? {
177+ // Validate post-run fingerprint (inferred inputs) only if auto inference is enabled
178+ if input_config. includes_auto {
179+ if let Some ( post_run_fingerprint_mismatch) =
180+ cache_value. post_run_fingerprint . validate ( base_dir) ?
181+ {
182+ // Found the cache with the same pre-run fingerprint, but the post-run fingerprint mismatches
183+ return Ok ( Err ( CacheMiss :: FingerprintMismatch (
184+ FingerprintMismatch :: PostRunFingerprintMismatch (
185+ post_run_fingerprint_mismatch,
186+ ) ,
187+ ) ) ) ;
188+ }
139189 }
140190 // Associate the execution key to the spawn fingerprint if not already,
141191 // so that next time we can find it and report spawn fingerprint mismatch
@@ -144,8 +194,8 @@ impl ExecutionCache {
144194 return Ok ( Ok ( cache_value) ) ;
145195 }
146196
147- // No cache found with the current spawn fingerprint,
148- // check if execution key maps to different fingerprint
197+ // No cache found with the current pre-run fingerprint,
198+ // check if execution key maps to different spawn fingerprint
149199 if let Some ( old_spawn_fingerprint) =
150200 self . get_fingerprint_by_execution_key ( execution_cache_key) . await ?
151201 {
@@ -163,15 +213,45 @@ impl ExecutionCache {
163213 }
164214
165215 /// Update cache after successful execution.
216+ ///
217+ /// # Arguments
218+ /// * `cache_metadata` - Cache metadata from plan stage
219+ /// * `globbed_inputs` - Hashes of explicit input files computed from positive globs
220+ /// * `base_dir` - Workspace root for converting absolute paths to relative
221+ /// * `cache_value` - The cache value to store (outputs and post-run fingerprint)
166222 pub async fn update (
167223 & self ,
168224 cache_metadata : & CacheMetadata ,
225+ globbed_inputs : BTreeMap < RelativePathBuf , u64 > ,
226+ base_dir : & AbsolutePath ,
169227 cache_value : CommandCacheValue ,
170228 ) -> anyhow:: Result < ( ) > {
171229 let spawn_fingerprint = & cache_metadata. spawn_fingerprint ;
172230 let execution_cache_key = & cache_metadata. execution_cache_key ;
231+ let input_config = & cache_metadata. input_config ;
232+
233+ // Convert absolute glob_base to relative for cache key
234+ let glob_base = cache_metadata
235+ . glob_base
236+ . strip_prefix ( base_dir)
237+ . map_err ( |e| anyhow:: anyhow!( "failed to strip prefix from glob_base: {e}" ) ) ?
238+ . ok_or_else ( || {
239+ anyhow:: anyhow!(
240+ "glob_base {:?} is not inside workspace {:?}" ,
241+ cache_metadata. glob_base,
242+ base_dir
243+ )
244+ } ) ?;
173245
174- self . upsert_spawn_fingerprint_cache ( spawn_fingerprint, & cache_value) . await ?;
246+ // Build pre-run fingerprint combining spawn fingerprint, input config, and globbed inputs
247+ let pre_run_fingerprint = PreRunFingerprint {
248+ spawn_fingerprint : spawn_fingerprint. clone ( ) ,
249+ input_config : input_config. clone ( ) ,
250+ glob_base,
251+ globbed_inputs,
252+ } ;
253+
254+ self . upsert_pre_run_fingerprint_cache ( & pre_run_fingerprint, & cache_value) . await ?;
175255 self . upsert_execution_key_to_fingerprint ( execution_cache_key, spawn_fingerprint) . await ?;
176256 Ok ( ( ) )
177257 }
@@ -197,11 +277,11 @@ impl ExecutionCache {
197277 Ok ( Some ( value) )
198278 }
199279
200- async fn get_by_spawn_fingerprint (
280+ async fn get_by_pre_run_fingerprint (
201281 & self ,
202- spawn_fingerprint : & SpawnFingerprint ,
282+ pre_run_fingerprint : & PreRunFingerprint ,
203283 ) -> anyhow:: Result < Option < CommandCacheValue > > {
204- self . get_key_by_value ( "spawn_fingerprint_cache " , spawn_fingerprint ) . await
284+ self . get_key_by_value ( "pre_run_fingerprint_cache " , pre_run_fingerprint ) . await
205285 }
206286
207287 async fn get_fingerprint_by_execution_key (
@@ -227,12 +307,12 @@ impl ExecutionCache {
227307 Ok ( ( ) )
228308 }
229309
230- async fn upsert_spawn_fingerprint_cache (
310+ async fn upsert_pre_run_fingerprint_cache (
231311 & self ,
232- spawn_fingerprint : & SpawnFingerprint ,
312+ pre_run_fingerprint : & PreRunFingerprint ,
233313 cache_value : & CommandCacheValue ,
234314 ) -> anyhow:: Result < ( ) > {
235- self . upsert ( "spawn_fingerprint_cache " , spawn_fingerprint , cache_value) . await
315+ self . upsert ( "pre_run_fingerprint_cache " , pre_run_fingerprint , cache_value) . await
236316 }
237317
238318 async fn upsert_execution_key_to_fingerprint (
@@ -273,9 +353,12 @@ impl ExecutionCache {
273353 & mut out,
274354 )
275355 . await ?;
276- out. write_all ( b"------- spawn_fingerprint_cache -------\n " ) ?;
277- self . list_table :: < SpawnFingerprint , CommandCacheValue > ( "spawn_fingerprint_cache" , & mut out)
278- . await ?;
356+ out. write_all ( b"------- pre_run_fingerprint_cache -------\n " ) ?;
357+ self . list_table :: < PreRunFingerprint , CommandCacheValue > (
358+ "pre_run_fingerprint_cache" ,
359+ & mut out,
360+ )
361+ . await ?;
279362 Ok ( ( ) )
280363 }
281364}
0 commit comments