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: 21 additions & 5 deletions crates/turborepo-lib/src/run/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -987,6 +987,7 @@ impl Run {
) -> Option<(
turborepo_repository::toolchain::CompileCacheEndpoint,
tokio::sync::broadcast::Sender<()>,
std::sync::Arc<turborepo_sccache_proxy::IncrementalCacheStats>,
)> {
if !self.opts.future_flags.experimental_cargo_sccache {
return None;
Expand Down Expand Up @@ -1075,13 +1076,14 @@ impl Run {
server_port: turborepo_sccache_proxy::derive_server_port(&self.repo_root),
};
let shutdown = server.shutdown_handle();
let stats = server.stats();
info!("sccache compile cache proxy listening on {}", endpoint.url);
tokio::spawn(async move {
if let Err(err) = server.run().await {
error!("sccache compile cache proxy error: {err}");
}
});
Some((endpoint, shutdown))
Some((endpoint, shutdown, stats))
}

async fn cleanup_proxy(
Expand Down Expand Up @@ -1288,10 +1290,11 @@ impl Run {
drop(_setup_span);

let sccache_proxy = self.start_sccache_proxy_if_needed().await;
let (compile_cache_endpoint, sccache_shutdown) = match sccache_proxy {
Some((endpoint, shutdown)) => (Some(endpoint), Some(shutdown)),
None => (None, None),
};
let (compile_cache_endpoint, sccache_shutdown, incremental_cache_stats) =
match sccache_proxy {
Some((endpoint, shutdown, stats)) => (Some(endpoint), Some(shutdown), Some(stats)),
None => (None, None, None),
};

let mut visitor = Visitor::new(
self.pkg_dep_graph.clone(),
Expand Down Expand Up @@ -1365,6 +1368,18 @@ impl Run {
self.processes.stop().await;
}

// Snapshot the incremental-cache traffic now that every task (and
// its tools) has finished. `None` when the proxy never started —
// the summary line only appears for runs that attempted
// incremental caching.
let incremental_cache = incremental_cache_stats.map(|stats| {
let snapshot = stats.snapshot();
turborepo_run_summary::IncrementalCacheSummary {
hits: snapshot.hits,
misses: snapshot.misses,
}
});

visitor
.finish(
exit_code,
Expand All @@ -1374,6 +1389,7 @@ impl Run {
&self.env_at_execution_start,
&self.scm,
self.opts.scope_opts.pkg_inference_root.as_deref(),
incremental_cache,
)
.await?;

Expand Down
3 changes: 3 additions & 0 deletions crates/turborepo-lib/src/task_graph/visitor/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1015,6 +1015,7 @@ impl<'a> Visitor<'a> {
env_at_execution_start,
scm,
))]
#[allow(clippy::too_many_arguments)]
pub(crate) async fn finish(
self,
exit_code: i32,
Expand All @@ -1024,6 +1025,7 @@ impl<'a> Visitor<'a> {
env_at_execution_start: &EnvironmentVariableMap,
scm: &SCM,
pkg_inference_root: Option<&AnchoredSystemPath>,
incremental_cache: Option<turborepo_run_summary::IncrementalCacheSummary>,
) -> Result<(), Error> {
let Self {
package_graph,
Expand Down Expand Up @@ -1077,6 +1079,7 @@ impl<'a> Visitor<'a> {
scm,
is_watch,
Some(task_hasher.external_deps_hash_cache()),
incremental_cache,
)
.await?)
}
Expand Down
68 changes: 68 additions & 0 deletions crates/turborepo-run-summary/src/execution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,36 @@ pub struct ExecutionSummary<'a> {
pub exit_code: i32,
}

/// Totals for reuse below the task boundary: work units a tool running
/// inside a task fetched from (hits) or had to rebuild (misses) via the
/// incremental cache. Toolchain-agnostic — for Rust this is sccache
/// compile units served through the Remote Cache; other toolchains can
/// contribute the same shape.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct IncrementalCacheSummary {
pub hits: u64,
pub misses: u64,
}

/// The summary footer's "Incremental cache" line: reuse below the task
/// boundary. `None` — and therefore absent from the output — unless the
/// run actually exchanged work units: a repo that never engaged
/// incremental caching should not see the line at all.
fn incremental_cache_line(
ui: ColorConfig,
incremental_cache: Option<IncrementalCacheSummary>,
) -> Option<(&'static str, String)> {
let incremental = incremental_cache.filter(|s| s.hits + s.misses > 0)?;
Some((
"Incremental cache",
format!(
"{}, {} misses",
color!(ui, BOLD, "{} hits", incremental.hits),
incremental.misses,
),
))
}

impl<'a> ExecutionSummary<'a> {
pub fn new(
command: String,
Expand Down Expand Up @@ -77,6 +107,7 @@ impl<'a> ExecutionSummary<'a> {
ui: ColorConfig,
path: AbsoluteSystemPathBuf,
failed_tasks: Vec<&T>,
incremental_cache: Option<IncrementalCacheSummary>,
) {
let maybe_full_turbo = if self.cached == self.attempted && self.attempted > 0 {
match std::env::var("TERM_PROGRAM").as_deref() {
Expand Down Expand Up @@ -115,6 +146,10 @@ impl<'a> ExecutionSummary<'a> {
),
];

if let Some(line) = incremental_cache_line(ui, incremental_cache) {
line_data.insert(2, line);
}

if path.exists() {
line_data.push(("Summary", path.to_string()));
}
Expand Down Expand Up @@ -474,6 +509,39 @@ mod test {

use super::*;

#[test]
fn test_incremental_cache_line_conditional() {
// strip_ansi: label/value text is what users see uncolored.
let ui = ColorConfig::new(true);

// No incremental caching attempted: no line.
assert!(incremental_cache_line(ui, None).is_none());

// Attempted, but no work units exchanged (e.g. every task was a
// task-cache hit and no tool ever ran): still no line.
assert!(
incremental_cache_line(ui, Some(IncrementalCacheSummary { hits: 0, misses: 0 }))
.is_none()
);

// Real traffic renders, misses included even when zero.
let (label, value) = incremental_cache_line(
ui,
Some(IncrementalCacheSummary {
hits: 407,
misses: 12,
}),
)
.unwrap();
assert_eq!(label, "Incremental cache");
assert_eq!(value, "407 hits, 12 misses");

let (_, value) =
incremental_cache_line(ui, Some(IncrementalCacheSummary { hits: 0, misses: 3 }))
.unwrap();
assert_eq!(value, "0 hits, 3 misses");
}

#[tokio::test]
async fn test_multiple_tasks() -> Result<(), Box<dyn std::error::Error>> {
let summary = ExecutionTracker::new();
Expand Down
3 changes: 2 additions & 1 deletion crates/turborepo-run-summary/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ mod tracker;

pub use duration::TurboDuration;
pub use execution::{
ExecutionSummary, ExecutionTracker, SummaryState, TaskState, TaskSummaryInfo, TaskTracker,
ExecutionSummary, ExecutionTracker, IncrementalCacheSummary, SummaryState, TaskState,
TaskSummaryInfo, TaskTracker,
};
pub use global_hash::{GlobalEnvConfiguration, GlobalEnvVarSummary, GlobalHashSummary};
pub use observability::Handle as ObservabilityHandle;
Expand Down
17 changes: 13 additions & 4 deletions crates/turborepo-run-summary/src/tracker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ use turborepo_ui::{BOLD, BOLD_CYAN, ColorConfig, GREY, color, cprintln, cwriteln

use crate::{
GlobalHashSummary, SCMState, TaskTracker,
execution::{ExecutionSummary, ExecutionTracker, TaskState},
execution::{ExecutionSummary, ExecutionTracker, IncrementalCacheSummary, TaskState},
observability::Handle as ObservabilityHandle,
task::{SinglePackageTaskSummary, TaskSummary},
task_factory::TaskSummaryFactory,
Expand Down Expand Up @@ -235,6 +235,7 @@ impl RunTracker {
scm: &SCM,
is_watch: bool,
external_deps_hashes: Option<&HashMap<String, String>>,
incremental_cache: Option<IncrementalCacheSummary>,
) -> Result<(), Error>
where
E: EngineInfo + Sync,
Expand Down Expand Up @@ -274,7 +275,7 @@ impl RunTracker {
);

let path = repo_root.join_components(&[".turbo", "runs", "dummy.json"]);
execution.print(ui, path, failed_tasks.iter().collect());
execution.print(ui, path, failed_tasks.iter().collect(), incremental_cache);
}

return Ok(());
Expand Down Expand Up @@ -307,7 +308,14 @@ impl RunTracker {
.await?;

run_summary
.finish(end_time, exit_code, pkg_dep_graph, ui, is_watch)
.finish(
end_time,
exit_code,
pkg_dep_graph,
ui,
is_watch,
incremental_cache,
)
.await
}

Expand Down Expand Up @@ -398,6 +406,7 @@ impl<'a> RunSummary<'a> {
pkg_dep_graph: &PackageGraph,
ui: ColorConfig,
is_watch: bool,
incremental_cache: Option<IncrementalCacheSummary>,
) -> Result<(), Error> {
// Handle observability shutdown before the dry run check to ensure graceful
// cleanup even when metrics are not being emitted.
Expand Down Expand Up @@ -428,7 +437,7 @@ impl<'a> RunSummary<'a> {
if !is_watch && let Some(execution) = &self.execution {
let path = self.get_path();
let failed_tasks = self.get_failed_tasks();
execution.print(ui, path, failed_tasks);
execution.print(ui, path, failed_tasks, incremental_cache);
}

Ok(())
Expand Down
Loading
Loading