From 5237b13fca5dc2908ad15b2222320fb897b70d3a Mon Sep 17 00:00:00 2001 From: wan9chi Date: Tue, 21 Jul 2026 11:16:42 +0800 Subject: [PATCH] fix(cache): raise cache preallocation limit Co-authored-by: GPT-5.6 Codex --- CHANGELOG.md | 1 + crates/vite_task/src/session/cache/mod.rs | 47 ++++++++++++++++------- 2 files changed, 34 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e99c841f..a76e814d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ # Changelog +- **Fixed** The task cache now supports much larger automatically tracked input sets without hitting wincode's default 4 MiB sequence preallocation limit ([#554](https://github.com/voidzero-dev/vite-task/pull/554)). - **Fixed** npm workspace patterns beginning with `./` now discover matching packages correctly ([vite-plus#2201](https://github.com/voidzero-dev/vite-plus/issues/2201), [#547](https://github.com/voidzero-dev/vite-task/pull/547)). - **Fixed** Failures while forwarding output from a started task process no longer incorrectly say the process failed to spawn ([#506](https://github.com/voidzero-dev/vite-task/issues/506)). - **Fixed** An issue where Bun tasks on macOS did not rerun when files they read, wrote, or listed changed ([#532](https://github.com/voidzero-dev/vite-task/issues/532), [#542](https://github.com/voidzero-dev/vite-task/pull/542)). diff --git a/crates/vite_task/src/session/cache/mod.rs b/crates/vite_task/src/session/cache/mod.rs index 0991f60a..b18bccac 100644 --- a/crates/vite_task/src/session/cache/mod.rs +++ b/crates/vite_task/src/session/cache/mod.rs @@ -20,7 +20,7 @@ use vite_task_graph::config::ResolvedGlobConfig; use vite_task_plan::cache_metadata::{CacheMetadata, ExecutionCacheKey, SpawnFingerprint}; use wincode::{ SchemaRead, SchemaReadOwned, SchemaWrite, - config::{ConfigCore, DefaultConfig}, + config::{ConfigCore, Configuration}, error::{ReadResult, WriteResult}, io::{Reader, Writer}, }; @@ -30,6 +30,25 @@ use super::execute::{ pipe::StdOutput, }; +const TASK_CACHE_PREALLOCATION_SIZE_LIMIT: usize = 256 * 1024 * 1024; +type TaskCacheConfig = Configuration; +const TASK_CACHE_CONFIG: TaskCacheConfig = + Configuration::default().with_preallocation_size_limit::(); + +fn serialize_cache(value: &T) -> WriteResult> +where + T: SchemaWrite + ?Sized, +{ + wincode::config::serialize(value, TASK_CACHE_CONFIG) +} + +fn deserialize_cache(bytes: &[u8]) -> ReadResult +where + T: SchemaReadOwned, +{ + wincode::config::deserialize_exact(bytes, TASK_CACHE_CONFIG) +} + /// Cache lookup key identifying a task's execution configuration. /// /// # Key vs value design @@ -255,7 +274,7 @@ pub fn split_path(path: &str) -> (Option<&str>, &str) { /// its own cache warm across branch switches, and a cache from a different /// version is simply ignored (it lives in a directory this build never looks /// at) rather than aborting the run. Bumping the version starts a fresh cache. -const CACHE_SCHEMA_VERSION: u32 = 17; +const CACHE_SCHEMA_VERSION: u32 = 18; /// Name of the per-version subdirectory (e.g. `v14`) under the task-cache /// directory that holds the database and output archives for the current @@ -461,14 +480,14 @@ impl ExecutionCache { reason = "lock guard cannot be dropped earlier because prepared statement borrows connection" )] async fn get_key_by_value< - K: SchemaWrite, - V: SchemaReadOwned, + K: SchemaWrite, + V: SchemaReadOwned, >( &self, table: &str, key: &K, ) -> anyhow::Result> { - let key_blob = wincode::serialize(key)?; + let key_blob = serialize_cache(key)?; let value_blob = { let conn = self.conn.lock().await; #[expect( @@ -484,7 +503,7 @@ impl ExecutionCache { let Some(value_blob) = value_blob else { return Ok(None); }; - let value: V = wincode::deserialize(&value_blob)?; + let value: V = deserialize_cache(&value_blob)?; Ok(Some(value)) } @@ -507,16 +526,16 @@ impl ExecutionCache { reason = "lock guard must be held while executing the prepared statement" )] async fn upsert< - K: SchemaWrite, - V: SchemaWrite, + K: SchemaWrite, + V: SchemaWrite, >( &self, table: &str, key: &K, value: &V, ) -> anyhow::Result<()> { - let key_blob = wincode::serialize(key)?; - let value_blob = wincode::serialize(value)?; + let key_blob = serialize_cache(key)?; + let value_blob = serialize_cache(value)?; let conn = self.conn.lock().await; #[expect(clippy::disallowed_macros, reason = "SQL query string for rusqlite requires String")] let mut update_stmt = conn.prepare_cached(&format!( @@ -547,8 +566,8 @@ impl ExecutionCache { reason = "lock guard must be held while iterating over query rows" )] async fn list_table< - K: SchemaReadOwned + Serialize, - V: SchemaReadOwned + Serialize, + K: SchemaReadOwned + Serialize, + V: SchemaReadOwned + Serialize, >( &self, table: &str, @@ -564,8 +583,8 @@ impl ExecutionCache { while let Some(row) = rows.next()? { let key_blob: Vec = row.get(0)?; let value_blob: Vec = row.get(1)?; - let key: K = wincode::deserialize(&key_blob)?; - let value: V = wincode::deserialize(&value_blob)?; + let key: K = deserialize_cache(&key_blob)?; + let value: V = deserialize_cache(&value_blob)?; writeln!( out, "{} => {}",