foundationdb/
error.rs

1// Copyright 2018 foundationdb-rs developers, https://github.com/Clikengo/foundationdb-rs/graphs/contributors
2// Copyright 2013-2018 Apple, Inc and the FoundationDB project authors.
3//
4// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
5// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
6// http://opensource.org/licenses/MIT>, at your option. This file may not be
7// copied, modified, or distributed except according to those terms.
8
9//! Error types for the Fdb crate
10
11use crate::directory::DirectoryError;
12use crate::options;
13use crate::tuple::hca::HcaError;
14use crate::tuple::PackError;
15use foundationdb_sys as fdb_sys;
16use std::ffi::CStr;
17use std::fmt;
18use std::fmt::{Debug, Display, Formatter};
19
20pub(crate) fn eval(error_code: fdb_sys::fdb_error_t) -> FdbResult<()> {
21    let rust_code: i32 = error_code;
22    if rust_code == 0 {
23        Ok(())
24    } else {
25        Err(FdbError::from_code(error_code))
26    }
27}
28
29/// Error returned when attempting to access metrics on a transaction that wasn't created with metrics instrumentation.
30///
31/// This error occurs when calling methods like `set_custom_metric` or `increment_custom_metric` on a
32/// transaction that was created without metrics instrumentation (i.e., using `create_trx` instead of
33/// `create_instrumented_trx`).
34///
35/// # Example
36/// ```
37/// # use foundationdb::*;
38/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
39/// let db = Database::default()?;
40///
41/// // This transaction doesn't have metrics instrumentation
42/// let txn = db.create_trx()?;
43///
44/// // This will return a TransactionMetricsNotFound error
45/// let result = txn.set_custom_metric("my_metric", 42, &[("label", "value")]);
46/// assert!(result.is_err());
47/// # Ok(())
48/// # }
49/// ```
50#[derive(Debug)]
51pub struct TransactionMetricsNotFound;
52
53impl std::fmt::Display for TransactionMetricsNotFound {
54    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
55        write!(f, "Transaction metrics not found")
56    }
57}
58
59impl std::error::Error for TransactionMetricsNotFound {}
60
61/// The Standard Error type of FoundationDB
62#[derive(Debug, Copy, Clone)]
63pub struct FdbError {
64    /// The FoundationDB error code
65    error_code: i32,
66}
67
68impl FdbError {
69    /// Converts from a raw foundationDB error code
70    pub fn from_code(error_code: fdb_sys::fdb_error_t) -> Self {
71        Self { error_code }
72    }
73
74    #[cfg(feature = "tenant-experimental")]
75    /// Converts from a raw foundationDB error code
76    pub(crate) fn new(error_code: i32) -> Self {
77        Self { error_code }
78    }
79
80    pub fn message(self) -> &'static str {
81        let error_str =
82            unsafe { CStr::from_ptr::<'static>(fdb_sys::fdb_get_error(self.error_code)) };
83        error_str
84            .to_str()
85            .expect("bad error string from FoundationDB")
86    }
87
88    fn is_error_predicate(self, predicate: options::ErrorPredicate) -> bool {
89        // This cast to `i32` isn't unnecessary in all configurations.
90        #[allow(clippy::unnecessary_cast)]
91        let check =
92            unsafe { fdb_sys::fdb_error_predicate(predicate.code() as i32, self.error_code) };
93
94        check != 0
95    }
96
97    /// Indicates the transaction may have succeeded, though not in a way the system can verify.
98    pub fn is_maybe_committed(self) -> bool {
99        self.is_error_predicate(options::ErrorPredicate::MaybeCommitted)
100    }
101
102    /// Indicates the operations in the transactions should be retried because of transient error.
103    pub fn is_retryable(self) -> bool {
104        self.is_error_predicate(options::ErrorPredicate::Retryable)
105    }
106
107    /// Indicates the transaction has not committed, though in a way that can be retried.
108    pub fn is_retryable_not_committed(self) -> bool {
109        self.is_error_predicate(options::ErrorPredicate::RetryableNotCommitted)
110    }
111
112    /// Raw foundationdb error code
113    pub fn code(self) -> i32 {
114        self.error_code
115    }
116}
117
118impl fmt::Display for FdbError {
119    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
120        std::fmt::Display::fmt(&self.message(), f)
121    }
122}
123
124impl std::error::Error for FdbError {}
125
126/// Alias for `Result<..., FdbError>`
127pub type FdbResult<T = ()> = Result<T, FdbError>;
128
129/// This error represent all errors that can be throwed by `db.run`.
130/// Layer developers may use the `CustomError`.
131pub enum FdbBindingError {
132    NonRetryableFdbError(FdbError),
133    HcaError(HcaError),
134    DirectoryError(DirectoryError),
135    PackError(PackError),
136    /// A reference to the `RetryableTransaction` has been kept
137    ReferenceToTransactionKept,
138    /// A custom error that layer developers can use
139    CustomError(Box<dyn std::error::Error + Send + Sync>),
140    /// Error returned when attempting to access metrics on a transaction that wasn't created with metrics instrumentation
141    TransactionMetricsNotFound,
142    #[cfg(feature = "recipes-leader-election")]
143    /// Leader election specific error
144    LeaderElectionError(crate::recipes::leader_election::LeaderElectionError),
145}
146
147impl FdbBindingError {
148    /// Returns the underlying `FdbError`, if any.
149    pub fn get_fdb_error(&self) -> Option<FdbError> {
150        match *self {
151            Self::NonRetryableFdbError(error)
152            | Self::DirectoryError(DirectoryError::FdbError(error))
153            | Self::HcaError(HcaError::FdbError(error)) => Some(error),
154            Self::CustomError(ref error) => {
155                if let Some(e) = error.downcast_ref::<FdbError>() {
156                    Some(*e)
157                } else if let Some(e) = error.downcast_ref::<FdbBindingError>() {
158                    e.get_fdb_error()
159                } else {
160                    None
161                }
162            }
163            #[cfg(feature = "recipes-leader-election")]
164            Self::LeaderElectionError(
165                crate::recipes::leader_election::LeaderElectionError::Fdb(e),
166            ) => Some(e),
167            _ => None,
168        }
169    }
170}
171
172impl From<FdbError> for FdbBindingError {
173    fn from(e: FdbError) -> Self {
174        Self::NonRetryableFdbError(e)
175    }
176}
177
178impl From<HcaError> for FdbBindingError {
179    fn from(e: HcaError) -> Self {
180        Self::HcaError(e)
181    }
182}
183
184impl From<DirectoryError> for FdbBindingError {
185    fn from(e: DirectoryError) -> Self {
186        Self::DirectoryError(e)
187    }
188}
189
190impl From<TransactionMetricsNotFound> for FdbBindingError {
191    fn from(_e: TransactionMetricsNotFound) -> Self {
192        Self::TransactionMetricsNotFound
193    }
194}
195
196#[cfg(feature = "recipes-leader-election")]
197impl From<crate::recipes::leader_election::LeaderElectionError> for FdbBindingError {
198    fn from(error: crate::recipes::leader_election::LeaderElectionError) -> Self {
199        Self::LeaderElectionError(error)
200    }
201}
202
203impl FdbBindingError {
204    /// create a new custom error
205    pub fn new_custom_error(e: Box<dyn std::error::Error + Send + Sync>) -> Self {
206        Self::CustomError(e)
207    }
208}
209
210impl Debug for FdbBindingError {
211    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
212        match self {
213            FdbBindingError::NonRetryableFdbError(err) => write!(f, "{err:?}"),
214            FdbBindingError::HcaError(err) => write!(f, "{err:?}"),
215            FdbBindingError::DirectoryError(err) => write!(f, "{err:?}"),
216            FdbBindingError::PackError(err) => write!(f, "{err:?}"),
217            FdbBindingError::ReferenceToTransactionKept => {
218                write!(f, "Reference to transaction kept")
219            }
220            FdbBindingError::CustomError(err) => write!(f, "{err:?}"),
221            FdbBindingError::TransactionMetricsNotFound => {
222                write!(f, "Transaction metrics not found")
223            }
224            #[cfg(feature = "recipes-leader-election")]
225            FdbBindingError::LeaderElectionError(err) => write!(f, "{err:?}"),
226        }
227    }
228}
229
230impl Display for FdbBindingError {
231    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
232        std::fmt::Debug::fmt(&self, f)
233    }
234}
235
236impl std::error::Error for FdbBindingError {}