Skip to main content

nodedb_lite/storage/
corruption.rs

1// SPDX-License-Identifier: Apache-2.0
2
3//! What to do when opening a store that cannot be read.
4//!
5//! An embedded database is frequently the only copy of its data. Whether a
6//! damaged store can be thrown away and rebuilt depends on something only the
7//! embedder knows — whether an Origin exists to re-sync from, whether the data
8//! is reproducible, whether starting empty is worse than not starting. So the
9//! library reports the fault and stops; discarding is a decision the caller
10//! makes, in advance, by name.
11//!
12//! Note that "does this store sync?" is not something the library can infer at
13//! open time. `LiteConfig::sync_enabled` governs whether writes record
14//! themselves for the push to Origin, not whether an Origin has ever been
15//! reachable, and `start_sync` is called after open in any case. Treating either as consent to discard would
16//! reintroduce exactly the silent decision this type exists to prevent.
17
18use serde::{Deserialize, Serialize};
19
20/// How [`open`](crate::storage::pagedb_storage::PagedbStorage) reacts to a
21/// corruption-class fault in the store it was asked to open.
22#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
23#[serde(rename_all = "snake_case")]
24pub enum CorruptionPolicy {
25    /// Report the corruption and leave every byte where it is. The default.
26    ///
27    /// Open returns a corruption-class error the caller can match on
28    /// (`ErrorDetails::SegmentCorrupted`). Nothing is renamed, deleted, or
29    /// created, so the store is in the same state after the failed open as
30    /// before it — available for `pagedb-fsck`, for a byte-for-byte backup, or
31    /// for a later open under a different policy.
32    #[default]
33    FailClosed,
34
35    /// Rename the damaged store aside and continue against a fresh, empty one.
36    ///
37    /// The old store is moved to `{path}.corrupt.{unix_secs}` and never
38    /// deleted, but a caller opting into this is choosing to start empty: the
39    /// database that comes back has none of the previous data in it, and any
40    /// writes it accepts diverge from the copy set aside. Only meaningful when
41    /// the caller has another source of truth to refill from.
42    DiscardStoreAndRecreate,
43}
44
45impl CorruptionPolicy {
46    /// Whether this policy permits destroying or replacing stored bytes.
47    pub(crate) fn may_discard(self) -> bool {
48        matches!(self, CorruptionPolicy::DiscardStoreAndRecreate)
49    }
50}