1use std::error::Error;
11
12use super::BoxedError;
13use crate::error::codes::{ErrorCode, PublicError, ToPublicError};
14
15pub type StorageResult<T> = Result<T, StorageError>;
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
20pub enum StorageErrorKind {
21 DbError,
22 StorageError,
23 SerializeError,
24 DeserializeError,
25 NodeNotFound,
26 EdgeNotFound,
27 NotSupported,
28 Conflict,
29 LockTimeout,
30 Deadlock,
31 IOError,
32 NotFound,
33 AlreadyExists,
34 InvalidInput,
35 ParseError,
36 VertexNotFound,
37 VertexAlreadyExists,
38 EdgeAlreadyExists,
39 LabelNotFound,
40 LabelAlreadyExists,
41 PropertyNotFound,
42 ColumnNotFound,
43 ColumnAlreadyExists,
44 StorageNotOpen,
45 CapacityExceeded,
46 NullValueNotAllowed,
47 TypeMismatch,
48 InvalidOffset,
49 InvalidOperation,
50 WalError,
51 CompressError,
52 DecompressError,
53 DataCorruption,
54 HistoryBeforeFloor,
55}
56
57impl StorageErrorKind {
58 pub fn as_str(&self) -> &'static str {
59 match self {
60 StorageErrorKind::DbError => "db_error",
61 StorageErrorKind::StorageError => "storage_error",
62 StorageErrorKind::SerializeError => "serialize_error",
63 StorageErrorKind::DeserializeError => "deserialize_error",
64 StorageErrorKind::NodeNotFound => "node_not_found",
65 StorageErrorKind::EdgeNotFound => "edge_not_found",
66 StorageErrorKind::NotSupported => "not_supported",
67 StorageErrorKind::Conflict => "conflict",
68 StorageErrorKind::LockTimeout => "lock_timeout",
69 StorageErrorKind::Deadlock => "deadlock",
70 StorageErrorKind::IOError => "io_error",
71 StorageErrorKind::NotFound => "not_found",
72 StorageErrorKind::AlreadyExists => "already_exists",
73 StorageErrorKind::InvalidInput => "invalid_input",
74 StorageErrorKind::ParseError => "parse_error",
75 StorageErrorKind::VertexNotFound => "vertex_not_found",
76 StorageErrorKind::VertexAlreadyExists => "vertex_already_exists",
77 StorageErrorKind::EdgeAlreadyExists => "edge_already_exists",
78 StorageErrorKind::LabelNotFound => "label_not_found",
79 StorageErrorKind::LabelAlreadyExists => "label_already_exists",
80 StorageErrorKind::PropertyNotFound => "property_not_found",
81 StorageErrorKind::ColumnNotFound => "column_not_found",
82 StorageErrorKind::ColumnAlreadyExists => "column_already_exists",
83 StorageErrorKind::StorageNotOpen => "storage_not_open",
84 StorageErrorKind::CapacityExceeded => "capacity_exceeded",
85 StorageErrorKind::NullValueNotAllowed => "null_value_not_allowed",
86 StorageErrorKind::TypeMismatch => "type_mismatch",
87 StorageErrorKind::InvalidOffset => "invalid_offset",
88 StorageErrorKind::InvalidOperation => "invalid_operation",
89 StorageErrorKind::WalError => "wal_error",
90 StorageErrorKind::CompressError => "compress_error",
91 StorageErrorKind::DecompressError => "decompress_error",
92 StorageErrorKind::DataCorruption => "data_corruption",
93 StorageErrorKind::HistoryBeforeFloor => "history_before_floor",
94 }
95 }
96}
97
98#[derive(Debug)]
105pub struct StorageError {
106 kind: StorageErrorKind,
107 message: String,
108 source: Option<BoxedError>,
109}
110
111impl StorageError {
112 pub fn new(kind: StorageErrorKind, message: impl Into<String>) -> Self {
113 Self {
114 kind,
115 message: message.into(),
116 source: None,
117 }
118 }
119
120 pub fn with_source(mut self, source: BoxedError) -> Self {
121 self.source = Some(source);
122 self
123 }
124
125 pub fn kind(&self) -> StorageErrorKind {
126 self.kind
127 }
128
129 pub fn message(&self) -> &str {
130 &self.message
131 }
132
133 pub fn source(&self) -> &Option<BoxedError> {
134 &self.source
135 }
136
137 fn from_boxed<E: Error + Send + Sync + 'static>(kind: StorageErrorKind, error: E) -> Self {
138 Self {
139 kind,
140 message: error.to_string(),
141 source: Some(Box::new(error)),
142 }
143 }
144
145 pub fn is_retryable(&self) -> bool {
146 matches!(
147 self.kind,
148 StorageErrorKind::LockTimeout | StorageErrorKind::Deadlock
149 )
150 }
151
152 pub fn db_error(message: impl Into<String>) -> Self {
154 Self::new(StorageErrorKind::DbError, message)
155 }
156
157 #[allow(clippy::self_named_constructors)]
158 pub fn storage_error(message: impl Into<String>) -> Self {
159 Self::new(StorageErrorKind::StorageError, message)
160 }
161
162 pub fn serialize_error(message: impl Into<String>) -> Self {
163 Self::new(StorageErrorKind::SerializeError, message)
164 }
165
166 pub fn deserialize_error(message: impl Into<String>) -> Self {
167 Self::new(StorageErrorKind::DeserializeError, message)
168 }
169
170 pub fn node_not_found(value: crate::types::VertexId) -> Self {
171 Self::new(StorageErrorKind::NodeNotFound, format!("{}", value))
172 }
173
174 pub fn edge_not_found(value: crate::types::VertexId) -> Self {
175 Self::new(StorageErrorKind::EdgeNotFound, format!("{}", value))
176 }
177
178 pub fn not_supported(message: impl Into<String>) -> Self {
179 Self::new(StorageErrorKind::NotSupported, message)
180 }
181
182 pub fn conflict(message: impl Into<String>) -> Self {
183 Self::new(StorageErrorKind::Conflict, message)
184 }
185
186 pub fn write_write_conflict(entity: impl Into<String>) -> Self {
193 Self::new(
194 StorageErrorKind::Conflict,
195 format!("Write-write conflict: {}", entity.into()),
196 )
197 }
198
199 pub fn lock_timeout(message: impl Into<String>) -> Self {
200 Self::new(StorageErrorKind::LockTimeout, message)
201 }
202
203 pub fn deadlock() -> Self {
204 Self::new(StorageErrorKind::Deadlock, "Deadlock detected")
205 }
206
207 pub fn io_error(message: impl Into<String>) -> Self {
208 Self::new(StorageErrorKind::IOError, message)
209 }
210
211 pub fn not_found(message: impl Into<String>) -> Self {
212 Self::new(StorageErrorKind::NotFound, message)
213 }
214
215 pub fn already_exists(message: impl Into<String>) -> Self {
216 Self::new(StorageErrorKind::AlreadyExists, message)
217 }
218
219 pub fn invalid_input(message: impl Into<String>) -> Self {
220 Self::new(StorageErrorKind::InvalidInput, message)
221 }
222
223 pub fn parse_error(message: impl Into<String>) -> Self {
224 Self::new(StorageErrorKind::ParseError, message)
225 }
226
227 pub fn vertex_not_found() -> Self {
228 Self::new(StorageErrorKind::VertexNotFound, "Vertex not found")
229 }
230
231 pub fn vertex_already_exists(message: impl Into<String>) -> Self {
232 Self::new(StorageErrorKind::VertexAlreadyExists, message)
233 }
234
235 pub fn edge_already_exists(message: impl Into<String>) -> Self {
236 Self::new(StorageErrorKind::EdgeAlreadyExists, message)
237 }
238
239 pub fn label_not_found(message: impl Into<String>) -> Self {
240 Self::new(StorageErrorKind::LabelNotFound, message)
241 }
242
243 pub fn label_already_exists(message: impl Into<String>) -> Self {
244 Self::new(StorageErrorKind::LabelAlreadyExists, message)
245 }
246
247 pub fn property_not_found(message: impl Into<String>) -> Self {
248 Self::new(StorageErrorKind::PropertyNotFound, message)
249 }
250
251 pub fn column_not_found(message: impl Into<String>) -> Self {
252 Self::new(StorageErrorKind::ColumnNotFound, message)
253 }
254
255 pub fn column_already_exists(message: impl Into<String>) -> Self {
256 Self::new(StorageErrorKind::ColumnAlreadyExists, message)
257 }
258
259 pub fn storage_not_open() -> Self {
260 Self::new(StorageErrorKind::StorageNotOpen, "Storage not open")
261 }
262
263 pub fn capacity_exceeded() -> Self {
264 Self::new(StorageErrorKind::CapacityExceeded, "Capacity exceeded")
265 }
266
267 pub fn null_value_not_allowed(column: impl Into<String>) -> Self {
268 Self::new(
269 StorageErrorKind::NullValueNotAllowed,
270 format!("Null value not allowed for column: {}", column.into()),
271 )
272 }
273
274 pub fn type_mismatch(expected: crate::DataType, actual: crate::DataType) -> Self {
275 Self::new(
276 StorageErrorKind::TypeMismatch,
277 format!("Type mismatch: expected {:?}, got {:?}", expected, actual),
278 )
279 }
280
281 pub fn invalid_offset(offset: u32) -> Self {
282 Self::new(
283 StorageErrorKind::InvalidOffset,
284 format!("Invalid offset: {}", offset),
285 )
286 }
287
288 pub fn invalid_operation(message: impl Into<String>) -> Self {
289 Self::new(StorageErrorKind::InvalidOperation, message)
290 }
291
292 pub fn wal_error(message: impl Into<String>) -> Self {
293 Self::new(StorageErrorKind::WalError, message)
294 }
295
296 pub fn compress_error(message: impl Into<String>) -> Self {
297 Self::new(StorageErrorKind::CompressError, message)
298 }
299
300 pub fn decompress_error(message: impl Into<String>) -> Self {
301 Self::new(StorageErrorKind::DecompressError, message)
302 }
303
304 pub fn data_corruption(message: impl Into<String>) -> Self {
305 Self::new(StorageErrorKind::DataCorruption, message)
306 }
307
308 pub fn history_before_floor(row: u32, ts: u64, floor: u64) -> Self {
309 Self::new(
310 StorageErrorKind::HistoryBeforeFloor,
311 format!(
312 "vertex row {} history at {} before epoch floor {} is not retained; version chains do not survive checkpoints",
313 row, ts, floor
314 ),
315 )
316 }
317
318 pub fn history_before_floor_edge(edge: impl Into<String>, ts: u64, floor: u64) -> Self {
319 Self::new(
320 StorageErrorKind::HistoryBeforeFloor,
321 format!(
322 "edge {} history at {} before epoch floor {} is not retained; version chains do not survive checkpoints",
323 edge.into(),
324 ts,
325 floor
326 ),
327 )
328 }
329}
330
331impl std::fmt::Display for StorageError {
332 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
333 write!(f, "[{}] {}", self.kind.as_str(), self.message)?;
334 if let Some(ref source) = self.source {
335 write!(f, "\n Caused by: {}", source)?;
336 }
337 Ok(())
338 }
339}
340
341impl Clone for StorageError {
342 fn clone(&self) -> Self {
343 Self {
344 kind: self.kind,
345 message: self.message.clone(),
346 source: None,
347 }
348 }
349}
350
351impl std::error::Error for StorageError {
352 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
353 self.source
354 .as_ref()
355 .map(|e| e.as_ref() as &(dyn std::error::Error + 'static))
356 }
357}
358
359impl From<std::io::Error> for StorageError {
360 fn from(e: std::io::Error) -> Self {
361 Self::from_boxed(StorageErrorKind::IOError, e)
362 }
363}
364
365impl From<String> for StorageError {
366 fn from(s: String) -> Self {
367 Self::db_error(s)
368 }
369}
370
371impl From<&str> for StorageError {
372 fn from(s: &str) -> Self {
373 Self::db_error(s)
374 }
375}
376
377impl<T> From<std::sync::PoisonError<T>> for StorageError {
378 fn from(e: std::sync::PoisonError<T>) -> Self {
379 Self::db_error(e.to_string())
380 }
381}
382
383impl From<postcard::Error> for StorageError {
384 fn from(e: postcard::Error) -> Self {
385 Self::from_boxed(StorageErrorKind::SerializeError, e)
386 }
387}
388
389impl From<crate::types::UndoLogError> for StorageError {
390 fn from(e: crate::types::UndoLogError) -> Self {
391 Self::db_error(e.to_string())
392 }
393}
394
395impl ToPublicError for StorageError {
396 fn to_public_error(&self) -> PublicError {
397 PublicError::new(self.to_error_code(), self.to_public_message())
398 }
399
400 fn to_error_code(&self) -> ErrorCode {
401 match self.kind {
402 StorageErrorKind::NodeNotFound
403 | StorageErrorKind::EdgeNotFound
404 | StorageErrorKind::NotFound => ErrorCode::ResourceNotFound,
405 StorageErrorKind::AlreadyExists => ErrorCode::ResourceAlreadyExists,
406 StorageErrorKind::InvalidInput
407 | StorageErrorKind::InvalidOperation
408 | StorageErrorKind::HistoryBeforeFloor => ErrorCode::InvalidInput,
409 StorageErrorKind::LockTimeout => ErrorCode::Timeout,
410 StorageErrorKind::Deadlock => ErrorCode::Deadlock,
411 StorageErrorKind::Conflict => ErrorCode::Conflict,
412 StorageErrorKind::NotSupported => ErrorCode::InvalidStatement,
413 _ => ErrorCode::InternalError,
414 }
415 }
416
417 fn to_public_message(&self) -> String {
418 match self.kind {
419 StorageErrorKind::NodeNotFound => "Node does not exist".to_string(),
420 StorageErrorKind::EdgeNotFound => "Edge does not exist".to_string(),
421 StorageErrorKind::NotFound => format!("Resource not found: {}", self.message),
422 StorageErrorKind::AlreadyExists => format!("Resource already exists: {}", self.message),
423 _ => "Storage operation failed".to_string(),
424 }
425 }
426}
427
428#[cfg(test)]
429mod tests {
430 use super::*;
431
432 #[test]
433 fn test_storage_error_size() {
434 assert!(
435 std::mem::size_of::<StorageError>() <= 64,
436 "StorageError should be small, got {} bytes",
437 std::mem::size_of::<StorageError>()
438 );
439 }
440
441 #[test]
442 fn test_storage_error_creation() {
443 let err = StorageError::not_found("test resource");
444 assert_eq!(err.kind(), StorageErrorKind::NotFound);
445 assert!(err.message().contains("test resource"));
446 }
447
448 #[test]
449 fn test_retryable_error() {
450 let err = StorageError::lock_timeout("test");
451 assert!(err.is_retryable());
452
453 let err = StorageError::deadlock();
454 assert!(err.is_retryable());
455
456 let err = StorageError::not_found("test");
457 assert!(!err.is_retryable());
458 }
459}