use std::fmt; use anyhow::Context; use measured::FixedCardinalityLabel; use tokio::task::JoinError; /// Marks errors that may be safely shown to a client. /// This trait can be seen as a specialized version of [`ToString`]. /// /// NOTE: This trait should not be implemented for [`anyhow::Error`], since it /// is way too convenient and tends to proliferate all across the codebase, /// ultimately leading to accidental leaks of sensitive data. pub(crate) trait UserFacingError: ReportableError { /// Format the error for client, stripping all sensitive info. /// /// Although this might be a no-op for many types, it's highly /// recommended to override the default impl in case error type /// contains anything sensitive: various IDs, IP addresses etc. #[inline(always)] fn to_string_client(&self) -> String { self.to_string() } } #[derive(Copy, Clone, Debug, Eq, PartialEq, FixedCardinalityLabel)] #[label(singleton = "type")] pub enum ErrorKind { /// Wrong password, unknown endpoint, protocol violation, etc... User, /// Network error between user and proxy. Not necessarily user error #[label(rename = "clientdisconnect")] ClientDisconnect, /// Proxy self-imposed user rate limits #[label(rename = "ratelimit")] RateLimit, /// Proxy self-imposed service-wise rate limits #[label(rename = "serviceratelimit")] ServiceRateLimit, /// Proxy quota limit violation #[label(rename = "quota")] Quota, /// internal errors Service, /// Error communicating with control plane #[label(rename = "controlplane")] ControlPlane, /// Postgres error Postgres, /// Error communicating with compute Compute, } impl ErrorKind { pub(crate) fn to_metric_label(self) -> &'static str { match self { ErrorKind::User => "user", ErrorKind::ClientDisconnect => "clientdisconnect", ErrorKind::RateLimit => "ratelimit", ErrorKind::ServiceRateLimit => "serviceratelimit", ErrorKind::Quota => "quota", ErrorKind::Service => "service", ErrorKind::ControlPlane => "controlplane", ErrorKind::Postgres => "postgres", ErrorKind::Compute => "compute", } } } pub(crate) trait ReportableError: fmt::Display + Send + 'static { fn get_error_kind(&self) -> ErrorKind; } /// Flattens `Result>` into `Result`. pub fn flatten_err(r: Result, JoinError>) -> anyhow::Result { r.context("join error").and_then(|x| x) }