Skip to main content

surrealdb_core/kvs/
err.rs

1use thiserror::Error;
2
3/// Result type for KVS (Key-Value Store) layer operations
4pub type Result<T> = std::result::Result<T, Error>;
5
6/// An error originating from the KVS (Key-Value Store) layer.
7///
8/// This error type abstracts storage engine details and provides
9/// generic error variants that can be used across all storage backends.
10#[allow(dead_code, reason = "Some variants are only used by specific KV stores")]
11#[derive(Error, Debug)]
12pub enum Error {
13	/// There was a problem with the underlying datastore
14	#[error("There was a problem with the datastore: {0}")]
15	Datastore(String),
16
17	/// Failed to connect to the storage backend
18	#[error("Connection to storage backend failed: {0}")]
19	ConnectionFailed(String),
20
21	/// The datastore is read-and-deletion-only due to disk saturation
22	#[error(
23		"The datastore is in read-and-deletion-only mode due to disk space limitations. Only read and delete operations are allowed. Deleting data will free up space and automatically restore normal operations when usage drops below the threshold"
24	)]
25	ReadAndDeleteOnly,
26
27	/// There was a problem with a datastore transaction
28	#[error("There was a problem with a transaction: {0}")]
29	Transaction(String),
30
31	/// The transaction is too large
32	#[error("The transaction is too large")]
33	TransactionTooLarge,
34
35	/// The key being inserted in the transaction is too large
36	#[error("The key being inserted is too large")]
37	TransactionKeyTooLarge,
38
39	/// A transaction conflict occurred and the operation should be retried
40	#[error("Transaction conflict: {0}. This transaction can be retried")]
41	TransactionConflict(String),
42
43	/// The transaction was already cancelled or committed
44	#[error("Couldn't update a finished transaction")]
45	TransactionFinished,
46
47	/// The current transaction was created as read-only
48	#[error("Couldn't write to a read only transaction")]
49	TransactionReadonly,
50
51	/// The conditional value in the request was not equal
52	#[error("Value being checked was not correct")]
53	TransactionConditionNotMet,
54
55	/// The key being inserted in the transaction already exists
56	#[error("The key being inserted already exists")]
57	TransactionKeyAlreadyExists,
58
59	/// The underlying datastore does not support versioned queries
60	#[error("The underlying datastore does not support versioned queries")]
61	UnsupportedVersionedQueries,
62
63	/// The specified timestamp is not valid for the underlying datastore
64	#[error("The specified timestamp is not valid for the underlying datastore: {0}")]
65	TimestampInvalid(String),
66
67	/// There was an unknown internal error
68	#[error("There was an internal error: {0}")]
69	Internal(String),
70
71	#[error("The storage layer does not support compaction requests.")]
72	CompactionNotSupported,
73}
74
75impl Error {
76	/// Check if this error indicates the transaction can be retried
77	pub fn is_retryable(&self) -> bool {
78		matches!(self, Error::TransactionConflict(_))
79	}
80}
81
82impl From<std::num::TryFromIntError> for Error {
83	fn from(e: std::num::TryFromIntError) -> Error {
84		Error::TimestampInvalid(e.to_string())
85	}
86}
87
88#[cfg(feature = "kv-mem")]
89impl From<surrealmx::Error> for Error {
90	fn from(e: surrealmx::Error) -> Error {
91		match e {
92			surrealmx::Error::TxNotWritable => Error::TransactionReadonly,
93			surrealmx::Error::ValNotExpectedValue => Error::TransactionConditionNotMet,
94			surrealmx::Error::TxClosed => Error::TransactionFinished,
95			surrealmx::Error::KeyAlreadyExists => Error::TransactionKeyAlreadyExists,
96			surrealmx::Error::KeyReadConflict => Error::TransactionConflict(e.to_string()),
97			surrealmx::Error::KeyWriteConflict => Error::TransactionConflict(e.to_string()),
98			_ => Error::Transaction(e.to_string()),
99		}
100	}
101}
102
103#[cfg(feature = "kv-surrealkv")]
104impl From<surrealkv::Error> for Error {
105	fn from(e: surrealkv::Error) -> Error {
106		match e {
107			surrealkv::Error::TransactionWriteConflict => Error::TransactionConflict(e.to_string()),
108			surrealkv::Error::TransactionReadOnly => Error::TransactionReadonly,
109			surrealkv::Error::TransactionClosed => Error::TransactionFinished,
110			_ => Error::Transaction(e.to_string()),
111		}
112	}
113}
114
115#[cfg(feature = "kv-rocksdb")]
116impl From<rocksdb::Error> for Error {
117	fn from(e: rocksdb::Error) -> Error {
118		match e.kind() {
119			rocksdb::ErrorKind::Busy => Error::TransactionConflict(e.to_string()),
120			rocksdb::ErrorKind::TryAgain => Error::TransactionConflict(e.to_string()),
121			_ => Error::Transaction(e.to_string()),
122		}
123	}
124}
125
126#[cfg(feature = "kv-indxdb")]
127impl From<indxdb::Error> for Error {
128	fn from(e: indxdb::Error) -> Error {
129		match e {
130			indxdb::Error::DbError => Error::Datastore(e.to_string()),
131			indxdb::Error::TxError => Error::Transaction(e.to_string()),
132			indxdb::Error::TxClosed => Error::TransactionFinished,
133			indxdb::Error::TxNotWritable => Error::TransactionReadonly,
134			indxdb::Error::KeyAlreadyExists => Error::TransactionKeyAlreadyExists,
135			indxdb::Error::ValNotExpectedValue => Error::TransactionConditionNotMet,
136			_ => Error::Transaction(e.to_string()),
137		}
138	}
139}
140
141#[cfg(feature = "kv-tikv")]
142impl From<tikv::Error> for Error {
143	fn from(e: tikv::Error) -> Error {
144		match e {
145			tikv::Error::DuplicateKeyInsertion => Error::TransactionKeyAlreadyExists,
146			tikv::Error::Grpc(_) => Error::ConnectionFailed(e.to_string()),
147			tikv::Error::KeyError(ref ke) => {
148				if let Some(conflict) = &ke.conflict {
149					use crate::key::debug::Sprintable;
150					Error::TransactionConflict(conflict.key.sprint())
151				} else if ke.already_exist.is_some() {
152					Error::TransactionKeyAlreadyExists
153				} else if ke.abort.contains("KeyTooLarge") {
154					Error::TransactionKeyTooLarge
155				} else {
156					Error::Transaction(e.to_string())
157				}
158			}
159			tikv::Error::RegionError(ref re) if re.raft_entry_too_large.is_some() => {
160				Error::TransactionTooLarge
161			}
162			_ => Error::Transaction(e.to_string()),
163		}
164	}
165}
166
167// Conversion from anyhow::Error for compatibility with existing code
168impl From<anyhow::Error> for Error {
169	fn from(e: anyhow::Error) -> Self {
170		// Try to downcast to see if it's already a KVS error
171		match e.downcast::<Error>() {
172			Ok(e) => e,
173			Err(e) => Error::Internal(e.to_string()),
174		}
175	}
176}