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}
143
144impl FdbBindingError {
145    /// Returns the underlying `FdbError`, if any.
146    pub fn get_fdb_error(&self) -> Option<FdbError> {
147        match *self {
148            Self::NonRetryableFdbError(error)
149            | Self::DirectoryError(DirectoryError::FdbError(error))
150            | Self::HcaError(HcaError::FdbError(error)) => Some(error),
151            Self::CustomError(ref error) => {
152                if let Some(e) = error.downcast_ref::<FdbError>() {
153                    Some(*e)
154                } else if let Some(e) = error.downcast_ref::<FdbBindingError>() {
155                    e.get_fdb_error()
156                } else {
157                    None
158                }
159            }
160            _ => None,
161        }
162    }
163}
164
165impl From<FdbError> for FdbBindingError {
166    fn from(e: FdbError) -> Self {
167        Self::NonRetryableFdbError(e)
168    }
169}
170
171impl From<HcaError> for FdbBindingError {
172    fn from(e: HcaError) -> Self {
173        Self::HcaError(e)
174    }
175}
176
177impl From<DirectoryError> for FdbBindingError {
178    fn from(e: DirectoryError) -> Self {
179        Self::DirectoryError(e)
180    }
181}
182
183impl From<TransactionMetricsNotFound> for FdbBindingError {
184    fn from(_e: TransactionMetricsNotFound) -> Self {
185        Self::TransactionMetricsNotFound
186    }
187}
188
189impl FdbBindingError {
190    /// create a new custom error
191    pub fn new_custom_error(e: Box<dyn std::error::Error + Send + Sync>) -> Self {
192        Self::CustomError(e)
193    }
194}
195
196impl Debug for FdbBindingError {
197    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
198        match self {
199            FdbBindingError::NonRetryableFdbError(err) => write!(f, "{err:?}"),
200            FdbBindingError::HcaError(err) => write!(f, "{err:?}"),
201            FdbBindingError::DirectoryError(err) => write!(f, "{err:?}"),
202            FdbBindingError::PackError(err) => write!(f, "{err:?}"),
203            FdbBindingError::ReferenceToTransactionKept => {
204                write!(f, "Reference to transaction kept")
205            }
206            FdbBindingError::CustomError(err) => write!(f, "{err:?}"),
207            FdbBindingError::TransactionMetricsNotFound => {
208                write!(f, "Transaction metrics not found")
209            }
210        }
211    }
212}
213
214impl Display for FdbBindingError {
215    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
216        std::fmt::Debug::fmt(&self, f)
217    }
218}
219
220impl std::error::Error for FdbBindingError {}