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>;