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