use crate::storage::StorageEngine;
use std::sync::Arc;
use std::time::Duration;
pub struct TtlWorker {
storage: Arc<StorageEngine>,
interval_secs: u64,
}
impl TtlWorker {
pub fn new(storage: Arc<StorageEngine>) -> Self {
Self {
storage,
interval_secs: 60, }
}
pub async fn start(self: Arc<Self>) {
tracing::info!("Starting TTL Worker (interval: {}s)", self.interval_secs);
loop {
tokio::time::sleep(Duration::from_secs(self.interval_secs)).await;
self.cleanup_expired_documents().await;
}
}
async fn cleanup_expired_documents(&self) {
let databases = self.storage.list_databases();
let mut total_deleted = 0;
for db_name in databases {
let db = match self.storage.get_database(&db_name) {
Ok(db) => db,
Err(_) => continue,
};
let collections = db.list_collections();
for coll_name in collections {
let collection = match db.system_collection(&coll_name) {
Ok(coll) => coll,
Err(_) => continue,
};
let ttl_indexes = collection.list_ttl_indexes();
if ttl_indexes.is_empty() {
continue;
}
let coll = collection.clone();
match tokio::task::spawn_blocking(move || coll.cleanup_all_expired_documents())
.await
{
Ok(Ok(count)) => {
if count > 0 {
tracing::info!(
"TTL cleanup: deleted {} expired documents from {}.{}",
count,
db_name,
coll_name
);
total_deleted += count;
}
}
Ok(Err(e)) => {
tracing::warn!("TTL cleanup failed for {}.{}: {}", db_name, coll_name, e);
}
Err(e) => {
tracing::error!("TTL cleanup task panicked: {}", e);
}
}
}
}
if total_deleted > 0 {
tracing::debug!(
"TTL cleanup cycle complete: {} total documents deleted",
total_deleted
);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use tempfile::TempDir;
#[test]
fn test_ttl_worker_new() {
let tmp = TempDir::new().unwrap();
let storage = Arc::new(StorageEngine::new(tmp.path().to_str().unwrap()).unwrap());
let worker = TtlWorker::new(storage);
assert_eq!(worker.interval_secs, 60);
}
#[tokio::test]
async fn test_ttl_cleanup_empty_database() {
let tmp = TempDir::new().unwrap();
let storage = Arc::new(StorageEngine::new(tmp.path().to_str().unwrap()).unwrap());
let worker = TtlWorker::new(storage);
worker.cleanup_expired_documents().await;
}
#[tokio::test]
async fn test_ttl_cleanup_no_ttl_indexes() {
let tmp = TempDir::new().unwrap();
let storage = Arc::new(StorageEngine::new(tmp.path().to_str().unwrap()).unwrap());
storage.create_database("test_db".to_string()).unwrap();
let db = storage.get_database("test_db").unwrap();
db.create_collection("test_coll".to_string(), None).unwrap();
let worker = TtlWorker::new(storage);
worker.cleanup_expired_documents().await;
}
}