Skip to main content

max / makenotwork

4.7 KB · 147 lines History Blame Raw
1 //! Retention: deletes records older than the configured window from every table
2 //! and reports per-table counts.
3
4 use super::{Result, SqlitePool};
5 use tracing::instrument;
6
7 /// Prune result with counts for each table.
8 pub struct PruneResult {
9 pub health: u64,
10 pub tests: u64,
11 pub test_details: u64,
12 pub heartbeats: u64,
13 pub alerts: u64,
14 pub tls: u64,
15 pub incidents: u64,
16 pub routes: u64,
17 pub dns: u64,
18 pub whois: u64,
19 pub backups: u64,
20 pub systemd: u64,
21 pub synckit_fleet: u64,
22 }
23
24 /// Delete records older than `days` from all tables.
25 /// Only closed incidents (with a non-NULL `ended_at`) are pruned.
26 #[instrument(skip_all)]
27 pub async fn prune_old_records(pool: &SqlitePool, days: i64) -> Result<PruneResult> {
28 // Guard: days <= 0 would set cutoff to now (or the future), deleting
29 // everything. Treat this as a no-op instead.
30 if days <= 0 {
31 return Ok(PruneResult {
32 health: 0,
33 tests: 0,
34 test_details: 0,
35 heartbeats: 0,
36 alerts: 0,
37 tls: 0,
38 incidents: 0,
39 routes: 0,
40 dns: 0,
41 whois: 0,
42 backups: 0,
43 systemd: 0,
44 synckit_fleet: 0,
45 });
46 }
47
48 let cutoff = chrono::Utc::now() - chrono::Duration::days(days);
49 let cutoff_str = cutoff.to_rfc3339();
50
51 let health_result = sqlx::query("DELETE FROM health_checks WHERE checked_at < ?")
52 .bind(&cutoff_str)
53 .execute(pool)
54 .await?;
55
56 // test_details has ON DELETE CASCADE on run_id and the pool runs with
57 // foreign_keys=ON, so deleting the runs below already removes their details.
58 // Count them first: the orphan sweep afterwards can only ever report rows the
59 // cascade missed, which is why PruneResult.test_details always read ~0.
60 let cascaded_details = sqlx::query_as::<_, (i64,)>(
61 "SELECT COUNT(*) FROM test_details
62 WHERE run_id IN (SELECT id FROM test_runs WHERE started_at < ?)",
63 )
64 .bind(&cutoff_str)
65 .fetch_one(pool)
66 .await?;
67
68 let test_result = sqlx::query("DELETE FROM test_runs WHERE started_at < ?")
69 .bind(&cutoff_str)
70 .execute(pool)
71 .await?;
72
73 // Safety net for details orphaned some other way (a database written while
74 // foreign_keys was off). Normally zero.
75 let orphan_result =
76 sqlx::query("DELETE FROM test_details WHERE run_id NOT IN (SELECT id FROM test_runs)")
77 .execute(pool)
78 .await?;
79
80 let peer_hb_result = sqlx::query("DELETE FROM peer_heartbeats WHERE checked_at < ?")
81 .bind(&cutoff_str)
82 .execute(pool)
83 .await?;
84
85 let alerts_result = sqlx::query("DELETE FROM alerts WHERE sent_at < ?")
86 .bind(&cutoff_str)
87 .execute(pool)
88 .await?;
89
90 let tls_result = sqlx::query("DELETE FROM tls_checks WHERE checked_at < ?")
91 .bind(&cutoff_str)
92 .execute(pool)
93 .await?;
94
95 let incidents_result =
96 sqlx::query("DELETE FROM incidents WHERE ended_at IS NOT NULL AND ended_at < ?")
97 .bind(&cutoff_str)
98 .execute(pool)
99 .await?;
100
101 let routes_result = sqlx::query("DELETE FROM route_checks WHERE checked_at < ?")
102 .bind(&cutoff_str)
103 .execute(pool)
104 .await?;
105
106 let dns_result = sqlx::query("DELETE FROM dns_checks WHERE checked_at < ?")
107 .bind(&cutoff_str)
108 .execute(pool)
109 .await?;
110
111 let whois_result = sqlx::query("DELETE FROM whois_checks WHERE checked_at < ?")
112 .bind(&cutoff_str)
113 .execute(pool)
114 .await?;
115
116 let backups_result = sqlx::query("DELETE FROM backup_checks WHERE checked_at < ?")
117 .bind(&cutoff_str)
118 .execute(pool)
119 .await?;
120
121 let systemd_result = sqlx::query("DELETE FROM systemd_checks WHERE checked_at < ?")
122 .bind(&cutoff_str)
123 .execute(pool)
124 .await?;
125
126 let synckit_fleet_result = sqlx::query("DELETE FROM synckit_fleet_checks WHERE checked_at < ?")
127 .bind(&cutoff_str)
128 .execute(pool)
129 .await?;
130
131 Ok(PruneResult {
132 health: health_result.rows_affected(),
133 tests: test_result.rows_affected(),
134 test_details: cascaded_details.0 as u64 + orphan_result.rows_affected(),
135 heartbeats: peer_hb_result.rows_affected(),
136 alerts: alerts_result.rows_affected(),
137 tls: tls_result.rows_affected(),
138 incidents: incidents_result.rows_affected(),
139 routes: routes_result.rows_affected(),
140 dns: dns_result.rows_affected(),
141 whois: whois_result.rows_affected(),
142 backups: backups_result.rows_affected(),
143 systemd: systemd_result.rows_affected(),
144 synckit_fleet: synckit_fleet_result.rows_affected(),
145 })
146 }
147