autopush_common/db/bigtable/bigtable_client/
error.rs

1use std::fmt::{self, Display};
2
3use actix_web::http::StatusCode;
4use deadpool::managed::{PoolError, TimeoutType};
5use thiserror::Error;
6
7use crate::errors::ReportableError;
8
9#[derive(PartialEq, Eq, Debug)]
10pub enum MutateRowStatus {
11    OK,
12    Cancelled,
13    Unknown,
14    InvalidArgument,
15    DeadlineExceeded,
16    NotFound,
17    AlreadyExists,
18    PermissionDenied,
19    ResourceExhausted,
20    FailedPrecondition,
21    Aborted,
22    OutOfRange,
23    Unimplemented,
24    Internal,
25    Unavailable,
26    DataLoss,
27    Unauthenticated,
28}
29
30impl MutateRowStatus {
31    pub fn is_ok(&self) -> bool {
32        self == &Self::OK
33    }
34}
35
36impl From<i32> for MutateRowStatus {
37    fn from(v: i32) -> Self {
38        match v {
39            0 => Self::OK,
40            1 => Self::Cancelled,
41            2 => Self::Unknown,
42            3 => Self::InvalidArgument,
43            4 => Self::DeadlineExceeded,
44            5 => Self::NotFound,
45            6 => Self::AlreadyExists,
46            7 => Self::PermissionDenied,
47            8 => Self::ResourceExhausted,
48            9 => Self::FailedPrecondition,
49            10 => Self::Aborted,
50            11 => Self::OutOfRange,
51            12 => Self::Unimplemented,
52            13 => Self::Internal,
53            14 => Self::Unavailable,
54            15 => Self::DataLoss,
55            16 => Self::Unauthenticated,
56            _ => Self::Unknown,
57        }
58    }
59}
60
61impl Display for MutateRowStatus {
62    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
63        f.write_str(match self {
64            MutateRowStatus::OK => "Ok",
65            MutateRowStatus::Cancelled => "Cancelled",
66            MutateRowStatus::Unknown => "Unknown",
67            MutateRowStatus::InvalidArgument => "Invalid Argument",
68            MutateRowStatus::DeadlineExceeded => "Deadline Exceeded",
69            MutateRowStatus::NotFound => "Not Found",
70            MutateRowStatus::AlreadyExists => "Already Exists",
71            MutateRowStatus::PermissionDenied => "Permission Denied",
72            MutateRowStatus::ResourceExhausted => "Resource Exhausted",
73            MutateRowStatus::FailedPrecondition => "Failed Precondition",
74            MutateRowStatus::Aborted => "Aborted",
75            MutateRowStatus::OutOfRange => "Out of Range",
76            MutateRowStatus::Unimplemented => "Unimplemented",
77            MutateRowStatus::Internal => "Internal",
78            MutateRowStatus::Unavailable => "Unavailable",
79            MutateRowStatus::DataLoss => "Data Loss",
80            MutateRowStatus::Unauthenticated => "Unauthenticated",
81        })
82    }
83}
84
85impl MutateRowStatus {
86    pub fn status(&self) -> StatusCode {
87        match self {
88            MutateRowStatus::OK => StatusCode::OK,
89            // Some of these were taken from the java-bigtable-hbase retry handlers
90            MutateRowStatus::Aborted
91            | MutateRowStatus::DeadlineExceeded
92            | MutateRowStatus::Internal
93            | MutateRowStatus::ResourceExhausted
94            | MutateRowStatus::Unavailable => StatusCode::SERVICE_UNAVAILABLE,
95            _ => StatusCode::INTERNAL_SERVER_ERROR,
96        }
97    }
98}
99
100#[derive(Debug, Error)]
101pub enum BigTableError {
102    #[error("Invalid Row Response: {0}")]
103    InvalidRowResponse(#[source] grpcio::Error),
104
105    #[error("Invalid Chunk")]
106    InvalidChunk(String),
107
108    #[error("BigTable read error: {0}")]
109    Read(#[source] grpcio::Error),
110
111    #[error("BigTable write timestamp error: {0}")]
112    WriteTime(#[source] std::time::SystemTimeError),
113
114    #[error("Bigtable write error: {0}")]
115    Write(#[source] grpcio::Error),
116
117    #[error("GRPC Error: {0}")]
118    GRPC(#[source] grpcio::Error),
119
120    /// Return a GRPC status code and any message.
121    /// See https://grpc.github.io/grpc/core/md_doc_statuscodes.html
122    #[error("Bigtable status response: {0:?}")]
123    Status(MutateRowStatus, String),
124
125    #[error("BigTable Admin Error: {0}")]
126    Admin(String, Option<String>),
127
128    /// General Pool errors
129    #[error("Pool Error: {0}")]
130    Pool(Box<PoolError<BigTableError>>),
131
132    /// Timeout occurred while getting a pooled connection
133    #[error("Pool Timeout: {0:?}")]
134    PoolTimeout(TimeoutType),
135
136    #[error("BigTable config error: {0}")]
137    Config(String),
138}
139
140impl BigTableError {
141    pub fn status(&self) -> StatusCode {
142        match self {
143            BigTableError::PoolTimeout(_) => StatusCode::SERVICE_UNAVAILABLE,
144            BigTableError::Status(e, _) => e.status(),
145            _ => StatusCode::INTERNAL_SERVER_ERROR,
146        }
147    }
148}
149
150impl ReportableError for BigTableError {
151    fn is_sentry_event(&self) -> bool {
152        #[allow(clippy::match_like_matches_macro)]
153        match self {
154            BigTableError::PoolTimeout(_) => false,
155            _ => true,
156        }
157    }
158
159    fn metric_label(&self) -> Option<&'static str> {
160        let err = match self {
161            BigTableError::InvalidRowResponse(_) => "storage.bigtable.error.invalid_row_response",
162            BigTableError::InvalidChunk(_) => "storage.bigtable.error.invalid_chunk",
163            BigTableError::Read(_) => "storage.bigtable.error.read",
164            BigTableError::Write(_) => "storage.bigtable.error.write",
165            BigTableError::Status(_, _) => "storage.bigtable.error.status",
166            BigTableError::WriteTime(_) => "storage.bigtable.error.writetime",
167            BigTableError::Admin(_, _) => "storage.bigtable.error.admin",
168            BigTableError::Pool(_) => "storage.bigtable.error.pool",
169            BigTableError::PoolTimeout(_) => "storage.bigtable.error.pool_timeout",
170            BigTableError::GRPC(_) => "storage.bigtable.error.grpc",
171            BigTableError::Config(_) => "storage.bigtable.error.config",
172        };
173        Some(err)
174    }
175
176    fn tags(&self) -> Vec<(&str, String)> {
177        #[allow(clippy::match_like_matches_macro)]
178        match self {
179            BigTableError::PoolTimeout(tt) => vec![("type", format!("{tt:?}").to_lowercase())],
180            _ => vec![],
181        }
182    }
183
184    fn extras(&self) -> Vec<(&str, String)> {
185        match self {
186            BigTableError::InvalidRowResponse(s) => vec![("error", s.to_string())],
187            BigTableError::InvalidChunk(s) => vec![("error", s.to_string())],
188            BigTableError::GRPC(s) => vec![("error", s.to_string())],
189            BigTableError::Read(s) => vec![("error", s.to_string())],
190            BigTableError::Write(s) => vec![("error", s.to_string())],
191            BigTableError::Status(code, s) => {
192                vec![("code", code.to_string()), ("error", s.to_string())]
193            }
194            BigTableError::WriteTime(s) => vec![("error", s.to_string())],
195            BigTableError::Admin(s, raw) => {
196                let mut x = vec![("error", s.to_owned())];
197                if let Some(raw) = raw {
198                    x.push(("raw", raw.to_string()));
199                };
200                x
201            }
202            BigTableError::Pool(e) => vec![("error", e.to_string())],
203            _ => vec![],
204        }
205    }
206}