database_overlay.rs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522
  1. /* This file is part of DarkFi (https://dark.fi)
  2. *
  3. * Copyright (C) 2026-2026 Dyne.org foundation
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU Affero General Public License as
  7. * published by the Free Software Foundation, either version 3 of the
  8. * License, or (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU Affero General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU Affero General Public License
  16. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  17. */
  18. use std::collections::BTreeMap;
  19. #[cfg(feature = "sled-backend")]
  20. use sled::{Transactional, transaction::ConflictableTransactionError};
  21. use crate::{
  22. Batch, Database, DatabaseOverlayState, DatabaseOverlayStateDiff, Error, Result, Tree,
  23. TreeOverlay, TreeOverlayIter, TreeOverlayStateDiff,
  24. };
  25. /// An overlay on top of an entire [`Database`] which can span multiple trees
  26. #[derive(Clone)]
  27. pub struct DatabaseOverlay {
  28. /// The [`Database`] that is being overlayed.
  29. db: Database,
  30. /// Current overlay cache state
  31. pub state: DatabaseOverlayState,
  32. /// Checkpointed cache state to revert to
  33. checkpoint: DatabaseOverlayState,
  34. }
  35. impl DatabaseOverlay {
  36. /// Instantiate a new [`DatabaseOverlay`] on top of a given
  37. /// [`Database`].
  38. /// Note: Provided protected trees don't have to be opened as
  39. /// protected, as they are setup as protected here.
  40. pub fn new(db: &Database, protected_tree_names: Vec<String>) -> Result<Self> {
  41. let initial_tree_names = db.tree_names()?;
  42. Ok(Self {
  43. db: db.clone(),
  44. state: DatabaseOverlayState::new(
  45. initial_tree_names.clone(),
  46. protected_tree_names.clone(),
  47. ),
  48. checkpoint: DatabaseOverlayState::new(initial_tree_names, protected_tree_names),
  49. })
  50. }
  51. /// Create a new [`TreeOverlay`] on top of a given `tree_name`.
  52. /// This function will also open a new tree inside `database`
  53. /// regardless of if it has existed before, so for convenience, we
  54. /// also provide [`DatabaseOverlay::purge_new_trees`] in case we
  55. /// decide we don't want to write the batches, and drop the new
  56. /// trees. Additionally, a boolean flag is passed to mark the
  57. /// oppened tree as protected, meanning that it can't be removed
  58. /// and its references will never be dropped.
  59. pub fn open_tree(
  60. &mut self,
  61. name: &str,
  62. #[cfg(feature = "fjall-backend")] create_options: impl FnOnce() -> fjall::KeyspaceCreateOptions,
  63. protected: bool,
  64. ) -> Result<()> {
  65. // Check if we have already opened this tree
  66. if self.state.caches.contains_key(name) {
  67. return Ok(());
  68. }
  69. // Open this tree in the database
  70. let tree = self.db.open_tree(
  71. name,
  72. #[cfg(feature = "fjall-backend")]
  73. create_options,
  74. )?;
  75. let mut cache = TreeOverlay::new(&tree);
  76. // If we are reopenning a dropped tree, grab its cache
  77. if let Some(diff) = self.state.dropped_trees.remove(name) {
  78. cache.state = (&diff).into();
  79. }
  80. // In case it hasn't existed before, we also need to track it
  81. // in `self.new_tree_names`.
  82. let name = name.to_string();
  83. if !self.state.initial_tree_names.contains(&name) {
  84. self.state.new_tree_names.push(name.clone());
  85. }
  86. self.state.caches.insert(name.clone(), cache);
  87. // Mark tree as protected if requested
  88. if protected && !self.state.protected_tree_names.contains(&name) {
  89. self.state.protected_tree_names.push(name);
  90. }
  91. Ok(())
  92. }
  93. /// Create a new [`TreeOverlay`] on top of a given `tree_name`.
  94. /// This function will also open a new tree inside `database`,
  95. /// using the default backend configuration, regardless of if it
  96. /// has existed before, so for convenience, we also provide
  97. /// [`DatabaseOverlay::purge_new_trees`] in case we decide we don't
  98. /// want to write the batches, and drop the new trees.
  99. /// Additionally, a boolean flag is passed to mark the oppened tree
  100. /// as protected, meanning that it can't be removed and its
  101. /// references will never be dropped.
  102. pub fn open_tree_default(&mut self, name: &str, protected: bool) -> Result<()> {
  103. #[cfg(feature = "sled-backend")]
  104. {
  105. self.open_tree(name, protected)
  106. }
  107. #[cfg(feature = "fjall-backend")]
  108. {
  109. self.open_tree(name, fjall::KeyspaceCreateOptions::default, protected)
  110. }
  111. }
  112. /// Drop a tree from the overlay.
  113. pub fn drop_tree(&mut self, name: &str) -> Result<()> {
  114. // Check if tree is protected
  115. let name = name.to_string();
  116. if self.state.protected_tree_names.contains(&name) {
  117. return Err(Error::ProtectedTreeDrop(name));
  118. }
  119. // Check if already removed
  120. if self.state.dropped_trees.contains_key(&name) {
  121. return Err(Error::CollectionNotFound(name));
  122. }
  123. // Check if its a new tree we created
  124. if self.state.new_tree_names.contains(&name) {
  125. self.state.new_tree_names.retain(|x| x != &name);
  126. let tree = match self.get_cache(&name) {
  127. Ok(cache) => &cache.tree,
  128. _ => &self.db.open_tree_default(&name)?,
  129. };
  130. let diff = TreeOverlayStateDiff::new_dropped(tree);
  131. self.state.caches.remove(&name);
  132. self.state.dropped_trees.insert(name, diff);
  133. return Ok(());
  134. }
  135. // Check if tree existed in the database
  136. if !self.state.initial_tree_names.contains(&name) {
  137. return Err(Error::CollectionNotFound(name));
  138. }
  139. let tree = match self.get_cache(&name) {
  140. Ok(cache) => &cache.tree,
  141. _ => &self.db.open_tree_default(&name)?,
  142. };
  143. let diff = TreeOverlayStateDiff::new_dropped(tree);
  144. self.state.caches.remove(&name);
  145. self.state.dropped_trees.insert(name, diff);
  146. Ok(())
  147. }
  148. /// Drop newly created trees from the database. This is a
  149. /// convenience function that should be used when we decide that we
  150. /// don't want to apply any cache changes, and we want to revert
  151. /// back to the initial state.
  152. pub fn purge_new_trees(&self) -> Result<()> {
  153. for i in &self.state.new_tree_names {
  154. self.db.drop_tree(i)?;
  155. }
  156. Ok(())
  157. }
  158. /// Fetch the cache for a given tree.
  159. fn get_cache(&self, name: &str) -> Result<&TreeOverlay> {
  160. let name = name.to_string();
  161. if self.state.dropped_trees.contains_key(&name) {
  162. return Err(Error::CollectionNotFound(name));
  163. }
  164. if let Some(v) = self.state.caches.get(&name) {
  165. return Ok(v);
  166. }
  167. Err(Error::CollectionNotFound(name))
  168. }
  169. /// Fetch a mutable reference to the cache for a given tree.
  170. fn get_cache_mut(&mut self, name: &str) -> Result<&mut TreeOverlay> {
  171. let name = name.to_string();
  172. if self.state.dropped_trees.contains_key(&name) {
  173. return Err(Error::CollectionNotFound(name));
  174. }
  175. if let Some(v) = self.state.caches.get_mut(&name) {
  176. return Ok(v);
  177. }
  178. Err(Error::CollectionNotFound(name))
  179. }
  180. /// Fetch all our caches current [`Tree`] pointers.
  181. pub fn get_state_trees(&self) -> BTreeMap<String, Tree> {
  182. // Grab our state tree pointers
  183. let mut state_trees = BTreeMap::new();
  184. for (name, cache) in self.state.caches.iter() {
  185. state_trees.insert(name.clone(), cache.tree.clone());
  186. }
  187. state_trees
  188. }
  189. /// Returns `true` if the overlay contains a value for a specified
  190. /// key in the specified tree cache.
  191. pub fn contains_key(&self, name: &str, key: &[u8]) -> Result<bool> {
  192. let cache = self.get_cache(name)?;
  193. cache.contains_key(key)
  194. }
  195. /// Retrieve a value from the overlay if it exists in the specified
  196. /// tree cache.
  197. pub fn get(&self, name: &str, key: &[u8]) -> Result<Option<Vec<u8>>> {
  198. let cache = self.get_cache(name)?;
  199. cache.get(key)
  200. }
  201. /// Returns `true` if specified tree cache is empty.
  202. pub fn is_empty(&self, name: &str) -> Result<bool> {
  203. let cache = self.get_cache(name)?;
  204. cache.is_empty()
  205. }
  206. /// Returns last value from the overlay if the specified tree cache
  207. /// is not empty.
  208. pub fn last(&self, name: &str) -> Result<Option<(Vec<u8>, Vec<u8>)>> {
  209. let cache = self.get_cache(name)?;
  210. cache.last()
  211. }
  212. /// Insert a key to a new value in the specified tree cache,
  213. /// returning the last value if it was set.
  214. pub fn insert(&mut self, name: &str, key: &[u8], value: &[u8]) -> Result<Option<Vec<u8>>> {
  215. let cache = self.get_cache_mut(name)?;
  216. cache.insert(key, value)
  217. }
  218. /// Delete a value in the specified tree cache, returning the old
  219. /// value if it existed.
  220. pub fn remove(&mut self, name: &str, key: &[u8]) -> Result<Option<Vec<u8>>> {
  221. let cache = self.get_cache_mut(name)?;
  222. cache.remove(key)
  223. }
  224. /// Removes all values from the specified tree cache and marks all
  225. /// its tree records as removed.
  226. pub fn clear(&mut self, name: &str) -> Result<()> {
  227. let cache = self.get_cache_mut(name)?;
  228. cache.clear()
  229. }
  230. /// Aggregate all the current overlay changes into [`Batch`]
  231. /// instances and return a vector of `Tree` and their respective
  232. /// `Batch` that can be used for further operations. If there are
  233. /// no changes, vector will be empty.
  234. fn aggregate(&self) -> Result<Vec<(Tree, Batch)>> {
  235. self.state.aggregate()
  236. }
  237. /// Ensure all new trees that have been opened exist in the
  238. /// database by reopening them with the default backend
  239. /// configuration, atomically apply all batches on all trees as a
  240. /// transaction, and drop dropped trees from the database. This
  241. /// function **does not** perform a db flush. This should be done
  242. /// externally, since then there is a choice to perform either
  243. /// blocking or async IO. After execution is successful, caller
  244. /// should *NOT* use the overlay again.
  245. pub fn apply(&mut self) -> Result<()> {
  246. // Ensure new trees exist
  247. let new_tree_names = self.state.new_tree_names.clone();
  248. for tree_names in &new_tree_names {
  249. let tree = self.db.open_tree_default(tree_names)?;
  250. // Update cache tree pointer, it must exist
  251. let cache = self.get_cache_mut(tree_names)?;
  252. cache.tree = tree;
  253. }
  254. // Drop removed trees
  255. for tree in self.state.dropped_trees.keys() {
  256. self.db.drop_tree(tree)?;
  257. }
  258. // Aggregate batches
  259. let batches = self.aggregate()?;
  260. if batches.is_empty() {
  261. return Ok(());
  262. }
  263. #[cfg(feature = "sled-backend")]
  264. {
  265. // Grab all referenced trees
  266. let trees: Vec<&sled::Tree> = batches.iter().map(|(t, _)| t.tree()).collect();
  267. // Perform an atomic transaction over all the collected trees and
  268. // apply the batches.
  269. if let Err(e) = trees.transaction(|trees| {
  270. for (i, tree) in trees.iter().enumerate() {
  271. // Build and apply its batch
  272. let mut sled_batch = sled::Batch::default();
  273. for (key, value) in &batches[i].1.writes {
  274. match value {
  275. Some(v) => sled_batch.insert(key.as_slice(), v.as_slice()),
  276. None => sled_batch.remove(key.as_slice()),
  277. }
  278. }
  279. tree.apply_batch(&sled_batch)?;
  280. }
  281. Ok::<(), ConflictableTransactionError<sled::Error>>(())
  282. }) {
  283. return Err(Error::Transaction(e.to_string()));
  284. };
  285. }
  286. #[cfg(feature = "fjall-backend")]
  287. {
  288. // Grab a batch over the whole database
  289. let mut fjall_batch = self.db.fjall_batch();
  290. // Aggregate the overlay changes into the batch
  291. for (tree, batch) in batches {
  292. for (key, value) in batch.writes {
  293. match value {
  294. Some(v) => fjall_batch.insert(tree.tree(), key, v),
  295. None => fjall_batch.remove(tree.tree(), key),
  296. }
  297. }
  298. }
  299. // Apply the batch
  300. fjall_batch.commit()?;
  301. }
  302. Ok(())
  303. }
  304. /// Checkpoint current cache state so we can revert to it, if
  305. /// needed.
  306. pub fn checkpoint(&mut self) {
  307. self.checkpoint = self.state.clone();
  308. }
  309. /// Revert to current cache state checkpoint. This function will
  310. /// not drop new trees from the `db`, so caller should handle it.
  311. pub fn revert_to_checkpoint(&mut self) {
  312. self.state = self.checkpoint.clone();
  313. }
  314. /// Calculate differences from provided overlay state changes
  315. /// sequence. This can be used when we want to keep track of
  316. /// consecutive individual changes performed over the current
  317. /// overlay state. If the sequence is empty, current state
  318. /// is returned as the diff.
  319. pub fn diff(&self, sequence: &[DatabaseOverlayStateDiff]) -> Result<DatabaseOverlayStateDiff> {
  320. // Grab current state
  321. let mut current = DatabaseOverlayStateDiff::new(&self.state)?;
  322. // Remove provided diffs sequence
  323. for diff in sequence {
  324. current.remove_diff(diff);
  325. }
  326. Ok(current)
  327. }
  328. /// Add provided `db` overlay state changes from our own.
  329. pub fn add_diff(&mut self, diff: &DatabaseOverlayStateDiff) -> Result<()> {
  330. self.state.add_diff(&self.db, diff)
  331. }
  332. /// Remove provided `db` overlay state changes from our own.
  333. pub fn remove_diff(&mut self, diff: &DatabaseOverlayStateDiff) {
  334. self.state.remove_diff(diff)
  335. }
  336. /// For a provided `DatabaseOverlayStateDiff`, ensure all trees
  337. /// exist in the database by reopening them with the default
  338. /// backend configuration, atomically apply all batches on all
  339. /// trees as a transaction, and drop dropped trees from the
  340. /// database. After that, remove the state changes from our own.
  341. /// This is will also mutate the initial trees, based on what was
  342. /// oppened and/or dropped. This function **does not** perform a db
  343. /// flush. This should be done externally, since then there is a
  344. /// choice to perform either blocking or async IO.
  345. pub fn apply_diff(&mut self, diff: &DatabaseOverlayStateDiff) -> Result<()> {
  346. // We assert that the diff doesn't try to drop any of our
  347. // protected trees.
  348. for name in diff.dropped_trees.keys() {
  349. if self.state.protected_tree_names.contains(name) {
  350. return Err(Error::ProtectedTreeDrop(name.clone()));
  351. }
  352. }
  353. for (name, (_, drop)) in diff.caches.iter() {
  354. if *drop && self.state.protected_tree_names.contains(name) {
  355. return Err(Error::ProtectedTreeDrop(name.clone()));
  356. }
  357. }
  358. // Grab current state trees
  359. let mut state_trees = self.get_state_trees();
  360. // Ensure diff trees exist
  361. for (name, (_, drop)) in diff.caches.iter() {
  362. // Check if its an unknown tree
  363. if !self.state.initial_tree_names.contains(name)
  364. && !self.state.new_tree_names.contains(name)
  365. {
  366. self.state.new_tree_names.push(name.clone());
  367. }
  368. // Check if it should be dropped
  369. if *drop {
  370. self.db.drop_tree(name)?;
  371. continue;
  372. }
  373. if !state_trees.contains_key(name) {
  374. let tree = self.db.open_tree_default(name)?;
  375. state_trees.insert(name.clone(), tree);
  376. }
  377. }
  378. // Drop removed trees and ensure restored trees exist
  379. for (name, (_, restored)) in diff.dropped_trees.iter() {
  380. if !restored {
  381. state_trees.remove(name);
  382. self.db.drop_tree(name)?;
  383. continue;
  384. }
  385. // Check if its an unknown tree
  386. if !self.state.initial_tree_names.contains(name)
  387. && !self.state.new_tree_names.contains(name)
  388. {
  389. self.state.new_tree_names.push(name.clone());
  390. }
  391. if !state_trees.contains_key(name) {
  392. let tree = self.db.open_tree_default(name)?;
  393. state_trees.insert(name.clone(), tree);
  394. }
  395. }
  396. // Aggregate batches
  397. let batches = diff.aggregate(&state_trees)?;
  398. if batches.is_empty() {
  399. self.remove_diff(diff);
  400. return Ok(());
  401. }
  402. #[cfg(feature = "sled-backend")]
  403. {
  404. // Grab all referenced trees
  405. let trees: Vec<&sled::Tree> = batches.iter().map(|(t, _)| t.tree()).collect();
  406. // Perform an atomic transaction over all the collected trees and
  407. // apply the batches.
  408. if let Err(e) = trees.transaction(|trees| {
  409. for (i, tree) in trees.iter().enumerate() {
  410. // Build and apply its batch
  411. let mut sled_batch = sled::Batch::default();
  412. for (key, value) in &batches[i].1.writes {
  413. match value {
  414. Some(v) => sled_batch.insert(key.as_slice(), v.as_slice()),
  415. None => sled_batch.remove(key.as_slice()),
  416. }
  417. }
  418. tree.apply_batch(&sled_batch)?;
  419. }
  420. Ok::<(), ConflictableTransactionError<sled::Error>>(())
  421. }) {
  422. return Err(Error::Transaction(e.to_string()));
  423. };
  424. }
  425. #[cfg(feature = "fjall-backend")]
  426. {
  427. // Grab a batch over the whole database
  428. let mut fjall_batch = self.db.fjall_batch();
  429. // Aggregate the overlay changes into the batch
  430. for (tree, batch) in batches {
  431. for (key, value) in batch.writes {
  432. match value {
  433. Some(v) => fjall_batch.insert(tree.tree(), key, v),
  434. None => fjall_batch.remove(tree.tree(), key),
  435. }
  436. }
  437. }
  438. // Apply the batch
  439. fjall_batch.commit()?;
  440. }
  441. // Remove changes from our current state
  442. self.remove_diff(diff);
  443. Ok(())
  444. }
  445. /// Retrieve an immutable itterator from the overlay if the
  446. /// specified tree cache exists.
  447. pub fn iter(&self, name: &str) -> Result<TreeOverlayIter<'_>> {
  448. let cache = self.get_cache(name)?;
  449. Ok(cache.iter())
  450. }
  451. }