diff --git a/src/daemon.rs b/src/daemon.rs index fc3052a..aa0715d 100644 --- a/src/daemon.rs +++ b/src/daemon.rs @@ -32,7 +32,8 @@ CREATE TABLE IF NOT EXISTS events ( start_time INTEGER, end_time INTEGER, duration_ms INTEGER, - total_bytes INTEGER + total_bytes INTEGER, + phases TEXT ); CREATE INDEX IF NOT EXISTS idx_events_type_start ON events(event_type, start_time, duration_ms, total_bytes); CREATE INDEX IF NOT EXISTS idx_events_start_cover ON events(start_time, event_type, duration_ms, total_bytes); @@ -59,6 +60,7 @@ const SCHEMA_HASHES: &[u32] = &[ 0x7c09711e, // v3: idx_events_start_time(start_time) 0x24268227, // v4: idx_events_type_start extended to cover (duration_ms, total_bytes); idx_events_start_cover added 0xfcbb3598, // v5: daily_stats + daily_cache_stats tables + 0x2d396861, // v6: phases TEXT column on events — compact "phase:elapsed_secs,..." per build ]; const SCHEMA_VERSION: u32 = SCHEMA_HASHES.len() as u32; const _: () = assert!( @@ -140,6 +142,7 @@ const MIGRATIONS: &[(u32, &str)] = &[ count = count + excluded.count, total_ms = total_ms + excluded.total_ms; "), + (6, "ALTER TABLE events ADD COLUMN phases TEXT;"), ]; #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] @@ -300,6 +303,8 @@ struct Activity { start_time: DateTime, fields: Vec, total_bytes: u64, + // (phase_name, elapsed_secs_since_start) — only populated for Build activities. + phase_times: Vec<(String, i64)>, } // Per-day aggregate cache, maintained in memory for the current calendar day (UTC). @@ -712,6 +717,7 @@ fn process_event(event: NixEvent, state: &Arc>, db: &Arc { @@ -727,6 +733,13 @@ fn process_event(event: NixEvent, state: &Arc>, db: &Arc 0 { act.total_bytes = total; } } } + if res_type == ResultType::SetPhase { + if let Some(phase_name) = fields.get(0).and_then(|v| v.as_str()) { + let elapsed = Utc::now().signed_duration_since(act.start_time).num_seconds(); + assert!(elapsed >= 0, "phase elapsed time must be non-negative"); + act.phase_times.push((phase_name.to_string(), elapsed)); + } + } } } NixEvent::Stop { id } => { @@ -741,6 +754,12 @@ fn process_event(event: NixEvent, state: &Arc>, db: &Arc = if act.phase_times.is_empty() { + None + } else { + Some(act.phase_times.iter().map(|(name, secs)| format!("{}:{}", name, secs)).collect::>().join(",")) + }; + let drv_path = act.fields.get(0).and_then(|v| v.as_str()).map(|s| s.to_string()); // Both Substitute (fields[1] = substituter URL) and QueryPathInfo (fields[1] = cache URL) // have the same layout. Store cache_url for both. @@ -796,13 +815,14 @@ fn process_event(event: NixEvent, state: &Arc>, db: &Arc