cuprate_database/backend/heed/
error.rs

1//! Conversion from `heed::Error` -> `cuprate_database`'s errors.
2
3//---------------------------------------------------------------------------------------------------- Use
4use crate::constants::DATABASE_CORRUPT_MSG;
5
6//---------------------------------------------------------------------------------------------------- InitError
7impl From<heed::Error> for crate::InitError {
8    fn from(error: heed::Error) -> Self {
9        use heed::Error as E1;
10        use heed::MdbError as E2;
11
12        // Reference of all possible errors `heed` will return
13        // upon using [`heed::EnvOpenOptions::open`]:
14        // <https://docs.rs/heed/latest/src/heed/env.rs.html#149-219>
15        match error {
16            E1::Io(io_error) => Self::Io(io_error),
17            E1::DatabaseClosing => Self::ShuttingDown,
18
19            // LMDB errors.
20            E1::Mdb(mdb_error) => match mdb_error {
21                E2::Invalid => Self::Invalid,
22                E2::VersionMismatch => Self::InvalidVersion,
23
24                // "Located page was wrong type".
25                // <https://docs.rs/heed/latest/heed/enum.MdbError.html#variant.Corrupted>
26                //
27                // "Requested page not found - this usually indicates corruption."
28                // <https://docs.rs/heed/latest/heed/enum.MdbError.html#variant.PageNotFound>
29                E2::BadDbi | E2::Corrupted | E2::PageNotFound => Self::Corrupt,
30
31                // These errors shouldn't be returned on database init.
32                E2::Incompatible
33                | E2::Other(_)
34                | E2::BadTxn
35                | E2::Problem
36                | E2::KeyExist
37                | E2::NotFound
38                | E2::MapFull
39                | E2::ReadersFull
40                | E2::PageFull
41                | E2::DbsFull
42                | E2::TlsFull
43                | E2::TxnFull
44                | E2::CursorFull
45                | E2::MapResized
46                | E2::BadRslot
47                | E2::BadValSize
48                | E2::Panic => Self::Unknown(Box::new(mdb_error)),
49            },
50
51            E1::BadOpenOptions { .. } | E1::Encoding(_) | E1::Decoding(_) => {
52                Self::Unknown(Box::new(error))
53            }
54        }
55    }
56}
57
58//---------------------------------------------------------------------------------------------------- RuntimeError
59#[expect(
60    clippy::fallible_impl_from,
61    reason = "We need to panic sometimes for safety"
62)]
63impl From<heed::Error> for crate::RuntimeError {
64    /// # Panics
65    /// This will panic on unrecoverable errors for safety.
66    fn from(error: heed::Error) -> Self {
67        use heed::Error as E1;
68        use heed::MdbError as E2;
69
70        match error {
71            // I/O errors.
72            E1::Io(io_error) => Self::Io(io_error),
73
74            // LMDB errors.
75            E1::Mdb(mdb_error) => match mdb_error {
76                E2::KeyExist => Self::KeyExists,
77                E2::NotFound => Self::KeyNotFound,
78                E2::MapFull => Self::ResizeNeeded,
79
80                // Corruption errors, these have special panic messages.
81                //
82                // "Located page was wrong type".
83                // <https://docs.rs/heed/latest/heed/enum.MdbError.html#variant.Corrupted>
84                //
85                // "Requested page not found - this usually indicates corruption."
86                // <https://docs.rs/heed/latest/heed/enum.MdbError.html#variant.PageNotFound>
87                E2::BadDbi | E2::Corrupted | E2::PageNotFound => panic!("{mdb_error:#?}\n{DATABASE_CORRUPT_MSG}"),
88
89                // These errors should not occur, and if they do,
90                // the best thing `cuprate_database` can do for
91                // safety is to panic right here.
92                E2::Panic
93                | E2::PageFull
94                | E2::Other(_)
95                | E2::BadTxn
96                | E2::Problem
97                | E2::Invalid
98                | E2::TlsFull
99                | E2::TxnFull
100                | E2::BadRslot
101                | E2::VersionMismatch => panic!("{mdb_error:#?}"),
102
103                // These errors are the same as above, but instead
104                // of being errors we can't control, these are errors
105                // that only happen if we write incorrect code.
106
107                // "Database contents grew beyond environment mapsize."
108                // We should be resizing the map when needed, this error
109                // occurring indicates we did _not_ do that, which is a bug
110                // and we should panic.
111                //
112                // FIXME: This can also mean _another_ process wrote to our
113                // LMDB file and increased the size. I don't think we need to accommodate for this.
114                // <http://www.lmdb.tech/doc/group__mdb.html#gaa2506ec8dab3d969b0e609cd82e619e5>
115                // Although `monerod` reacts to that instead of `MDB_MAP_FULL`
116                // which is what `mdb_put()` returns so... idk?
117                // <https://github.com/monero-project/monero/blob/059028a30a8ae9752338a7897329fe8012a310d5/src/blockchain_db/lmdb/db_lmdb.cpp#L526>
118                | E2::MapResized
119                // We should be setting `heed::EnvOpenOptions::max_readers()`
120                // with our reader thread value in [`crate::config::Config`],
121                // thus this error should never occur.
122                // <http://www.lmdb.tech/doc/group__mdb.html#gae687966c24b790630be2a41573fe40e2>
123                | E2::ReadersFull
124                // Do not open more database tables than we initially started with.
125                // We know this number at compile time (amount of `Table`'s) so this
126                // should never happen.
127                // <https://docs.rs/heed/0.20.0-alpha.9/heed/struct.EnvOpenOptions.html#method.max_dbs>
128                // <https://docs.rs/heed/0.20.0-alpha.9/src/heed/env.rs.html#251>
129                | E2::DbsFull
130                // Don't do crazy multi-nested LMDB cursor stuff.
131                | E2::CursorFull
132                // <https://docs.rs/heed/0.20.0-alpha.9/heed/enum.MdbError.html#variant.Incompatible>
133                | E2::Incompatible
134                // Unsupported size of key/DB name/data, or wrong DUP_FIXED size.
135                // Don't use a key that is `>511` bytes.
136                // <http://www.lmdb.tech/doc/group__mdb.html#gaaf0be004f33828bf2fb09d77eb3cef94>
137                | E2::BadValSize
138                    => panic!("E2: fix the database code! {mdb_error:#?}"),
139            },
140
141            // Only if we write incorrect code.
142            E1::DatabaseClosing | E1::BadOpenOptions { .. } | E1::Encoding(_) | E1::Decoding(_) => {
143                panic!("E1: fix the database code! {error:#?}")
144            }
145        }
146    }
147}
148
149//---------------------------------------------------------------------------------------------------- Tests
150#[cfg(test)]
151mod test {
152    // use super::*;
153}