Skip to main content

sierradb_server/
error.rs

1use std::time::SystemTimeError;
2
3use kameo::error::SendError;
4use sierradb_cluster::ClusterError;
5use sierradb_cluster::write::error::WriteError;
6use sierradb_protocol::ErrorCode;
7
8/// Extension trait for converting results to Redis error format.
9pub trait MapRedisError<T> {
10    fn map_redis_err(self) -> Result<T, String>;
11}
12
13impl<T, E: AsRedisError> MapRedisError<T> for Result<T, E> {
14    fn map_redis_err(self) -> Result<T, String> {
15        self.map_err(|err| err.as_redis_error())
16    }
17}
18
19/// Trait for converting errors to Redis-compatible error strings.
20pub trait AsRedisError {
21    fn as_redis_error(&self) -> String;
22}
23
24impl AsRedisError for String {
25    fn as_redis_error(&self) -> String {
26        self.clone()
27    }
28}
29
30impl AsRedisError for &str {
31    fn as_redis_error(&self) -> String {
32        self.to_string()
33    }
34}
35
36impl<M, E: AsRedisError> AsRedisError for SendError<M, E> {
37    fn as_redis_error(&self) -> String {
38        match self {
39            SendError::ActorNotRunning(_) => ErrorCode::ActorDown.with_message("actor not running"),
40            SendError::ActorStopped => {
41                ErrorCode::ActorStopped.with_message("actor died when executing request")
42            }
43            SendError::MailboxFull(_) => {
44                ErrorCode::MailboxFull.with_message("actor mailbox is full")
45            }
46            SendError::HandlerError(err) => err.as_redis_error(),
47            SendError::Timeout(_) => ErrorCode::Timeout.to_string(),
48        }
49    }
50}
51
52impl AsRedisError for WriteError {
53    fn as_redis_error(&self) -> String {
54        let code = match self {
55            WriteError::AllReplicasFailed => ErrorCode::AllReplicasFailed,
56            WriteError::BufferEvicted => ErrorCode::BufferEvicted,
57            WriteError::BufferFull => ErrorCode::BufferFull,
58            WriteError::CircuitBreakerOpen { .. } => ErrorCode::CircuitOpen,
59            WriteError::ConfirmationFailed(_) => ErrorCode::ConfirmFailed,
60            WriteError::ReplicationConfirmationFailed(_) => ErrorCode::ReplConfirmFailed,
61            WriteError::DatabaseOperationFailed(_) => ErrorCode::DbOpFailed,
62            WriteError::InsufficientHealthyReplicas { .. } => ErrorCode::InsufficientReplicas,
63            WriteError::InvalidSender => ErrorCode::InvalidSender,
64            WriteError::StaleWrite => ErrorCode::StaleWrite,
65            WriteError::MissingExpectedPartitionSequence => ErrorCode::MissingPartSeq,
66            WriteError::PartitionNotOwned { .. } => ErrorCode::PartNotOwned,
67            WriteError::ReplicationQuorumFailed { .. } => ErrorCode::QuorumFailed,
68            WriteError::RequestTimeout => ErrorCode::Timeout,
69            WriteError::MaximumForwardsExceeded { .. } => ErrorCode::MaxForwards,
70            WriteError::RemoteOperationFailed(_) => ErrorCode::RemoteOpFailed,
71            WriteError::SequenceConflict => ErrorCode::SeqConflict,
72            WriteError::WrongExpectedSequence { .. } => ErrorCode::WrongSeq,
73            WriteError::WrongExpectedVersion { .. } => ErrorCode::WrongVer,
74        };
75
76        format!("{code} {self}")
77    }
78}
79
80impl AsRedisError for SystemTimeError {
81    fn as_redis_error(&self) -> String {
82        ErrorCode::ClockErr.with_message(self.to_string())
83    }
84}
85
86impl AsRedisError for kameo::error::Infallible {
87    fn as_redis_error(&self) -> String {
88        unreachable!()
89    }
90}
91
92impl AsRedisError for ClusterError {
93    fn as_redis_error(&self) -> String {
94        let code = match self {
95            ClusterError::InsufficientPartitionsForQuorum { .. } => ErrorCode::ClusterDown,
96            ClusterError::NoAvailablePartitions => ErrorCode::ClusterDown,
97            ClusterError::PartitionUnavailable => ErrorCode::ClusterDown,
98            ClusterError::NoAvailableLeaders => ErrorCode::ClusterDown,
99            ClusterError::QuorumNotAchieved { .. } => ErrorCode::TryAgain,
100            ClusterError::WriteTimeout => ErrorCode::Timeout,
101            ClusterError::TooManyForwards => ErrorCode::MaxForwards,
102            ClusterError::CircuitBreakerOpen { .. } => ErrorCode::CircuitOpen,
103            ClusterError::ConfirmationFailure(_) => ErrorCode::ConfirmFailed,
104            ClusterError::Read(_) => ErrorCode::ReadErr,
105            ClusterError::Write(_) => ErrorCode::WriteErr,
106            ClusterError::RemoteSend(_) => ErrorCode::RemoteErr,
107            ClusterError::Noise(_) => ErrorCode::NoiseErr,
108            ClusterError::Transport(_) => ErrorCode::TransportErr,
109            ClusterError::SwarmBuilder(_) => ErrorCode::SwarmErr,
110        };
111
112        match self {
113            ClusterError::InsufficientPartitionsForQuorum { alive, required } => code.with_message(
114                format!("insufficient partitions alive for quorum ({alive}/{required})"),
115            ),
116            ClusterError::NoAvailablePartitions => code.with_message("no available partitions"),
117            ClusterError::PartitionUnavailable => code.with_message("partition unavailable"),
118            ClusterError::NoAvailableLeaders => code.with_message("no available leaders"),
119            ClusterError::QuorumNotAchieved {
120                confirmed,
121                required,
122            } => code.with_message(format!("quorum not achieved ({confirmed}/{required})")),
123            ClusterError::WriteTimeout => code.with_message("write timed out"),
124            ClusterError::TooManyForwards => code.with_message("too many forwards"),
125            ClusterError::CircuitBreakerOpen {
126                estimated_recovery_time,
127            } => match estimated_recovery_time {
128                Some(time) => code.with_message(format!(
129                    "circuit breaker open: estimated recovery time: {time:?}"
130                )),
131                None => code.with_message("circuit breaker open"),
132            },
133            ClusterError::ConfirmationFailure(msg) => {
134                code.with_message(format!("failed to write confirmation count: {msg}"))
135            }
136            ClusterError::Read(msg) => code.with_message(format!("read error: {msg}")),
137            ClusterError::Write(msg) => code.with_message(format!("write error: {msg}")),
138            ClusterError::RemoteSend(err) => code.with_message(format!("remote send error: {err}")),
139            ClusterError::Noise(err) => code.with_message(format!("noise error: {err}")),
140            ClusterError::Transport(err) => code.with_message(format!("transport error: {err}")),
141            ClusterError::SwarmBuilder(err) => {
142                code.with_message(format!("swarm builder error: {err}"))
143            }
144        }
145    }
146}