events.sqlite vacuum moves host-side
retention is a host concern — agents have no business doing their own cleanup, and a misbehaving harness could skip it. drop spawn_events_vacuum from both hive-ag3nt and hive-m1nd, drop the matching Bus::vacuum + EventStore::vacuum methods. new hive_c0re::events_vacuum module sweeps every existing agents/<name>/state/hyperhive-events.sqlite on the same hourly cadence as the broker vacuum. same two-stage delete (older than 7 days, trim to 2000 newest). called from main alongside broker vacuum. also: server-side state badge entered into todo.md (today's badge is derived client-side from sse, fine for idle/thinking but a state machine that grows compacting/napping wants authoritative status from the harness).
This commit is contained in:
parent
897e7c07ae
commit
89ccc5e6c5
6 changed files with 89 additions and 63 deletions
|
|
@ -58,7 +58,6 @@ async fn main() -> Result<()> {
|
|||
let login_state = Arc::new(Mutex::new(initial));
|
||||
let ui_state = login_state.clone();
|
||||
let bus = Bus::new();
|
||||
spawn_events_vacuum(bus.clone());
|
||||
let ui_bus = bus.clone();
|
||||
let ui_socket = cli.socket.clone();
|
||||
tokio::spawn(async move {
|
||||
|
|
@ -154,23 +153,6 @@ async fn serve(
|
|||
}
|
||||
}
|
||||
|
||||
/// Vacuum events older than 7 days, cap to 2000 most-recent rows.
|
||||
/// Runs immediately, then hourly.
|
||||
fn spawn_events_vacuum(bus: Bus) {
|
||||
tokio::spawn(async move {
|
||||
let interval_secs = 3600u64;
|
||||
let keep_secs: i64 = 7 * 24 * 3600;
|
||||
let keep_rows = 2000;
|
||||
loop {
|
||||
let n = bus.vacuum(keep_secs, keep_rows);
|
||||
if n > 0 {
|
||||
tracing::info!(removed = n, "events vacuum");
|
||||
}
|
||||
tokio::time::sleep(Duration::from_secs(interval_secs)).await;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// Per-turn user prompt. The role/tools/etc. is in the system prompt
|
||||
/// (`prompts/agent.md` → `claude --system-prompt-file`); this is just the
|
||||
/// wake signal claude reacts to. `unread` is the count of *other*
|
||||
|
|
|
|||
|
|
@ -61,7 +61,6 @@ async fn main() -> Result<()> {
|
|||
let login_state = Arc::new(Mutex::new(initial));
|
||||
let ui_state = login_state.clone();
|
||||
let bus = Bus::new();
|
||||
spawn_events_vacuum(bus.clone());
|
||||
let ui_bus = bus.clone();
|
||||
let ui_socket = cli.socket.clone();
|
||||
tokio::spawn(async move {
|
||||
|
|
@ -90,22 +89,6 @@ async fn main() -> Result<()> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Vacuum events older than 7 days, cap to 2000 most-recent rows.
|
||||
fn spawn_events_vacuum(bus: Bus) {
|
||||
tokio::spawn(async move {
|
||||
let interval_secs = 3600u64;
|
||||
let keep_secs: i64 = 7 * 24 * 3600;
|
||||
let keep_rows = 2000;
|
||||
loop {
|
||||
let n = bus.vacuum(keep_secs, keep_rows);
|
||||
if n > 0 {
|
||||
tracing::info!(removed = n, "events vacuum");
|
||||
}
|
||||
tokio::time::sleep(Duration::from_secs(interval_secs)).await;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async fn serve(socket: &Path, interval: Duration, bus: Bus) -> Result<()> {
|
||||
tracing::info!(socket = %socket.display(), "hive-m1nd serve");
|
||||
let mcp_config = turn::write_mcp_config(socket).await?;
|
||||
|
|
|
|||
|
|
@ -115,28 +115,6 @@ impl EventStore {
|
|||
Ok(out)
|
||||
}
|
||||
|
||||
/// Drop rows older than `older_than_secs` AND any rows beyond
|
||||
/// `keep_rows` newest. Two-stage so a quiet agent keeps a useful
|
||||
/// tail and a chatty one is bounded.
|
||||
fn vacuum(&self, older_than_secs: i64, keep_rows: usize) -> rusqlite::Result<u64> {
|
||||
let now = std::time::SystemTime::now()
|
||||
.duration_since(std::time::UNIX_EPOCH)
|
||||
.ok()
|
||||
.and_then(|d| i64::try_from(d.as_secs()).ok())
|
||||
.unwrap_or(0);
|
||||
let cutoff = now - older_than_secs;
|
||||
let conn = self.conn.lock().unwrap();
|
||||
let by_age = conn.execute("DELETE FROM events WHERE ts < ?1", params![cutoff])?;
|
||||
let keep_i = i64::try_from(keep_rows).unwrap_or(i64::MAX);
|
||||
let by_count = conn.execute(
|
||||
"DELETE FROM events
|
||||
WHERE id NOT IN (
|
||||
SELECT id FROM events ORDER BY id DESC LIMIT ?1
|
||||
)",
|
||||
params![keep_i],
|
||||
)?;
|
||||
Ok(u64::try_from(by_age + by_count).unwrap_or(0))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
|
|
@ -195,12 +173,6 @@ impl Bus {
|
|||
store.recent(HISTORY_CAPACITY).unwrap_or_default()
|
||||
}
|
||||
|
||||
/// Drop events older than `older_than_secs` and keep only the
|
||||
/// newest `keep_rows`. Called periodically by the harness.
|
||||
pub fn vacuum(&self, older_than_secs: i64, keep_rows: usize) -> u64 {
|
||||
let Some(store) = &self.store else { return 0 };
|
||||
store.vacuum(older_than_secs, keep_rows).unwrap_or(0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Bus {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue