Line data Source code
1 : use std::error::Error as StdError;
2 : use std::{fmt, io};
3 :
4 : use anyhow::Context;
5 : use measured::FixedCardinalityLabel;
6 : use tokio::task::JoinError;
7 :
8 : /// Upcast (almost) any error into an opaque [`io::Error`].
9 0 : pub(crate) fn io_error(e: impl Into<Box<dyn StdError + Send + Sync>>) -> io::Error {
10 0 : io::Error::new(io::ErrorKind::Other, e)
11 0 : }
12 :
13 : /// A small combinator for pluggable error logging.
14 0 : pub(crate) fn log_error<E: fmt::Display>(e: E) -> E {
15 0 : tracing::error!("{e}");
16 0 : e
17 0 : }
18 :
19 : /// Marks errors that may be safely shown to a client.
20 : /// This trait can be seen as a specialized version of [`ToString`].
21 : ///
22 : /// NOTE: This trait should not be implemented for [`anyhow::Error`], since it
23 : /// is way too convenient and tends to proliferate all across the codebase,
24 : /// ultimately leading to accidental leaks of sensitive data.
25 : pub(crate) trait UserFacingError: ReportableError {
26 : /// Format the error for client, stripping all sensitive info.
27 : ///
28 : /// Although this might be a no-op for many types, it's highly
29 : /// recommended to override the default impl in case error type
30 : /// contains anything sensitive: various IDs, IP addresses etc.
31 : #[inline(always)]
32 0 : fn to_string_client(&self) -> String {
33 0 : self.to_string()
34 0 : }
35 : }
36 :
37 0 : #[derive(Copy, Clone, Debug, Eq, PartialEq, FixedCardinalityLabel)]
38 : #[label(singleton = "type")]
39 : pub enum ErrorKind {
40 : /// Wrong password, unknown endpoint, protocol violation, etc...
41 : User,
42 :
43 : /// Network error between user and proxy. Not necessarily user error
44 : #[label(rename = "clientdisconnect")]
45 : ClientDisconnect,
46 :
47 : /// Proxy self-imposed user rate limits
48 : #[label(rename = "ratelimit")]
49 : RateLimit,
50 :
51 : /// Proxy self-imposed service-wise rate limits
52 : #[label(rename = "serviceratelimit")]
53 : ServiceRateLimit,
54 :
55 : /// Proxy quota limit violation
56 : #[label(rename = "quota")]
57 : Quota,
58 :
59 : /// internal errors
60 : Service,
61 :
62 : /// Error communicating with control plane
63 : #[label(rename = "controlplane")]
64 : ControlPlane,
65 :
66 : /// Postgres error
67 : Postgres,
68 :
69 : /// Error communicating with compute
70 : Compute,
71 : }
72 :
73 : impl ErrorKind {
74 0 : pub(crate) fn to_metric_label(self) -> &'static str {
75 0 : match self {
76 0 : ErrorKind::User => "user",
77 0 : ErrorKind::ClientDisconnect => "clientdisconnect",
78 0 : ErrorKind::RateLimit => "ratelimit",
79 0 : ErrorKind::ServiceRateLimit => "serviceratelimit",
80 0 : ErrorKind::Quota => "quota",
81 0 : ErrorKind::Service => "service",
82 0 : ErrorKind::ControlPlane => "controlplane",
83 0 : ErrorKind::Postgres => "postgres",
84 0 : ErrorKind::Compute => "compute",
85 : }
86 0 : }
87 : }
88 :
89 : pub(crate) trait ReportableError: fmt::Display + Send + 'static {
90 : fn get_error_kind(&self) -> ErrorKind;
91 : }
92 :
93 : impl ReportableError for tokio_postgres::error::Error {
94 0 : fn get_error_kind(&self) -> ErrorKind {
95 0 : if self.as_db_error().is_some() {
96 0 : ErrorKind::Postgres
97 : } else {
98 0 : ErrorKind::Compute
99 : }
100 0 : }
101 : }
102 :
103 : /// Flattens `Result<Result<T>>` into `Result<T>`.
104 0 : pub fn flatten_err<T>(r: Result<anyhow::Result<T>, JoinError>) -> anyhow::Result<T> {
105 0 : r.context("join error").and_then(|x| x)
106 0 : }
|