All crates now pass `cargo clippy --workspace -- -D warnings` cleanly.
Fixes span six crates (hive-sh4re, hive-ag3nt, hive-c0re, hive-forge,
hive-priv, hive-matrix-mcp was already clean):
- doc_markdown: wrap snake_case, type names, constants in backticks
- collapsible_if / collapsible_match: fold nested ifs into let-chains
- duration_suboptimal_units: Duration::from_secs(N) → from_mins/from_hours
- implicit_hasher: allow on HashMap-param fns where generalization is risky
- items_after_statements: hoist use to function tops
- map(f).unwrap_or(x) → map_or(x, f); map(f).unwrap_or_else(g) → map_or_else
- is_ok_and / is_none_or in place of map().unwrap_or(bool)
- needless_continue: {} instead of continue in loop match arms
- match_same_arms: Ok(None) | Err(_) merged
- format_push_str: write!() instead of push_str(&format!())
- while let replaces loop { let Some(..) = x else { break } }
- struct_excessive_bools / dead_code: allow on purpose-built structs
- too_many_lines / too_many_arguments: allow where refactor not worth it
- unused_async: remove async from poll_once in bash_runner
- needless_borrow: fix &repo deref in hive-forge comments verb
- cast_possible_truncation: allow u64→usize in fetch_tail
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
67 lines
2.4 KiB
Rust
67 lines
2.4 KiB
Rust
//! Host-side vacuum of every per-agent events.sqlite. The harness
|
|
//! writes to `/agents/<name>/harness/hyperhive-events.sqlite`
|
|
//! (bind-mounted from `/var/lib/hyperhive/agents/<name>/harness/`);
|
|
//! we open the same file from the host every hour and delete rows
|
|
//! older than `KEEP_SECS`.
|
|
//! Age-only — no row cap — so a chatty turn doesn't lose history
|
|
//! sooner than a quiet one; disk pressure on a sustained burst is
|
|
//! a cheaper problem than a missing event when the operator is
|
|
//! debugging a regression. Keeping retention on the host means
|
|
//! agents don't need any cleanup wiring of their own, and a
|
|
//! misbehaving harness can't disable its own vacuum.
|
|
|
|
use std::path::Path;
|
|
use std::sync::Arc;
|
|
use std::time::{Duration, SystemTime, UNIX_EPOCH};
|
|
|
|
use rusqlite::{Connection, Result, params};
|
|
|
|
use crate::coordinator::Coordinator;
|
|
|
|
const VACUUM_INTERVAL: Duration = Duration::from_hours(1);
|
|
const KEEP_SECS: i64 = 7 * 24 * 3600;
|
|
|
|
/// Background loop: sweep every existing agent state dir hourly, run
|
|
/// the vacuum SQL against its events.sqlite if present. Errors are
|
|
/// logged but don't tear the loop down.
|
|
pub fn spawn(coord: &Arc<Coordinator>) {
|
|
let mut shutdown = coord.shutdown_rx();
|
|
tokio::spawn(async move {
|
|
loop {
|
|
sweep_once();
|
|
tokio::select! {
|
|
() = tokio::time::sleep(VACUUM_INTERVAL) => {}
|
|
_ = shutdown.changed() => {
|
|
tracing::info!("events vacuum: shutdown signal received");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
fn sweep_once() {
|
|
for name in Coordinator::kept_state_names() {
|
|
let path = Coordinator::agent_harness_dir(&name).join("hyperhive-events.sqlite");
|
|
if !path.exists() {
|
|
continue;
|
|
}
|
|
match vacuum_file(&path) {
|
|
Ok(0) => {}
|
|
Ok(n) => tracing::info!(agent = %name, removed = n, "events vacuum"),
|
|
Err(e) => tracing::warn!(agent = %name, error = ?e, "events vacuum failed"),
|
|
}
|
|
}
|
|
}
|
|
|
|
fn vacuum_file(path: &Path) -> Result<u64> {
|
|
let conn = Connection::open(path)?;
|
|
let now = SystemTime::now()
|
|
.duration_since(UNIX_EPOCH)
|
|
.ok()
|
|
.and_then(|d| i64::try_from(d.as_secs()).ok())
|
|
.unwrap_or(0);
|
|
let cutoff = now - KEEP_SECS;
|
|
let removed = conn.execute("DELETE FROM events WHERE ts < ?1", params![cutoff])?;
|
|
Ok(u64::try_from(removed).unwrap_or(0))
|
|
}
|