Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 18 additions & 8 deletions crates/vite_task/src/cache.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use std::{fmt::Display, io::Write, sync::Arc};
use std::{fmt::Display, io::Write, sync::Arc, time::Duration};

// use bincode::config::{Configuration, standard};
use bincode::{Decode, Encode, decode_from_slice, encode_to_vec};
use rusqlite::{Connection, OptionalExtension as _};
use rusqlite::{Connection, OptionalExtension as _, config::DbConfig};
use serde::{Deserialize, Serialize};
use tokio::sync::Mutex;
use vite_path::{AbsolutePath, AbsolutePathBuf};
Expand All @@ -22,6 +22,7 @@ use crate::{
pub struct CommandCacheValue {
pub post_run_fingerprint: PostRunFingerprint,
pub std_outputs: Arc<[StdOutput]>,
pub duration: Duration,
}

impl CommandCacheValue {
Expand All @@ -31,7 +32,11 @@ impl CommandCacheValue {
base_dir: &AbsolutePath,
) -> Result<Self, Error> {
let post_run_fingerprint = PostRunFingerprint::create(&executed_task, fs, base_dir)?;
Ok(Self { post_run_fingerprint, std_outputs: executed_task.std_outputs })
Ok(Self {
post_run_fingerprint,
std_outputs: executed_task.std_outputs,
duration: executed_task.duration,
})
}
}

Expand Down Expand Up @@ -86,7 +91,7 @@ impl TaskCache {

let db_path = path.join("cache.db");
let conn = Connection::open(db_path.as_path())?;
conn.execute_batch("PRAGMA journal_mode=WAL; BEGIN EXCLUSIVE;")?;
conn.execute_batch("PRAGMA journal_mode=WAL;")?;
loop {
let user_version: u32 = conn.query_one("PRAGMA user_version", (), |row| row.get(0))?;
match user_version {
Expand All @@ -100,13 +105,18 @@ impl TaskCache {
"CREATE TABLE taskrun_to_command (key BLOB PRIMARY KEY, value BLOB);",
(),
)?;
conn.execute("PRAGMA user_version = 1", ())?;
conn.execute("PRAGMA user_version = 2", ())?;
}
1 => break, // current version
2.. => return Err(Error::UnrecognizedDbVersion(user_version)),
1 => {
// old internal db version. reset
conn.set_db_config(DbConfig::SQLITE_DBCONFIG_RESET_DATABASE, true)?;
conn.execute("VACUUM", ())?;
conn.set_db_config(DbConfig::SQLITE_DBCONFIG_RESET_DATABASE, false)?;
Comment thread
branchseer marked this conversation as resolved.
}
Comment thread
branchseer marked this conversation as resolved.
2 => break, // current version
3.. => return Err(Error::UnrecognizedDbVersion(user_version)),
}
}
conn.execute_batch("COMMIT")?;
Ok(Self { conn: Mutex::new(conn), path: cache_path })
}

Expand Down
23 changes: 18 additions & 5 deletions crates/vite_task/src/execute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use std::{
path::PathBuf,
process::{ExitStatus, Stdio},
sync::{Arc, LazyLock, Mutex},
time::{Duration, Instant},
};

use bincode::{Decode, Encode};
Expand Down Expand Up @@ -53,6 +54,7 @@ pub struct ExecutedTask {
pub exit_status: ExitStatus,
pub path_reads: HashMap<RelativePathBuf, PathRead>,
pub path_writes: HashMap<RelativePathBuf, PathWrite>,
pub duration: Duration,
}

