dbx_core/engine/utilities.rs
1//! Database Utility Methods — helper functions for database operations
2
3use crate::engine::{Database, WosVariant};
4use crate::error::{DbxError, DbxResult};
5use crate::storage::StorageBackend;
6use crate::storage::encryption::EncryptionConfig;
7use crate::storage::encryption::wos::EncryptedWosBackend;
8use std::sync::Arc;
9
10impl Database {
11 /// 데이터베이스가 암호화되어 있는지 확인합니다.
12 pub fn is_encrypted(&self) -> bool {
13 self.encryption.read().unwrap().is_some()
14 }
15
16 /// 암호화 키를 교체합니다 (키 로테이션).
17 ///
18 /// 모든 저장 계층 (WOS, WAL)의 데이터를 현재 키로 복호화한 뒤
19 /// 새 키로 재암호화합니다. Delta Store의 데이터는 먼저 WOS로
20 /// flush된 후 재암호화됩니다.
21 ///
22 /// # 전제 조건
23 ///
24 /// - 데이터베이스가 암호화되어 있어야 합니다 (`is_encrypted() == true`).
25 /// - 키 교체 중 다른 쓰기가 발생하지 않아야 합니다.
26 ///
27 /// # 반환값
28 ///
29 /// 재암호화된 레코드 수 (WOS + WAL).
30 ///
31 /// # 예제
32 ///
33 /// ```rust,no_run
34 /// use dbx_core::Database;
35 /// use dbx_core::storage::encryption::EncryptionConfig;
36 /// use std::path::Path;
37 ///
38 /// let enc = EncryptionConfig::from_password("old-password");
39 /// let db = Database::open_encrypted(Path::new("./data"), enc).unwrap();
40 ///
41 /// let new_enc = EncryptionConfig::from_password("new-password");
42 /// let count = db.rotate_key(new_enc).unwrap();
43 /// println!("Re-encrypted {} records", count);
44 /// ```
45 pub fn rotate_key(&self, new_encryption: EncryptionConfig) -> DbxResult<usize> {
46 if !self.is_encrypted() {
47 return Err(DbxError::Encryption(
48 "cannot rotate key on unencrypted database".into(),
49 ));
50 }
51
52 // Step 1: Flush Delta → WOS (ensure all data is in encrypted WOS)
53 self.flush()?;
54
55 let mut total = 0;
56
57 // Step 2: Re-key WOS
58 // We need mutable access to the EncryptedWosBackend.
59 // Since WosVariant uses Arc, we use Arc::get_mut via try_unwrap workaround.
60 // For now, use the rekey method through interior mutability pattern.
61 match &self.wos {
62 WosVariant::Encrypted(enc_wos) => {
63 // Safety: We hold exclusive logical access during key rotation.
64 // Use unsafe to get mutable reference — caller guarantees no concurrent writes.
65 let wos_ptr = Arc::as_ptr(enc_wos) as *mut EncryptedWosBackend;
66 // SAFETY: rotate_key documentation states no concurrent writes allowed
67 let wos_mut = unsafe { &mut *wos_ptr };
68 total += wos_mut.rekey(new_encryption.clone())?;
69 }
70 WosVariant::Plain(_) | WosVariant::InMemory(_) => {
71 return Err(DbxError::Encryption(
72 "WOS is not encrypted — cannot rotate key".into(),
73 ));
74 }
75 }
76
77 // Step 3: Re-key encrypted WAL (if present)
78 if let Some(enc_wal) = &self.encrypted_wal {
79 let wal_ptr = Arc::as_ptr(enc_wal) as *mut crate::wal::encrypted_wal::EncryptedWal;
80 // SAFETY: rotate_key documentation states no concurrent writes allowed
81 let wal_mut = unsafe { &mut *wal_ptr };
82 total += wal_mut.rekey(new_encryption.clone())?;
83 }
84
85 // Step 4: Update local encryption configuration
86 let mut enc_lock = self.encryption.write().unwrap();
87 *enc_lock = Some(new_encryption);
88
89 Ok(total)
90 }
91
92 /// GPU Manager에 대한 참조를 반환합니다 (있는 경우).
93 pub fn gpu_manager(&self) -> Option<&crate::storage::gpu::GpuManager> {
94 self.gpu_manager.as_deref()
95 }
96
97 /// Delta Store의 모든 데이터를 WOS로 flush합니다.
98 pub fn flush(&self) -> DbxResult<()> {
99 match &self.delta {
100 crate::engine::DeltaVariant::RowBased(_) => {
101 let drained = self.delta.drain_all();
102 for (table, entries) in drained {
103 let rows: Vec<_> = entries.into_iter().collect();
104 self.wos.insert_batch(&table, rows)?;
105 }
106 self.wos.flush()
107 }
108 crate::engine::DeltaVariant::Columnar(_) => {
109 // Get all table names
110 let table_names = self.delta.table_names()?;
111 for table in table_names {
112 crate::engine::compaction::Compactor::bypass_flush(self, &table)?;
113 }
114 Ok(())
115 }
116 }
117 }
118
119 /// Get the total entry count (Delta + WOS) for a table.
120 pub fn count(&self, table: &str) -> DbxResult<usize> {
121 let delta_count = self.delta.count(table)?;
122 let wos_count = self.wos.count(table)?;
123 Ok(delta_count + wos_count)
124 }
125
126 /// Get all table names across all tiers.
127 pub fn table_names(&self) -> DbxResult<Vec<String>> {
128 let mut names: Vec<String> = self.delta.table_names()?;
129 for name in self.wos.table_names()? {
130 if !names.contains(&name) {
131 names.push(name);
132 }
133 }
134 names.sort();
135 Ok(names)
136 }
137
138 /// Get the Delta Store entry count (diagnostic).
139 pub fn delta_entry_count(&self) -> usize {
140 self.delta.entry_count()
141 }
142
143 // ════════════════════════════════════════════
144 // MVCC Garbage Collection
145 // ════════════════════════════════════════════
146
147 /// Run garbage collection to clean up old MVCC versions.
148 ///
149 /// This removes versions that are no longer visible to any active transaction.
150 /// Returns the number of versions deleted.
151 ///
152 /// # Example
153 ///
154 /// ```rust
155 /// # use dbx_core::Database;
156 /// # fn main() -> dbx_core::DbxResult<()> {
157 /// let db = Database::open_in_memory()?;
158 ///
159 /// // Run GC
160 /// let deleted = db.gc()?;
161 /// println!("Deleted {} old versions", deleted);
162 /// # Ok(())
163 /// # }
164 /// ```
165 pub fn gc(&self) -> DbxResult<usize> {
166 use crate::transaction::mvcc::gc::GarbageCollector;
167
168 let gc = GarbageCollector::new();
169
170 // Use min_active_ts as the watermark, or current_ts if no active transactions
171 let watermark = self
172 .tx_manager
173 .min_active_ts()
174 .unwrap_or_else(|| self.tx_manager.current_ts());
175
176 gc.collect(self, watermark)
177 }
178
179 /// Estimate the number of versions that would be deleted by GC.
180 pub fn gc_estimate(&self) -> DbxResult<usize> {
181 use crate::transaction::mvcc::gc::GarbageCollector;
182
183 let gc = GarbageCollector::new();
184 let watermark = self
185 .tx_manager
186 .min_active_ts()
187 .unwrap_or_else(|| self.tx_manager.current_ts());
188
189 gc.estimate_garbage(self, watermark)
190 }
191
192 /// Get the number of active transactions.
193 pub fn active_transaction_count(&self) -> usize {
194 self.tx_manager.active_count()
195 }
196}