block_db/
recover.rs

1// Authors: Robert Lopez
2
3use crate::{error::Error, wal::BlockDBState, BlockDB};
4use std::{collections::HashMap, sync::Arc};
5use tokio::sync::RwLock;
6
7impl BlockDB {
8    /// Recovers the state of the `BlockDB` by replaying its WAL,
9    /// as well as the WALs for all `DataFile`s. Invalid `DataFile`s
10    /// and `DataBlock`s are deleted during the process.
11    ///
12    /// This method complements `BlockDB::uncorrupt`, and is intended
13    /// for cases where the file structure has become corrupted through
14    /// external means — not via `BlockDB` methods themselves (e.g.,
15    /// hardware issues or filesystem corruption).
16    ///
17    /// As noted in `uncorrupt`, you should ensure filesystem and hardware
18    /// stability before invoking this method, as such corruption usually
19    /// indicates a deeper issue with the environment.
20    ///
21    /// If this method fails repeatedly, it likely means the WAL(s)
22    /// themselves are irreparably corrupted — in which case manual deletion
23    /// of the `BlockDB` may be required.
24    ///
25    /// ---
26    /// - **Atomic**
27    /// - **Non-corruptible**
28    ///
29    /// ---
30    /// Example
31    /// ``` let mut block_db = BlockDB::open("./data", None).await?;
32    ///
33    /// block_db.recover().await?;
34    /// ```
35    pub async fn recover(&mut self) -> Result<(), Error> {
36        let BlockDBState { valid_data_files } = self.wal.write().await.replay(None).await?;
37
38        let mut new_data_files = HashMap::new();
39        for (id, data_file) in self.data_files.read().await.clone() {
40            if valid_data_files.contains(&id) {
41                data_file.write().await.recover().await?;
42
43                new_data_files.insert(id, data_file);
44            } else {
45                let _ = data_file.write().await.delete().await;
46            }
47        }
48
49        self.data_files = Arc::new(RwLock::new(new_data_files));
50
51        Ok(())
52    }
53}