/// Collects stdout/stderr into `outputs` and at the same time writes them to the real stdout/stderr
Expand Down Expand Up @@ -371,17 +373,24 @@ pub async fn execute_task(
let child_stderr = child.stderr.take().unwrap();

let outputs = Mutex::new(Vec::<StdOutput>::new());
let ((), (), exit_status) = try_join3(

let ((), (), (exit_status, duration)) = try_join3(
collect_std_outputs(&outputs, child_stdout, OutputKind::StdOut),
collect_std_outputs(&outputs, child_stderr, OutputKind::StdErr),
async move { Ok(child.wait().await?) },
async move {
let start = Instant::now();
let exit_status = child.wait().await?;
Ok((exit_status, start.elapsed()))
},
)
.await?;

return Ok(ExecutedTask {
std_outputs: outputs.into_inner().unwrap().into(),
exit_status,
path_reads: HashMap::new(),
path_writes: HashMap::new(),
duration,
});
}
let mut cmd = spy.new_command(&task_parsed_command.program);
Expand Down Expand Up @@ -451,11 +460,15 @@ pub async fn execute_task(
Ok::<_, Error>((path_reads, path_writes))
};

let ((), (), (path_reads, path_writes), exit_status) = try_join4(
let ((), (), (path_reads, path_writes), (exit_status, duration)) = try_join4(
collect_std_outputs(&outputs, child_stdout, OutputKind::StdOut),
collect_std_outputs(&outputs, child_stderr, OutputKind::StdErr),
path_accesses_fut,
async move { Ok(child.wait().await?) },
async move {
let start = Instant::now();
let exit_status = child.wait().await?;
Ok((exit_status, start.elapsed()))
},
)
.await?;

Expand All @@ -470,7 +483,7 @@ pub async fn execute_task(

// let input_paths = gather_inputs(task, base_dir)?;

Ok(ExecutedTask { std_outputs: outputs.into(), exit_status, path_reads, path_writes })
Ok(ExecutedTask { std_outputs: outputs.into(), exit_status, path_reads, path_writes, duration })
}

#[expect(dead_code)]
Expand Down
9 changes: 6 additions & 3 deletions crates/vite_task/src/schedule.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::{process::ExitStatus, sync::Arc};
use std::{process::ExitStatus, sync::Arc, time::Duration};

use futures_core::future::BoxFuture;
use futures_util::future::FutureExt as _;
Expand Down Expand Up @@ -39,7 +39,10 @@ pub enum CacheStatus {
/// The task will be executed.
CacheMiss(CacheMiss),
/// Cache hit, will replay
CacheHit,
CacheHit {
/// Duration of the original execution
original_duration: Duration,
},
}

/// Status of a task execution
Expand Down Expand Up @@ -184,7 +187,7 @@ async fn get_cached_or_execute<'a>(
) -> Result<(CacheStatus, BoxFuture<'a, Result<ExitStatus, Error>>), Error> {
Ok(match cache.try_hit(&task, fs, base_dir).await? {
Ok(cache_task) => (
CacheStatus::CacheHit,
CacheStatus::CacheHit { original_duration: cache_task.duration },
({
async move {
if task.display_options.ignore_replay {
Expand Down
39 changes: 31 additions & 8 deletions crates/vite_task/src/ui.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::{fmt::Display, sync::LazyLock};
use std::{fmt::Display, sync::LazyLock, time::Duration};

use itertools::Itertools;
use owo_colors::{Style, Styled};
Expand Down Expand Up @@ -101,7 +101,7 @@ impl Display for PreExecutionStatus {
.style(CACHE_MISS_STYLE.dimmed())
)?;
}
CacheStatus::CacheHit => {
CacheStatus::CacheHit { .. } => {
if !self.display_options.ignore_replay {
if let Some(display_command) = &display_command {
write!(f, "{} ", display_command)?;
Expand Down Expand Up @@ -141,7 +141,7 @@ impl Display for ExecutionSummary {

for status in &self.execution_statuses {
match &status.pre_execution_status.cache_status {
CacheStatus::CacheHit => cache_hits += 1,
CacheStatus::CacheHit { .. } => cache_hits += 1,
CacheStatus::CacheMiss(_) => cache_misses += 1,
}

Expand Down Expand Up @@ -191,18 +191,40 @@ impl Display for ExecutionSummary {
let cache_rate =
if total > 0 { (cache_hits as f64 / total as f64 * 100.0) as u32 } else { 0 };

writeln!(
let total_duration = self
.execution_statuses
.iter()
.map(|status| {
if let CacheStatus::CacheHit { original_duration } =
&status.pre_execution_status.cache_status
{
*original_duration
} else {
Duration::ZERO
}
})
.sum::<std::time::Duration>();

write!(
f,
"{} {}% cache hit rate",
"{} {} cache hit rate",
"Performance:".style(Style::new().bold()),
cache_rate.to_string().style(if cache_rate >= 75 {
format_args!("{}%", cache_rate).style(if cache_rate >= 75 {
Style::new().green().bold()
} else if cache_rate >= 50 {
CACHE_MISS_STYLE
} else {
Style::new().red()
})
)?;
if total_duration > Duration::ZERO {
write!(
f,
", {:.2?} saved in total",
total_duration.style(Style::new().green().bold())
)?;
}
writeln!(f)?;
writeln!(f)?;

// Detailed task results
Expand Down Expand Up @@ -254,11 +276,12 @@ impl Display for ExecutionSummary {

// Cache status details (indented)
match &status.pre_execution_status.cache_status {
CacheStatus::CacheHit => {
CacheStatus::CacheHit { original_duration } => {
writeln!(
f,
" {}",
" {} {}",
"→ Cache hit - output replayed".style(Style::new().green()),
format!("- {:.2?} saved", original_duration).style(Style::new().green())
)?;
}
CacheStatus::CacheMiss(miss) => {
Expand Down
4 changes: 2 additions & 2 deletions packages/cli/snap-tests/associate-existing-cache/snap.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,12 @@ hello
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Statistics: 1 tasks • 1 cache hits • 0 cache misses
Performance: 100% cache hit rate
Performance: 100% cache hit rate, <variable>ms saved in total

Task Details:
────────────────────────────────────────────────
[1] script2: $ echo hello ✓
→ Cache hit - output replayed
→ Cache hit - output replayed - <variable>ms saved
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

> json-edit package.json '_.scripts.script2 = "echo world"' # update the command of script2
Expand Down
4 changes: 2 additions & 2 deletions packages/cli/snap-tests/cache-clean/snap.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,12 @@ hello
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Statistics: 1 tasks • 1 cache hits • 0 cache misses
Performance: 100% cache hit rate
Performance: 100% cache hit rate, <variable>ms saved in total

Task Details:
────────────────────────────────────────────────
[1] hello: $ echo hello ✓
→ Cache hit - output replayed
→ Cache hit - output replayed - <variable>ms saved
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

> vite cache clean # clean the cache
Expand Down
8 changes: 4 additions & 4 deletions packages/cli/snap-tests/cache-miss-command-change/snap.txt
Original file line number Diff line number Diff line change
Expand Up @@ -36,15 +36,15 @@ bar
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Statistics: 2 tasks • 1 cache hits • 1 cache misses
Performance: 50% cache hit rate
Performance: 50% cache hit rate, <variable>ms saved in total

Task Details:
────────────────────────────────────────────────
[1] hello(subcommand 0): $ echo baz ✓
→ Cache miss: command changed from echo foo to echo baz
·······················································
[2] hello: $ echo bar ✓
→ Cache hit - output replayed
→ Cache hit - output replayed - <variable>ms saved
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

> json-edit package.json '_.scripts.hello = "echo bar"' # remove the first subcommand, now the second subcommand becomes the first
Expand All @@ -58,10 +58,10 @@ bar
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Statistics: 1 tasks • 1 cache hits • 0 cache misses
Performance: 100% cache hit rate
Performance: 100% cache hit rate, <variable>ms saved in total

Task Details:
────────────────────────────────────────────────
[1] hello: $ echo bar ✓
→ Cache hit - output replayed
→ Cache hit - output replayed - <variable>ms saved
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,12 @@ $ node -p process.env.MY_ENV (✓ cache hit, replaying)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Statistics: 1 tasks • 1 cache hits • 0 cache misses
Performance: 100% cache hit rate
Performance: 100% cache hit rate, <variable>ms saved in total

Task Details:
────────────────────────────────────────────────
[1] hello: $ node -p process.env.MY_ENV ✓
→ Cache hit - output replayed
→ Cache hit - output replayed - <variable>ms saved
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

> json-edit vite-task.json '_.tasks.hello.passThroughEnvs.push("MY_ENV2")' # add a new pass through env
Expand Down
4 changes: 2 additions & 2 deletions packages/cli/snap-tests/exit-code/snap.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,12 @@ success
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Statistics: 1 tasks • 1 cache hits • 0 cache misses
Performance: 100% cache hit rate
Performance: 100% cache hit rate, <variable>ms saved in total

Task Details:
────────────────────────────────────────────────
[1] script1: $ echo success ✓
→ Cache hit - output replayed
→ Cache hit - output replayed - <variable>ms saved
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

[1]> vite run script2 # script2 should be failure and not cache
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,12 @@ a
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Statistics: 1 tasks • 1 cache hits • 0 cache misses
Performance: 100% cache hit rate
Performance: 100% cache hit rate, <variable>ms saved in total

Task Details:
────────────────────────────────────────────────
[1] echo: $ echo a ✓
→ Cache hit - output replayed
→ Cache hit - output replayed - <variable>ms saved
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

> vite run echo -- b # should hit the cache created in step 2
Expand All @@ -62,10 +62,10 @@ b
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Statistics: 1 tasks • 1 cache hits • 0 cache misses
Performance: 100% cache hit rate
Performance: 100% cache hit rate, <variable>ms saved in total

Task Details:
────────────────────────────────────────────────
[1] echo: $ echo b ✓
→ Cache hit - output replayed
→ Cache hit - output replayed - <variable>ms saved
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
8 changes: 4 additions & 4 deletions packages/cli/snap-tests/individual-cache-for-envs/snap.txt
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,12 @@ $ node -p process.env.FOO (✓ cache hit, replaying)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Statistics: 1 tasks • 1 cache hits • 0 cache misses
Performance: 100% cache hit rate
Performance: 100% cache hit rate, <variable>ms saved in total

Task Details:
────────────────────────────────────────────────
[1] hello: $ node -p process.env.FOO ✓
→ Cache hit - output replayed
→ Cache hit - output replayed - <variable>ms saved
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

> FOO=2 vite run hello # hit cache created in step 2
Expand All @@ -62,10 +62,10 @@ $ node -p process.env.FOO (✓ cache hit, replaying)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Statistics: 1 tasks • 1 cache hits • 0 cache misses
Performance: 100% cache hit rate
Performance: 100% cache hit rate, <variable>ms saved in total

Task Details:
────────────────────────────────────────────────
[1] hello: $ node -p process.env.FOO ✓
→ Cache hit - output replayed
→ Cache hit - output replayed - <variable>ms saved
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Loading
Loading