Skip to main content

nexir_mvcc/
error.rs

1use thiserror::Error;
2
3use crate::types::{Timestamp, TxnId};
4
5/// Error during a read operation.
6#[derive(Error, Debug, Clone, PartialEq, Eq)]
7pub enum ReadError {
8    /// An error returned by the backend storage.
9    #[error("backend error: {0}")]
10    Backend(String),
11}
12
13/// Error during a single-key prewrite operation.
14#[derive(Error, Debug, Clone, PartialEq, Eq)]
15pub enum PrewriteError {
16    /// The key is locked by another active transaction.
17    #[error("key is locked by another transaction: txn_id={txn_id}")]
18    KeyLocked {
19        /// The transaction ID currently holding the lock.
20        txn_id: TxnId,
21    },
22    /// A committed version exists that is newer than the transaction's start_ts.
23    #[error("write conflict: committed version after start_ts")]
24    WriteConflict,
25    /// An error returned by the backend storage.
26    #[error("backend error: {0}")]
27    Backend(String),
28    /// An intent already exists for the same transaction but with different parameters.
29    #[error("intent already exists with different parameters")]
30    IntentAlreadyExists,
31}
32
33/// Error during a single-key commit operation.
34#[derive(Error, Debug, Clone, PartialEq, Eq)]
35pub enum CommitError {
36    /// The intent to be committed was not found.
37    #[error("intent not found")]
38    IntentNotFound,
39    /// The transaction ID on the intent does not match the commit request.
40    #[error("txn_id mismatch")]
41    TxnIdMismatch,
42    /// The start timestamp on the intent does not match the commit request.
43    #[error("start_ts mismatch")]
44    StartTsMismatch,
45    /// The commit timestamp is less than or equal to the start timestamp.
46    #[error("invalid commit timestamp: commit_ts {commit_ts} <= start_ts {start_ts}")]
47    InvalidCommitTimestamp {
48        /// The start timestamp of the transaction.
49        start_ts: Timestamp,
50        /// The invalid commit timestamp.
51        commit_ts: Timestamp,
52    },
53    /// A committed version already exists exactly at this commit timestamp.
54    #[error("duplicate commit timestamp: {commit_ts}")]
55    DuplicateCommitTimestamp {
56        /// The duplicate commit timestamp.
57        commit_ts: Timestamp,
58    },
59    /// The commit timestamp is older than the latest committed version for a key.
60    #[error("commit timestamp too old: commit_ts {commit_ts}, latest is {latest_commit_ts}")]
61    CommitTsTooOld {
62        /// The requested commit timestamp.
63        commit_ts: Timestamp,
64        /// The latest committed version's timestamp.
65        latest_commit_ts: Timestamp,
66    },
67    /// The commit timestamp is earlier than the intent's required minimum commit timestamp.
68    #[error("commit_ts {commit_ts} is before required minimum {min_commit_ts}")]
69    CommitTsTooEarly {
70        /// The requested commit timestamp.
71        commit_ts: Timestamp,
72        /// The minimum allowed commit timestamp.
73        min_commit_ts: Timestamp,
74    },
75    /// An error returned by the backend storage.
76    #[error("backend error: {0}")]
77    Backend(String),
78}
79
80/// Error during a single-key abort operation.
81#[derive(Error, Debug, Clone, PartialEq, Eq)]
82pub enum AbortError {
83    /// An error returned by the backend storage.
84    #[error("backend error: {0}")]
85    Backend(String),
86}
87
88/// Error during garbage collection.
89#[derive(Error, Debug, Clone, PartialEq, Eq)]
90pub enum GcError {
91    /// An error returned by the backend storage.
92    #[error("backend error: {0}")]
93    Backend(String),
94    /// The provided GC budget is invalid (e.g., max_keys or max_versions is 0).
95    #[error("invalid gc budget: max_keys and max_versions must be > 0")]
96    InvalidGcBudget,
97    /// The provided per-key planning budget is invalid.
98    #[error("invalid per-key gc budget: max_versions_examined must be > 0")]
99    InvalidKeyGcBudget,
100}
101
102/// Error during codec operations.
103#[derive(Error, Debug, Clone, PartialEq, Eq)]
104pub enum CodecError {
105    /// An error occurred during encoding.
106    #[error("encode error: {0}")]
107    Encode(String),
108    /// An error occurred during decoding.
109    #[error("decode error: {0}")]
110    Decode(String),
111}
112
113/// Error during a direct or guarded batch.
114#[derive(Error, Debug, Clone, PartialEq, Eq)]
115pub enum BatchError {
116    /// The batch contained no write operations.
117    #[error("batch is empty")]
118    EmptyBatch,
119    /// The commit timestamp is less than or equal to a guard's read timestamp.
120    #[error("invalid commit timestamp: commit_ts {commit_ts} <= read_ts {read_ts}")]
121    InvalidCommitTimestamp {
122        /// The read timestamp of the guard.
123        read_ts: Timestamp,
124        /// The invalid commit timestamp.
125        commit_ts: Timestamp,
126    },
127    /// The commit timestamp is older than the latest committed version for a key.
128    #[error("commit timestamp too old: key {key:?} at {commit_ts}, latest is {latest_commit_ts}")]
129    CommitTsTooOld {
130        /// The key that caused the error.
131        key: Vec<u8>,
132        /// The requested commit timestamp.
133        commit_ts: Timestamp,
134        /// The latest committed version's timestamp.
135        latest_commit_ts: Timestamp,
136    },
137    /// A guarded batch was submitted without any read guards.
138    #[error("guarded batch requires at least one read guard")]
139    NoReadGuards,
140    /// The batch contains multiple physical writes for the same key.
141    #[error("duplicate key in batch: {key:?}")]
142    DuplicateKeyInBatch {
143        /// The duplicate key.
144        key: Vec<u8>,
145    },
146    /// A key in the batch is currently locked by an active intent.
147    #[error("key is locked by an active intent: key {key:?}, txn_id {txn_id}")]
148    KeyLocked {
149        /// The locked key.
150        key: Vec<u8>,
151        /// The transaction ID holding the lock.
152        txn_id: TxnId,
153    },
154    /// A read guard failed because a newer version exists after the `read_ts`.
155    #[error(
156        "read guard failed: newer version exists after read_ts {read_ts} for key {key:?} (actual_commit_ts: {actual_commit_ts})"
157    )]
158    GuardFailedNewerVersion {
159        /// The key that failed the guard.
160        key: Vec<u8>,
161        /// The read timestamp of the guard.
162        read_ts: Timestamp,
163        /// The timestamp of the newer version.
164        actual_commit_ts: Timestamp,
165    },
166    /// A read guard failed because the actual version did not match the expected version.
167    #[error(
168        "read guard failed: expected commit_ts {expected:?} but found {actual:?} for key {key:?}"
169    )]
170    GuardFailedVersionMismatch {
171        /// The key that failed the guard.
172        key: Vec<u8>,
173        /// The expected commit timestamp (or None if expected absent).
174        expected: Option<Timestamp>,
175        /// The actual commit timestamp found (or None if actually absent).
176        actual: Option<Timestamp>,
177    },
178    /// A read guard failed because the actual logical value did not match the expected value.
179    #[error("read guard failed: expected value mismatch for key {key:?}")]
180    GuardFailedValueMismatch {
181        /// The key that failed the guard.
182        key: Vec<u8>,
183    },
184    /// An error returned by the backend storage.
185    #[error("backend error: {0}")]
186    Backend(String),
187}
188
189/// Error during a prewrite batch.
190#[derive(Error, Debug, Clone, PartialEq, Eq)]
191pub enum BatchPrewriteError {
192    /// The batch contained no write operations.
193    #[error("batch is empty")]
194    EmptyBatch,
195    /// The batch contains multiple writes for the same key.
196    #[error("duplicate key in batch: {key:?}")]
197    DuplicateKeyInBatch {
198        /// The duplicate key.
199        key: Vec<u8>,
200    },
201    /// A key in the batch is currently locked by another active transaction.
202    #[error("key is locked by another transaction: key {key:?}, txn_id {txn_id}")]
203    KeyLocked {
204        /// The locked key.
205        key: Vec<u8>,
206        /// The transaction ID holding the lock.
207        txn_id: TxnId,
208    },
209    /// A committed version exists that is newer than the transaction's start_ts.
210    #[error("write conflict: committed version after start_ts for key {key:?}")]
211    WriteConflict {
212        /// The key that caused the conflict.
213        key: Vec<u8>,
214    },
215    /// An intent already exists for the same transaction but with different parameters.
216    #[error("intent already exists with different parameters for key {key:?}")]
217    IntentAlreadyExists {
218        /// The key with the conflicting intent.
219        key: Vec<u8>,
220    },
221    /// The batch is a partial replay, meaning some intents exist while others do not.
222    #[error("partial batch replay detected: some keys have intents, others are missing")]
223    PartialBatchReplay,
224    /// An error returned by the backend storage.
225    #[error("backend error: {0}")]
226    Backend(String),
227}
228
229/// Error during a commit batch.
230#[derive(Error, Debug, Clone, PartialEq, Eq)]
231pub enum BatchCommitError {
232    /// The batch contained no commit operations.
233    #[error("batch is empty")]
234    EmptyBatch,
235    /// The batch contains multiple commits for the same key.
236    #[error("duplicate key in batch: {key:?}")]
237    DuplicateKeyInBatch {
238        /// The duplicate key.
239        key: Vec<u8>,
240    },
241    /// The intent to be committed was not found.
242    #[error("intent not found for key {key:?}")]
243    IntentNotFound {
244        /// The key whose intent is missing.
245        key: Vec<u8>,
246    },
247    /// The transaction ID on the intent does not match the commit request.
248    #[error("txn_id mismatch for key {key:?}")]
249    TxnIdMismatch {
250        /// The key with the mismatched transaction ID.
251        key: Vec<u8>,
252    },
253    /// The start timestamp on the intent does not match the commit request.
254    #[error("start_ts mismatch for key {key:?}")]
255    StartTsMismatch {
256        /// The key with the mismatched start timestamp.
257        key: Vec<u8>,
258    },
259    /// The commit timestamp is less than or equal to the start timestamp.
260    #[error("invalid commit timestamp: commit_ts {commit_ts} <= start_ts {start_ts}")]
261    InvalidCommitTimestamp {
262        /// The start timestamp of the transaction.
263        start_ts: Timestamp,
264        /// The invalid commit timestamp.
265        commit_ts: Timestamp,
266    },
267    /// The commit timestamp is earlier than the intent's required minimum commit timestamp.
268    #[error("commit_ts {commit_ts} is before required minimum {min_commit_ts} for key {key:?}")]
269    CommitTsTooEarly {
270        /// The key failing the minimum commit timestamp check.
271        key: Vec<u8>,
272        /// The requested commit timestamp.
273        commit_ts: Timestamp,
274        /// The minimum allowed commit timestamp.
275        min_commit_ts: Timestamp,
276    },
277    /// The commit timestamp is older than the latest committed version for a key.
278    #[error("commit timestamp too old: key {key:?} at {commit_ts}, latest is {latest_commit_ts}")]
279    CommitTsTooOld {
280        /// The key that caused the error.
281        key: Vec<u8>,
282        /// The requested commit timestamp.
283        commit_ts: Timestamp,
284        /// The latest committed version's timestamp.
285        latest_commit_ts: Timestamp,
286    },
287    /// An error returned by the backend storage.
288    #[error("backend error: {0}")]
289    Backend(String),
290}
291
292/// Error during a batch abort operation.
293#[derive(Error, Debug, Clone, PartialEq, Eq)]
294pub enum BatchAbortError {
295    /// The batch contains multiple aborts for the same key.
296    #[error("duplicate key in batch: {key:?}")]
297    DuplicateKeyInBatch {
298        /// The duplicate key.
299        key: Vec<u8>,
300    },
301    /// An error returned by the backend storage.
302    #[error("backend error: {0}")]
303    Backend(String),
304}