kaizen/store/sqlite/
maintenance.rs1use super::*;
2
3pub(super) fn old_session_ids(
4 tx: &rusqlite::Transaction<'_>,
5 cutoff_ms: i64,
6) -> Result<Vec<String>> {
7 let mut stmt = tx.prepare("SELECT id FROM sessions WHERE started_at_ms < ?1")?;
8 let rows = stmt.query_map(params![cutoff_ms], |r| r.get::<_, String>(0))?;
9 Ok(rows.filter_map(|r| r.ok()).collect())
10}
11
12impl Store {
13 pub fn prune_sessions_started_before(&self, cutoff_ms: i64) -> Result<PruneStats> {
15 let tx = rusqlite::Transaction::new_unchecked(&self.conn, TransactionBehavior::Deferred)?;
16 let old_ids = old_session_ids(&tx, cutoff_ms)?;
17 let sessions_to_remove: i64 = tx.query_row(
18 "SELECT COUNT(*) FROM sessions WHERE started_at_ms < ?1",
19 params![cutoff_ms],
20 |r| r.get(0),
21 )?;
22 let events_to_remove: i64 = tx.query_row(
23 "SELECT COUNT(*) FROM events WHERE session_id IN \
24 (SELECT id FROM sessions WHERE started_at_ms < ?1)",
25 params![cutoff_ms],
26 |r| r.get(0),
27 )?;
28
29 let sub_old_sessions = "SELECT id FROM sessions WHERE started_at_ms < ?1";
30 tx.execute(
31 &format!(
32 "DELETE FROM tool_span_paths WHERE span_id IN \
33 (SELECT span_id FROM tool_spans WHERE session_id IN ({sub_old_sessions}))"
34 ),
35 params![cutoff_ms],
36 )?;
37 tx.execute(
38 &format!("DELETE FROM tool_spans WHERE session_id IN ({sub_old_sessions})"),
39 params![cutoff_ms],
40 )?;
41 tx.execute(
42 &format!("DELETE FROM events WHERE session_id IN ({sub_old_sessions})"),
43 params![cutoff_ms],
44 )?;
45 tx.execute(
46 &format!("DELETE FROM files_touched WHERE session_id IN ({sub_old_sessions})"),
47 params![cutoff_ms],
48 )?;
49 tx.execute(
50 &format!("DELETE FROM skills_used WHERE session_id IN ({sub_old_sessions})"),
51 params![cutoff_ms],
52 )?;
53 tx.execute(
54 &format!("DELETE FROM rules_used WHERE session_id IN ({sub_old_sessions})"),
55 params![cutoff_ms],
56 )?;
57 tx.execute(
58 &format!("DELETE FROM sync_outbox WHERE session_id IN ({sub_old_sessions})"),
59 params![cutoff_ms],
60 )?;
61 tx.execute(
62 &format!("DELETE FROM session_repo_binding WHERE session_id IN ({sub_old_sessions})"),
63 params![cutoff_ms],
64 )?;
65 tx.execute(
66 &format!("DELETE FROM experiment_tags WHERE session_id IN ({sub_old_sessions})"),
67 params![cutoff_ms],
68 )?;
69 tx.execute(
70 &format!("DELETE FROM session_outcomes WHERE session_id IN ({sub_old_sessions})"),
71 params![cutoff_ms],
72 )?;
73 tx.execute(
74 &format!("DELETE FROM session_samples WHERE session_id IN ({sub_old_sessions})"),
75 params![cutoff_ms],
76 )?;
77 tx.execute(
78 "DELETE FROM sessions WHERE started_at_ms < ?1",
79 params![cutoff_ms],
80 )?;
81 tx.commit()?;
82 if let Some(mut writer) = self.search_writer.borrow_mut().take() {
83 let _ = writer.commit();
84 }
85 if let Err(err) = crate::search::delete_sessions(&self.root, &old_ids) {
86 tracing::warn!("search prune skipped: {err:#}");
87 let _ = self.sync_state_set_u64(SYNC_STATE_SEARCH_DIRTY_MS, now_ms());
88 }
89 self.invalidate_span_tree_cache();
90 Ok(PruneStats {
91 sessions_removed: sessions_to_remove as u64,
92 events_removed: events_to_remove as u64,
93 })
94 }
95
96 pub fn vacuum(&self) -> Result<()> {
98 self.conn.execute_batch("VACUUM;").context("VACUUM")?;
99 Ok(())
100 }
101}