Skip to main content

lsm_tree/
error.rs

1// Copyright (c) 2024-present, fjall-rs
2// This source code is licensed under both the Apache 2.0 and MIT License
3// (found in the LICENSE-* files in the repository)
4
5use crate::{Checksum, CompressionType};
6
7/// Represents errors that can occur in the LSM-tree
8#[derive(Debug)]
9#[non_exhaustive]
10pub enum Error {
11    /// I/O error
12    Io(std::io::Error),
13
14    /// Decompression failed
15    Decompress(CompressionType),
16
17    /// Invalid or unparsable data format version
18    InvalidVersion(u8),
19
20    /// Some required files could not be recovered from disk
21    Unrecoverable,
22
23    /// Checksum mismatch
24    ChecksumMismatch {
25        /// Checksum of loaded block
26        got: Checksum,
27
28        /// Checksum that was saved in block header
29        expected: Checksum,
30    },
31
32    /// Blob frame header CRC mismatch (V4 format).
33    /// Distinct from `ChecksumMismatch` which covers data payload checksums.
34    HeaderCrcMismatch {
35        /// CRC recomputed from header fields
36        recomputed: u32,
37
38        /// CRC stored in the blob frame header
39        stored: u32,
40    },
41
42    /// Invalid enum tag
43    InvalidTag((&'static str, u8)),
44
45    /// Invalid block trailer
46    InvalidTrailer,
47
48    /// Invalid block header
49    InvalidHeader(&'static str),
50
51    /// Data size (decompressed, on-disk, or requested) is invalid or exceeds a safety limit
52    DecompressedSizeTooLarge {
53        /// Size associated with the data being processed. This may come from
54        /// on-disk/in-memory metadata (e.g., header, block/value handle) or be
55        /// derived from caller input (e.g., a requested key or value length),
56        /// and may be zero, invalid, or over the configured limit.
57        declared: u64,
58
59        /// Maximum allowed size for the data or request being processed
60        limit: u64,
61    },
62
63    /// UTF-8 error
64    Utf8(std::str::Utf8Error),
65
66    /// Merge operator failed.
67    ///
68    /// No context payload — consistent with other unit variants
69    /// (`Unrecoverable`, `InvalidTrailer`). Operators should log
70    /// details before returning this error.
71    MergeOperator,
72
73    /// Encryption failed
74    Encrypt(&'static str),
75
76    /// Decryption failed
77    Decrypt(&'static str),
78
79    /// Comparator mismatch on tree reopen.
80    ///
81    /// The tree was created with a comparator whose [`crate::UserComparator::name`]
82    /// differs from the one supplied at reopen time.
83    ComparatorMismatch {
84        /// Comparator name persisted in the tree metadata.
85        stored: String,
86
87        /// Comparator name supplied by the caller.
88        supplied: &'static str,
89    },
90
91    /// Zstd dictionary required but not provided, or `dict_id` mismatch
92    ZstdDictMismatch {
93        /// Dictionary ID stored in the block/table metadata
94        expected: u32,
95
96        /// Dictionary ID provided by the caller (`None` if no dictionary supplied)
97        got: Option<u32>,
98    },
99
100    /// Range tombstone block decode failure.
101    RangeTombstoneDecode {
102        /// Which field or validation failed (e.g. `start_len`, `start`, `seqno`, `interval`)
103        field: &'static str,
104
105        /// Byte offset within the block to the start of the field whose decoding failed
106        /// (captured before reading bytes for that field).
107        offset: u64,
108    },
109}
110
111impl std::fmt::Display for Error {
112    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
113        write!(f, "LsmTreeError: {self:?}")
114    }
115}
116
117impl std::error::Error for Error {
118    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
119        match self {
120            Self::Io(e) => Some(e),
121            _ => None,
122        }
123    }
124}
125
126impl From<sfa::Error> for Error {
127    fn from(value: sfa::Error) -> Self {
128        match value {
129            sfa::Error::Io(e) => Self::from(e),
130            sfa::Error::ChecksumMismatch { got, expected } => {
131                log::error!("Archive ToC checksum mismatch");
132                Self::ChecksumMismatch {
133                    got: got.into(),
134                    expected: expected.into(),
135                }
136            }
137            sfa::Error::InvalidHeader => {
138                log::error!("Invalid archive header");
139                Self::Unrecoverable
140            }
141            sfa::Error::InvalidVersion => {
142                log::error!("Invalid archive version");
143                Self::Unrecoverable
144            }
145            sfa::Error::UnsupportedChecksumType => {
146                log::error!("Invalid archive checksum type");
147                Self::Unrecoverable
148            }
149        }
150    }
151}
152
153impl From<std::io::Error> for Error {
154    fn from(value: std::io::Error) -> Self {
155        Self::Io(value)
156    }
157}
158
159/// Tree result
160pub type Result<T> = std::result::Result<T, Error>;