database_overlay_state.rs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434
  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, BTreeSet};
  19. use crate::{Batch, Database, Error, Result, Tree, TreeOverlay, TreeOverlayStateDiff};
  20. /// Struct representing [`DatabaseOverlay`] cache state.
  21. #[derive(Debug, Clone)]
  22. pub struct DatabaseOverlayState {
  23. /// Existing trees in `Database` at the time of instantiation, so
  24. /// we can track newly opened trees.
  25. pub initial_tree_names: Vec<String>,
  26. /// New trees that have been opened, but didn't exist in `Database`
  27. /// before.
  28. pub new_tree_names: Vec<String>,
  29. /// Pointers to [`TreeOverlay`] instances that have been created.
  30. pub caches: BTreeMap<String, TreeOverlay>,
  31. /// Trees that were dropped, along with their last state full diff.
  32. pub dropped_trees: BTreeMap<String, TreeOverlayStateDiff>,
  33. /// Protected trees, that we don't allow their removal, and don't
  34. /// drop their references if they become stale.
  35. pub protected_tree_names: Vec<String>,
  36. }
  37. impl DatabaseOverlayState {
  38. /// Instantiate a new [`DatabaseOverlayState`].
  39. pub fn new(initial_tree_names: Vec<String>, protected_tree_names: Vec<String>) -> Self {
  40. Self {
  41. initial_tree_names,
  42. new_tree_names: vec![],
  43. caches: BTreeMap::new(),
  44. dropped_trees: BTreeMap::new(),
  45. protected_tree_names,
  46. }
  47. }
  48. /// Aggregate all the current overlay changes into [`Batch`]
  49. /// instances and return vectors of [`Tree`] and their respective
  50. /// [`Batch`] that can be used for further operations. If there are
  51. /// no changes, both vectors will be empty.
  52. pub fn aggregate(&self) -> Result<Vec<(Tree, Batch)>> {
  53. let mut batches = vec![];
  54. for (key, cache) in self.caches.iter() {
  55. if self.dropped_trees.contains_key(key) {
  56. return Err(Error::CollectionNotFound(key.clone()));
  57. }
  58. if let Some(batch) = cache.aggregate() {
  59. batches.push((cache.tree.clone(), batch));
  60. }
  61. }
  62. Ok(batches)
  63. }
  64. /// Add provided `Database` overlay state changes to our own.
  65. /// If a `Tree` doesn't exists it is opened using the default
  66. /// backend configuration
  67. pub fn add_diff(&mut self, database: &Database, diff: &DatabaseOverlayStateDiff) -> Result<()> {
  68. self.initial_tree_names
  69. .retain(|x| diff.initial_tree_names.contains(x));
  70. for (k, (cache, drop)) in diff.caches.iter() {
  71. if *drop {
  72. assert!(!self.protected_tree_names.contains(k));
  73. self.new_tree_names.retain(|x| x != k);
  74. self.caches.remove(k);
  75. self.dropped_trees.insert(k.clone(), cache.clone());
  76. continue;
  77. }
  78. let Some(tree_overlay) = self.caches.get_mut(k) else {
  79. if !self.initial_tree_names.contains(k) && !self.new_tree_names.contains(k) {
  80. self.new_tree_names.push(k.clone());
  81. }
  82. let mut overlay = TreeOverlay::new(&database.open_tree_default(k)?);
  83. overlay.add_diff(cache);
  84. self.caches.insert(k.clone(), overlay);
  85. continue;
  86. };
  87. // Add the diff to our tree overlay state
  88. tree_overlay.add_diff(cache);
  89. }
  90. for (k, (cache, restored)) in &diff.dropped_trees {
  91. // Drop the trees that are not restored
  92. if !restored {
  93. if self.dropped_trees.contains_key(k) {
  94. continue;
  95. }
  96. self.new_tree_names.retain(|x| x != k);
  97. self.caches.remove(k);
  98. self.dropped_trees.insert(k.clone(), cache.clone());
  99. continue;
  100. }
  101. assert!(!self.protected_tree_names.contains(k));
  102. // Restore the tree
  103. self.initial_tree_names.retain(|x| x != k);
  104. if !self.new_tree_names.contains(k) {
  105. self.new_tree_names.push(k.clone());
  106. }
  107. let mut overlay = TreeOverlay::new(&database.open_tree_default(k)?);
  108. overlay.add_diff(cache);
  109. self.caches.insert(k.clone(), overlay);
  110. }
  111. Ok(())
  112. }
  113. /// Remove provided `database` overlay state changes from our own.
  114. pub fn remove_diff(&mut self, diff: &DatabaseOverlayStateDiff) {
  115. // We have some assertions here to catch catastrophic
  116. // logic bugs here, as all our fields are depending on each
  117. // other when checking for differences.
  118. for (k, (cache, drop)) in diff.caches.iter() {
  119. // We must know the tree
  120. assert!(
  121. self.initial_tree_names.contains(k)
  122. || self.new_tree_names.contains(k)
  123. || self.dropped_trees.contains_key(k)
  124. );
  125. if !self.initial_tree_names.contains(k) {
  126. self.initial_tree_names.push(k.clone());
  127. }
  128. self.new_tree_names.retain(|x| x != k);
  129. // Check if tree is marked for drop
  130. if *drop {
  131. assert!(!self.protected_tree_names.contains(k));
  132. self.initial_tree_names.retain(|x| x != k);
  133. self.new_tree_names.retain(|x| x != k);
  134. self.caches.remove(k);
  135. self.dropped_trees.remove(k);
  136. continue;
  137. }
  138. // If the key is not in the cache, and it exists
  139. // in the dropped trees, update its diff
  140. let Some(tree_overlay) = self.caches.get_mut(k) else {
  141. let Some(tree_overlay) = self.dropped_trees.get_mut(k) else {
  142. continue;
  143. };
  144. tree_overlay.update_values(cache);
  145. continue;
  146. };
  147. // If the state is unchanged, handle the stale tree
  148. if tree_overlay.state == cache.into() {
  149. // If tree is protected, we simply reset its cache
  150. if self.protected_tree_names.contains(k) {
  151. tree_overlay.state.cache = BTreeMap::new();
  152. tree_overlay.state.removed = BTreeSet::new();
  153. tree_overlay.checkpoint();
  154. continue;
  155. }
  156. // Drop the stale reference
  157. self.caches.remove(k);
  158. continue;
  159. }
  160. // Remove the diff from our tree overlay state
  161. tree_overlay.remove_diff(cache);
  162. }
  163. // Now we handle the dropped trees
  164. for (k, (cache, restored)) in diff.dropped_trees.iter() {
  165. // We must know the tree
  166. assert!(
  167. self.initial_tree_names.contains(k)
  168. || self.new_tree_names.contains(k)
  169. || self.dropped_trees.contains_key(k)
  170. );
  171. // Drop the trees that are not restored
  172. if !restored {
  173. assert!(!self.protected_tree_names.contains(k));
  174. self.initial_tree_names.retain(|x| x != k);
  175. self.new_tree_names.retain(|x| x != k);
  176. self.caches.remove(k);
  177. self.dropped_trees.remove(k);
  178. continue;
  179. }
  180. // Restore the tree
  181. self.initial_tree_names.retain(|x| x != k);
  182. if !self.new_tree_names.contains(k) {
  183. self.new_tree_names.push(k.clone());
  184. }
  185. // Skip if not in cache
  186. let Some(tree_overlay) = self.caches.get_mut(k) else {
  187. continue;
  188. };
  189. // If the state is unchanged, handle the stale tree
  190. if tree_overlay.state == cache.into() {
  191. // If tree is protected, we simply reset its cache
  192. if self.protected_tree_names.contains(k) {
  193. tree_overlay.state.cache = BTreeMap::new();
  194. tree_overlay.state.removed = BTreeSet::new();
  195. tree_overlay.checkpoint();
  196. continue;
  197. }
  198. // Drop the stale reference
  199. self.caches.remove(k);
  200. continue;
  201. }
  202. // Remove the diff from our tree overlay state
  203. tree_overlay.remove_diff(cache);
  204. }
  205. }
  206. }
  207. impl Default for DatabaseOverlayState {
  208. fn default() -> Self {
  209. Self::new(vec![], vec![])
  210. }
  211. }
  212. /// Auxilliary struct representing a [`DatabaseOverlayState`] diff log.
  213. #[derive(Debug, Default, Clone, PartialEq)]
  214. pub struct DatabaseOverlayStateDiff {
  215. /// Existing trees in `database` at the time of instantiation, so
  216. /// we can track newly opened trees.
  217. pub initial_tree_names: Vec<String>,
  218. /// State diff logs of all [`TreeOverlay`] instances that have been
  219. /// created, along with a boolean flag indicating if it should be
  220. /// dropped. The drop flag is always set to false, and change to
  221. /// true when we inverse the diff of a new tree(not in our initial
  222. /// tree names) and the inserts vector is empty, indicating that
  223. /// the tree should be dropped.
  224. pub caches: BTreeMap<String, (TreeOverlayStateDiff, bool)>,
  225. /// Trees that were dropped, along with their last state full diff,
  226. /// along with a boolean flag indicating if they should be
  227. /// restored. The restore flag is always set to false, and change
  228. /// to true when we inverse the diff, unless the tree is a new
  229. /// tree(not in our initial tree names).
  230. pub dropped_trees: BTreeMap<String, (TreeOverlayStateDiff, bool)>,
  231. }
  232. impl DatabaseOverlayStateDiff {
  233. /// Instantiate a new [`DatabaseOverlayStateDiff`], over the
  234. /// provided [`DatabaseOverlayState`].
  235. pub fn new(state: &DatabaseOverlayState) -> Result<Self> {
  236. let mut caches = BTreeMap::new();
  237. let mut dropped_trees = BTreeMap::new();
  238. for (key, cache) in state.caches.iter() {
  239. let diff = cache.diff(&[])?;
  240. // Skip if diff is empty for an existing tree
  241. if diff.cache.is_empty()
  242. && diff.removed.is_empty()
  243. && !state.new_tree_names.contains(key)
  244. {
  245. continue;
  246. }
  247. caches.insert(key.clone(), (diff, false));
  248. }
  249. for (key, cache) in state.dropped_trees.iter() {
  250. dropped_trees.insert(key.clone(), (cache.clone(), false));
  251. }
  252. Ok(Self {
  253. initial_tree_names: state.initial_tree_names.clone(),
  254. caches,
  255. dropped_trees,
  256. })
  257. }
  258. /// Aggregate all the overlay changes into [`Batch`] instances and
  259. /// return a vector of `Tree` and their respective `Batch` that can
  260. /// be used for further operations. If there are no changes, vector
  261. /// will be empty. Provided state trees must contain all the
  262. /// [`Tree`] pointers the diff mutates.
  263. pub fn aggregate(&self, state_trees: &BTreeMap<String, Tree>) -> Result<Vec<(Tree, Batch)>> {
  264. let mut batches = vec![];
  265. for (key, (cache, drop)) in self.caches.iter() {
  266. if *drop {
  267. continue;
  268. }
  269. let Some(tree) = state_trees.get(key) else {
  270. return Err(Error::CollectionNotFound(key.clone()));
  271. };
  272. if let Some(batch) = cache.aggregate() {
  273. batches.push((tree.clone(), batch));
  274. }
  275. }
  276. for (key, (cache, restored)) in self.dropped_trees.iter() {
  277. if !restored {
  278. continue;
  279. }
  280. let Some(tree) = state_trees.get(key) else {
  281. return Err(Error::CollectionNotFound(key.clone()));
  282. };
  283. if let Some(batch) = cache.aggregate() {
  284. batches.push((tree.clone(), batch));
  285. }
  286. }
  287. Ok(batches)
  288. }
  289. /// Produces a [`DatabaseOverlayStateDiff`] containing the inverse
  290. /// changes from our own.
  291. pub fn inverse(&self) -> Self {
  292. let mut diff = Self {
  293. initial_tree_names: self.initial_tree_names.clone(),
  294. ..Default::default()
  295. };
  296. for (key, (cache, drop)) in self.caches.iter() {
  297. let inverse = cache.inverse();
  298. // Flip its drop flag if its a new empty tree, otherwise
  299. // check if its cache is empty and its a new tree.
  300. let drop = if inverse.cache.is_empty()
  301. && inverse.removed.is_empty()
  302. && !self.initial_tree_names.contains(key)
  303. {
  304. !drop
  305. } else {
  306. inverse.cache.is_empty() && !self.initial_tree_names.contains(key)
  307. };
  308. diff.caches.insert(key.clone(), (inverse, drop));
  309. }
  310. for (key, (cache, restored)) in self.dropped_trees.iter() {
  311. if !self.initial_tree_names.contains(key) {
  312. continue;
  313. }
  314. diff.dropped_trees
  315. .insert(key.clone(), (cache.clone(), !restored));
  316. }
  317. diff
  318. }
  319. /// Remove provided `database` overlay state changes from our own.
  320. pub fn remove_diff(&mut self, other: &Self) {
  321. // We have some assertions here to catch catastrophic
  322. // logic bugs here, as all our fields are depending on each
  323. // other when checking for differences.
  324. for initial_tree_name in &other.initial_tree_names {
  325. assert!(self.initial_tree_names.contains(initial_tree_name));
  326. }
  327. // First we remove each cache diff
  328. for (key, cache_pair) in other.caches.iter() {
  329. if !self.initial_tree_names.contains(key) {
  330. self.initial_tree_names.push(key.clone());
  331. }
  332. // If the key is not in the cache, and it exists
  333. // in the dropped trees, update its diff.
  334. let Some(tree_overlay) = self.caches.get_mut(key) else {
  335. let Some((tree_overlay, _)) = self.dropped_trees.get_mut(key) else {
  336. continue;
  337. };
  338. tree_overlay.update_values(&cache_pair.0);
  339. continue;
  340. };
  341. // If the state is unchanged, handle the stale tree
  342. if tree_overlay == cache_pair {
  343. // Drop the stale reference
  344. self.caches.remove(key);
  345. continue;
  346. }
  347. // Remove the diff from our tree overlay state
  348. tree_overlay.0.remove_diff(&cache_pair.0);
  349. }
  350. // Now we handle the dropped trees. We must have all
  351. // the keys in our dropped trees keys.
  352. for (key, (cache, restored)) in other.dropped_trees.iter() {
  353. // Check if the tree was reopened
  354. if let Some(tree_overlay) = self.caches.get_mut(key) {
  355. assert!(!self.dropped_trees.contains_key(key));
  356. // Remove the diff from our tree overlay state
  357. tree_overlay.0.remove_diff(cache);
  358. continue;
  359. }
  360. assert!(self.dropped_trees.contains_key(key));
  361. // Restore tree if its flag is set to true
  362. if *restored {
  363. self.caches.insert(key.clone(), (cache.clone(), false));
  364. }
  365. // Drop the tree
  366. self.initial_tree_names.retain(|x| x != key);
  367. self.dropped_trees.remove(key);
  368. }
  369. }
  370. /// Auxilliary function to retrieve our newly opened trees.
  371. pub fn new_trees(&self) -> Vec<String> {
  372. let mut new_trees: Vec<String> = self.caches.keys().cloned().collect();
  373. new_trees.retain(|tree| !self.initial_tree_names.contains(tree));
  374. new_trees
  375. }
  376. }