yggdrasil/tasks/
log_purge.rs1use std::time::Duration;
11
12use tokio::time::interval;
13
14use crate::db::pool::get_conn;
15use crate::models::log::{LogSettings, DEFAULT_LOGS_MAX_ROWS, DEFAULT_LOGS_RETENTION_DAYS};
16
17const PURGE_INTERVAL: Duration = Duration::from_secs(3600);
19
20pub async fn run_purge() {
22 let mut ticker = interval(PURGE_INTERVAL);
23 loop {
24 match get_conn().await {
25 Ok(client) => {
26 if let Err(e) = purge_once(&client).await {
27 tracing::error!("Log purge error: {:?}", e);
28 }
29 }
30 Err(e) => tracing::error!("Failed to get DB connection for log purge: {:?}", e),
31 }
32 ticker.tick().await;
33 }
34}
35
36async fn purge_once(client: &tokio_postgres::Client) -> Result<(), tokio_postgres::Error> {
38 let retention_days: i32 = client
40 .query_opt(
41 "SELECT value FROM settings WHERE key = 'logs_retention_days'",
42 &[],
43 )
44 .await?
45 .and_then(|r| r.get::<_, String>("value").parse().ok())
46 .unwrap_or(DEFAULT_LOGS_RETENTION_DAYS);
47
48 let max_rows: i32 = client
49 .query_opt(
50 "SELECT value FROM settings WHERE key = 'logs_max_rows'",
51 &[],
52 )
53 .await?
54 .and_then(|r| r.get::<_, String>("value").parse().ok())
55 .unwrap_or(DEFAULT_LOGS_MAX_ROWS);
56
57 let retention_days = LogSettings::clamp_retention(retention_days);
58 let max_rows = LogSettings::clamp_max_rows(max_rows) as i64;
59
60 let aged = client
62 .execute(
63 "DELETE FROM logs WHERE ts < now() - make_interval(days => $1)",
64 &[&retention_days],
65 )
66 .await?;
67
68 let trimmed = client
72 .execute(
73 "DELETE FROM logs WHERE id <= (\
74 SELECT id FROM logs ORDER BY id DESC OFFSET $1 LIMIT 1\
75 )",
76 &[&max_rows],
77 )
78 .await?;
79
80 if aged + trimmed > 0 {
81 tracing::info!(
82 aged,
83 trimmed,
84 retention_days,
85 max_rows,
86 "Log purge: removed expired/excess log rows"
87 );
88 }
89 Ok(())
90}