blob: 5d9f2ac41e45a830a924394d4c4ad4ed121d1c12 [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
use std::fmt::Display;
use thiserror::Error as ThisError;
use crate::model::write::Response;
/// An error generated by the client.
#[derive(Debug, ThisError)]
pub enum Error {
/// Error from the running server
#[error("failed in server, err:{0}")]
Server(ServerError),
/// Error from the rpc
/// Note that any error caused by a running server wont be wrapped in the
/// grpc errors.
#[error("failed in grpc, err:{0}")]
Rpc(tonic::Status),
/// Error about rpc.
/// It will be throw while connection between client and server is broken
/// and try for reconnecting is failed(timeout).
#[error("failed to connect, addr:{addr:?}, err:{source:?}")]
Connect {
addr: String,
source: Box<dyn std::error::Error + Send + Sync>,
},
/// Error from the client and basically the rpc request has not been called
/// yet or the rpc request has already been finished successfully.
#[error("failed in client, msg:{0}")]
Client(String),
/// Error about authentication
#[error("failed to check auth, err:{0}")]
AuthFail(AuthFailStatus),
/// Error from write in route based mode, some of rows may be written
/// successfully, and others may fail.
#[error("failed to write with route based client, err:{0}")]
RouteBasedWriteError(RouteBasedWriteError),
/// Error unknown
#[error("unknown error, msg:{0}")]
Unknown(String),
#[error("failed to decode, msg:{0}")]
BuildRows(String),
#[error("failed to decode arrow payload, msg:{0}")]
DecodeArrowPayload(Box<dyn std::error::Error + Send + Sync>),
#[error("failed to find a database")]
NoDatabase,
#[error(transparent)]
Other {
#[from]
source: anyhow::Error,
},
}
#[derive(Debug)]
pub struct RouteBasedWriteError {
pub ok: (Vec<String>, Response), // (tables, write_response)
pub errors: Vec<(Vec<String>, Error)>, // [(tables, errors)]
}
impl From<Vec<(Vec<String>, Result<Response>)>> for RouteBasedWriteError {
fn from(write_results: Vec<(Vec<String>, Result<Response>)>) -> Self {
let mut success_total = 0;
let mut failed_total = 0;
let mut ok_tables = Vec::new();
let mut errors = Vec::new();
for (tables, write_result) in write_results {
match write_result {
Ok(write_resp) => {
success_total += write_resp.success;
failed_total += write_resp.failed;
ok_tables.extend(tables);
}
Err(e) => {
errors.push((tables, e));
}
}
}
Self {
ok: (ok_tables, Response::new(success_total, failed_total)),
errors,
}
}
}
impl RouteBasedWriteError {
pub fn all_ok(&self) -> bool {
self.errors.is_empty()
}
}
impl Display for RouteBasedWriteError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("RouteBasedWriteError")
.field("ok", &self.ok)
.field("errors", &self.errors)
.finish()
}
}
#[derive(Debug, Clone)]
pub struct ServerError {
pub code: u32,
pub msg: String,
}
impl Display for ServerError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ServerError")
.field("code", &self.code)
.field("msg", &self.msg)
.finish()
}
}
#[derive(Debug, Clone)]
pub struct AuthFailStatus {
pub code: AuthCode,
pub msg: String,
}
#[derive(Debug, Clone)]
pub enum AuthCode {
Ok = 0,
InvalidTenantMeta = 1,
InvalidTokenMeta = 2,
}
impl Display for AuthFailStatus {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("AuthFailStatus")
.field("code", &self.code)
.field("msg", &self.msg)
.finish()
}
}
/// A result holding the an [`Error`](Error).
pub type Result<T> = std::result::Result<T, Error>;
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_error_standardizing() {
let source_error = Box::new(Error::Unknown("unknown error".to_string()));
let connect_error = Error::Connect {
addr: "1.1.1.1:1111".to_string(),
source: source_error as _,
};
assert_eq!(
&format!("{connect_error}"),
r#"failed to connect, addr:"1.1.1.1:1111", err:Unknown("unknown error")"#
);
}
}