orbok_workers/
cleanup_service.rs1use orbok_cache::CacheService;
10use orbok_core::{CleanupAction, CleanupPlan, OrbokResult};
11use orbok_db::Catalog;
12use orbok_db::repo::CleanupExecutor;
13use std::path::Path;
14use tracing::info;
15
16#[derive(Debug, Default)]
18pub struct FullCleanupOutcome {
19 pub catalog_rows_deleted: u64,
20 pub cache_bytes_freed: u64,
22}
23
24pub struct CleanupService<'a> {
26 catalog: &'a Catalog,
27 cache: &'a CacheService,
28 cache_db_path: &'a Path,
29}
30
31impl<'a> CleanupService<'a> {
32 pub fn new(catalog: &'a Catalog, cache: &'a CacheService, cache_db_path: &'a Path) -> Self {
33 Self {
34 catalog,
35 cache,
36 cache_db_path,
37 }
38 }
39
40 pub fn run_safe(&self, plan: &CleanupPlan) -> OrbokResult<FullCleanupOutcome> {
44 let catalog_outcome = CleanupExecutor::new(self.catalog).run_safe(plan)?;
46 info!(
47 action = ?plan.action,
48 rows = catalog_outcome.deleted_rows,
49 "catalog cleanup completed"
50 );
51
52 let cache_bytes_freed = self.run_cache_side(plan)?;
54 if cache_bytes_freed > 0 {
55 info!(bytes = cache_bytes_freed, "cache cleanup freed space");
56 }
57
58 Ok(FullCleanupOutcome {
59 catalog_rows_deleted: catalog_outcome.deleted_rows,
60 cache_bytes_freed,
61 })
62 }
63
64 pub fn run_reset(
66 &self,
67 plan: &CleanupPlan,
68 keep_settings: bool,
69 ) -> OrbokResult<FullCleanupOutcome> {
70 let catalog_outcome =
72 CleanupExecutor::new(self.catalog).run_reset_catalog(plan, keep_settings)?;
73
74 let cache_bytes_freed = self.purge_all_cache_namespaces()?;
76
77 info!(
78 rows = catalog_outcome.deleted_rows,
79 cache_freed = cache_bytes_freed,
80 "catalog reset completed"
81 );
82
83 Ok(FullCleanupOutcome {
84 catalog_rows_deleted: catalog_outcome.deleted_rows,
85 cache_bytes_freed,
86 })
87 }
88
89 fn run_cache_side(&self, plan: &CleanupPlan) -> OrbokResult<u64> {
90 use orbok_cache::{EngineOptions, OrbokCacheNamespace};
91
92 let size_before = self.cache_db_path.metadata().map(|m| m.len()).unwrap_or(0);
93
94 match plan.action {
95 CleanupAction::ClearSnippetCache | CleanupAction::ClearExpiredSearchCache => {
96 let engine = self.cache.engine::<Vec<u8>>(
98 self.catalog,
99 &OrbokCacheNamespace::PreviewCache,
100 EngineOptions::default(),
101 )?;
102 engine
103 .cleanup_expired()
104 .map_err(|e| orbok_core::OrbokError::Cache(e.to_string()))?;
105 engine
106 .shrink_database()
107 .map_err(|e| orbok_core::OrbokError::Cache(e.to_string()))?;
108 }
109 CleanupAction::ClearTemporaryExtraction
110 | CleanupAction::RemoveTemporarySourceIndexes => {
111 let engine = self.cache.engine::<Vec<u8>>(
113 self.catalog,
114 &OrbokCacheNamespace::ExtractSegments,
115 EngineOptions::default(),
116 )?;
117 engine
118 .purge_stale_versions()
119 .map_err(|e| orbok_core::OrbokError::Cache(e.to_string()))?;
120 engine
121 .cleanup_missing_files()
122 .map_err(|e| orbok_core::OrbokError::Cache(e.to_string()))?;
123 }
124 CleanupAction::RemoveReplacedStaleIndexes => {
125 for ns in [
127 OrbokCacheNamespace::ChunkBundle,
128 OrbokCacheNamespace::ExtractSegments,
129 ] {
130 let engine = self.cache.engine::<Vec<u8>>(
131 self.catalog,
132 &ns,
133 EngineOptions::default(),
134 )?;
135 engine
136 .cleanup_missing_files()
137 .map_err(|e| orbok_core::OrbokError::Cache(e.to_string()))?;
138 }
139 }
140 _ => {}
141 }
142
143 let size_after = self.cache_db_path.metadata().map(|m| m.len()).unwrap_or(0);
144 Ok(size_before.saturating_sub(size_after))
145 }
146
147 fn purge_all_cache_namespaces(&self) -> OrbokResult<u64> {
148 use orbok_cache::{EngineOptions, OrbokCacheNamespace};
149 let size_before = self.cache_db_path.metadata().map(|m| m.len()).unwrap_or(0);
150 for ns in [
151 OrbokCacheNamespace::ExtractSegments,
152 OrbokCacheNamespace::ChunkBundle,
153 OrbokCacheNamespace::PreviewCache,
154 ] {
155 let engine =
156 self.cache
157 .engine::<Vec<u8>>(self.catalog, &ns, EngineOptions::default())?;
158 let _ = engine.purge_stale_versions();
159 let _ = engine.cleanup_expired();
160 let _ = engine.shrink_database();
161 }
162 let size_after = self.cache_db_path.metadata().map(|m| m.len()).unwrap_or(0);
163 Ok(size_before.saturating_sub(size_after))
164 }
165}