Skip to main content

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}