Skip to main content

aster/session/
cleanup.rs

1//! Session Cleanup Support
2//!
3//! Provides functionality for cleaning up expired sessions and summaries.
4
5use anyhow::Result;
6use chrono::{Duration, Utc};
7use serde::Serialize;
8use tracing::{info, warn};
9
10/// Default cleanup period in days
11pub const DEFAULT_CLEANUP_PERIOD_DAYS: u32 = 30;
12
13/// Cleanup statistics
14#[derive(Debug, Clone, Default, Serialize)]
15pub struct CleanupStats {
16    /// Number of sessions cleaned
17    pub sessions: usize,
18    /// Number of summaries cleaned
19    pub summaries: usize,
20    /// Number of errors encountered
21    pub errors: usize,
22    /// Number of directories processed
23    pub directories: usize,
24}
25
26impl CleanupStats {
27    /// Check if any cleanup was performed
28    pub fn has_changes(&self) -> bool {
29        self.sessions > 0 || self.summaries > 0
30    }
31}
32
33/// Get the cutoff date for cleanup
34///
35/// # Arguments
36/// * `period_days` - Number of days to keep data
37pub fn get_cutoff_date(period_days: u32) -> chrono::DateTime<Utc> {
38    Utc::now() - Duration::days(period_days as i64)
39}
40
41/// Clean up expired summaries
42///
43/// # Arguments
44/// * `period_days` - Number of days to keep summaries
45///
46/// # Returns
47/// Number of summaries deleted
48pub fn cleanup_summaries(period_days: u32) -> Result<usize> {
49    crate::session::resume::cleanup_old_summaries(period_days)
50}
51
52/// Clean up expired data (summaries only for now)
53///
54/// Note: Session cleanup is handled by the database,
55/// this function cleans up file-based caches.
56///
57/// # Arguments
58/// * `period_days` - Number of days to keep data
59pub fn cleanup_expired_data(period_days: u32) -> CleanupStats {
60    let mut stats = CleanupStats::default();
61
62    // Clean up summaries
63    match cleanup_summaries(period_days) {
64        Ok(count) => {
65            stats.summaries = count;
66            if count > 0 {
67                stats.directories += 1;
68            }
69        }
70        Err(e) => {
71            warn!("Failed to cleanup summaries: {}", e);
72            stats.errors += 1;
73        }
74    }
75
76    stats
77}
78
79/// Schedule cleanup to run asynchronously
80///
81/// This function spawns a background task to clean up
82/// expired data without blocking the main thread.
83///
84/// # Arguments
85/// * `period_days` - Number of days to keep data
86pub fn schedule_cleanup(period_days: u32) {
87    tokio::spawn(async move {
88        // Small delay to avoid impacting startup
89        tokio::time::sleep(tokio::time::Duration::from_secs(5)).await;
90
91        let stats = cleanup_expired_data(period_days);
92
93        if stats.has_changes() {
94            info!("Cleanup complete: {} summaries removed", stats.summaries);
95        }
96
97        if stats.errors > 0 {
98            warn!("Cleanup encountered {} errors", stats.errors);
99        }
100    });
101}
102
103/// Force cleanup synchronously
104///
105/// # Arguments
106/// * `period_days` - Number of days to keep data
107///
108/// # Returns
109/// Cleanup statistics
110pub fn force_cleanup(period_days: u32) -> CleanupStats {
111    let stats = cleanup_expired_data(period_days);
112
113    info!(
114        "Force cleanup complete: {} summaries removed",
115        stats.summaries
116    );
117
118    if stats.errors > 0 {
119        warn!("Cleanup encountered {} errors", stats.errors);
120    }
121
122    stats
123}
124
125#[cfg(test)]
126mod tests {
127    use super::*;
128
129    #[test]
130    fn test_get_cutoff_date() {
131        let cutoff = get_cutoff_date(30);
132        let now = Utc::now();
133
134        // Cutoff should be approximately 30 days ago
135        let diff = now - cutoff;
136        assert!(diff.num_days() >= 29 && diff.num_days() <= 31);
137    }
138
139    #[test]
140    fn test_cleanup_stats_has_changes() {
141        let empty = CleanupStats::default();
142        assert!(!empty.has_changes());
143
144        let with_sessions = CleanupStats {
145            sessions: 1,
146            ..Default::default()
147        };
148        assert!(with_sessions.has_changes());
149
150        let with_summaries = CleanupStats {
151            summaries: 1,
152            ..Default::default()
153        };
154        assert!(with_summaries.has_changes());
155    }
156}