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 { catalog, cache, cache_db_path }
34 }
35
36 pub fn run_safe(&self, plan: &CleanupPlan) -> OrbokResult<FullCleanupOutcome> {
40 let catalog_outcome = CleanupExecutor::new(self.catalog).run_safe(plan)?;
42 info!(
43 action = ?plan.action,
44 rows = catalog_outcome.deleted_rows,
45 "catalog cleanup completed"
46 );
47
48 let cache_bytes_freed = self.run_cache_side(plan)?;
50 if cache_bytes_freed > 0 {
51 info!(bytes = cache_bytes_freed, "cache cleanup freed space");
52 }
53
54 Ok(FullCleanupOutcome {
55 catalog_rows_deleted: catalog_outcome.deleted_rows,
56 cache_bytes_freed,
57 })
58 }
59
60 pub fn run_reset(
62 &self,
63 plan: &CleanupPlan,
64 keep_settings: bool,
65 ) -> OrbokResult<FullCleanupOutcome> {
66 let catalog_outcome =
68 CleanupExecutor::new(self.catalog).run_reset_catalog(plan, keep_settings)?;
69
70 let cache_bytes_freed = self.purge_all_cache_namespaces()?;
72
73 info!(
74 rows = catalog_outcome.deleted_rows,
75 cache_freed = cache_bytes_freed,
76 "catalog reset completed"
77 );
78
79 Ok(FullCleanupOutcome {
80 catalog_rows_deleted: catalog_outcome.deleted_rows,
81 cache_bytes_freed,
82 })
83 }
84
85 fn run_cache_side(&self, plan: &CleanupPlan) -> OrbokResult<u64> {
86 use orbok_cache::{EngineOptions, OrbokCacheNamespace};
87
88 let size_before = self
89 .cache_db_path
90 .metadata()
91 .map(|m| m.len())
92 .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.cleanup_expired().map_err(|e| {
103 orbok_core::OrbokError::Cache(e.to_string())
104 })?;
105 engine.shrink_database().map_err(|e| {
106 orbok_core::OrbokError::Cache(e.to_string())
107 })?;
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.purge_stale_versions().map_err(|e| {
118 orbok_core::OrbokError::Cache(e.to_string())
119 })?;
120 engine.cleanup_missing_files().map_err(|e| {
121 orbok_core::OrbokError::Cache(e.to_string())
122 })?;
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.cleanup_missing_files().map_err(|e| {
136 orbok_core::OrbokError::Cache(e.to_string())
137 })?;
138 }
139 }
140 _ => {}
141 }
142
143 let size_after = self
144 .cache_db_path
145 .metadata()
146 .map(|m| m.len())
147 .unwrap_or(0);
148 Ok(size_before.saturating_sub(size_after))
149 }
150
151 fn purge_all_cache_namespaces(&self) -> OrbokResult<u64> {
152 use orbok_cache::{EngineOptions, OrbokCacheNamespace};
153 let size_before = self.cache_db_path.metadata().map(|m| m.len()).unwrap_or(0);
154 for ns in [
155 OrbokCacheNamespace::ExtractSegments,
156 OrbokCacheNamespace::ChunkBundle,
157 OrbokCacheNamespace::PreviewCache,
158 ] {
159 let engine = self.cache.engine::<Vec<u8>>(
160 self.catalog,
161 &ns,
162 EngineOptions::default(),
163 )?;
164 let _ = engine.purge_stale_versions();
165 let _ = engine.cleanup_expired();
166 let _ = engine.shrink_database();
167 }
168 let size_after = self.cache_db_path.metadata().map(|m| m.len()).unwrap_or(0);
169 Ok(size_before.saturating_sub(size_after))
170 }
171}