Skip to main content

vertep_store/
error.rs

1//! Errors of the storage contracts.
2
3use core::fmt;
4
5use oxrdf::NamedNode;
6
7use crate::journal::Position;
8
9/// An error of a read, a graph store operation or a backend outside the append protocol.
10#[derive(Clone, Debug, PartialEq, Eq)]
11pub enum StoreError {
12    /// The store cannot be reached right now (a remote endpoint is down, a file is busy). The
13    /// operation may succeed when retried.
14    Unavailable(String),
15    /// The request is malformed: an invalid entry, a quad the store cannot hold, invalid SPARQL.
16    Invalid(String),
17    /// The store does not implement this operation or this kind of term.
18    Unsupported(String),
19    /// Any other failure inside the backend.
20    Backend(String),
21}
22
23impl StoreError {
24    /// An [`StoreError::Invalid`] from any message.
25    pub fn invalid(msg: impl Into<String>) -> Self {
26        StoreError::Invalid(msg.into())
27    }
28
29    /// A [`StoreError::Backend`] from any error.
30    pub fn backend(err: impl fmt::Display) -> Self {
31        StoreError::Backend(err.to_string())
32    }
33
34    /// True for [`StoreError::Unavailable`]: retrying later may succeed.
35    pub fn is_transient(&self) -> bool {
36        matches!(self, StoreError::Unavailable(_))
37    }
38}
39
40impl fmt::Display for StoreError {
41    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
42        match self {
43            StoreError::Unavailable(m) => write!(f, "store unavailable: {m}"),
44            StoreError::Invalid(m) => write!(f, "invalid request: {m}"),
45            StoreError::Unsupported(m) => write!(f, "unsupported: {m}"),
46            StoreError::Backend(m) => write!(f, "backend error: {m}"),
47        }
48    }
49}
50
51impl std::error::Error for StoreError {}
52
53/// Why a journal append wrote nothing.
54///
55/// Every variant means the same thing for the journal: none of the append's entries, and none
56/// of its state changes, are visible to any later read.
57#[derive(Clone, Debug, PartialEq, Eq)]
58pub enum AppendError {
59    /// The actor's journal is not at the expected position (compare-and-set failed).
60    Conflict {
61        /// The position the append expected.
62        expected: Position,
63        /// The position the journal is actually at.
64        actual: Position,
65    },
66    /// The fencing token is older than the highest epoch the store has accepted for its scope.
67    Fenced {
68        /// The scope of the token.
69        scope: NamedNode,
70        /// The epoch the append carried.
71        epoch: u64,
72        /// The highest epoch accepted so far for the scope.
73        highest: u64,
74    },
75    /// The append carried a fencing token or a state delta, and the store does not support it
76    /// (see [`crate::JournalCapabilities`]).
77    Unsupported(String),
78    /// The entries are malformed (empty append, wrong graph, duplicate entry IRI, a term the
79    /// store cannot hold).
80    Invalid(String),
81    /// The store failed; the transaction was rolled back.
82    Storage(StoreError),
83}
84
85impl AppendError {
86    /// True for [`AppendError::Conflict`].
87    pub fn is_conflict(&self) -> bool {
88        matches!(self, AppendError::Conflict { .. })
89    }
90
91    /// True for [`AppendError::Fenced`].
92    pub fn is_fenced(&self) -> bool {
93        matches!(self, AppendError::Fenced { .. })
94    }
95}
96
97impl fmt::Display for AppendError {
98    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
99        match self {
100            AppendError::Conflict { expected, actual } => write!(
101                f,
102                "append conflict: expected position {}, journal is at {}",
103                expected.get(),
104                actual.get()
105            ),
106            AppendError::Fenced {
107                scope,
108                epoch,
109                highest,
110            } => write!(
111                f,
112                "append fenced: epoch {epoch} for {scope} is below the accepted epoch {highest}"
113            ),
114            AppendError::Unsupported(m) => write!(f, "unsupported append: {m}"),
115            AppendError::Invalid(m) => write!(f, "invalid append: {m}"),
116            AppendError::Storage(e) => write!(f, "append failed: {e}"),
117        }
118    }
119}
120
121impl std::error::Error for AppendError {
122    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
123        match self {
124            AppendError::Storage(e) => Some(e),
125            _ => None,
126        }
127    }
128}
129
130impl From<StoreError> for AppendError {
131    fn from(e: StoreError) -> Self {
132        match e {
133            StoreError::Invalid(m) => AppendError::Invalid(m),
134            StoreError::Unsupported(m) => AppendError::Unsupported(m),
135            other => AppendError::Storage(other),
136        }
137    }
138}