1use std::any::Any;
16
17use common_error::define_from_tonic_status;
18use common_error::ext::{BoxedError, ErrorExt, RetryHint};
19use common_error::status_code::StatusCode;
20use common_macro::stack_trace_debug;
21use snafu::{Location, Snafu, location};
22use tonic::Code;
23use tonic::metadata::errors::InvalidMetadataValue;
24
25#[derive(Snafu)]
26#[snafu(visibility(pub))]
27#[stack_trace_debug]
28pub enum Error {
29 #[snafu(display("Illegal Flight messages, reason: {}", reason))]
30 IllegalFlightMessages {
31 reason: String,
32 #[snafu(implicit)]
33 location: Location,
34 },
35
36 #[snafu(display("Failed to do Flight get, code: {}", tonic_code))]
37 FlightGet {
38 addr: String,
39 tonic_code: Code,
40 source: BoxedError,
41 },
42
43 #[snafu(display(
44 "Failed to receive Flight data from {}, code: {}: {}",
45 addr,
46 tonic_code,
47 message
48 ))]
49 FlightStream {
50 addr: String,
51 tonic_code: Code,
52 message: String,
53 source: BoxedError,
54 #[snafu(implicit)]
55 location: Location,
56 },
57
58 #[snafu(display("Failed to convert FlightData"))]
59 ConvertFlightData {
60 #[snafu(implicit)]
61 location: Location,
62 source: common_grpc::Error,
63 },
64
65 #[snafu(display("Illegal GRPC client state: {}", err_msg))]
66 IllegalGrpcClientState {
67 err_msg: String,
68 #[snafu(implicit)]
69 location: Location,
70 },
71
72 #[snafu(display("Missing required field in protobuf, field: {}", field))]
73 MissingField {
74 field: String,
75 #[snafu(implicit)]
76 location: Location,
77 },
78
79 #[snafu(display("Failed to create gRPC channel, peer address: {}", addr))]
80 CreateChannel {
81 addr: String,
82 #[snafu(implicit)]
83 location: Location,
84 source: common_grpc::error::Error,
85 },
86
87 #[snafu(display("Failed to create Tls channel manager"))]
88 CreateTlsChannel {
89 #[snafu(implicit)]
90 location: Location,
91 source: common_grpc::error::Error,
92 },
93
94 #[snafu(display("Failed to request RegionServer {}, code: {}", addr, code))]
95 RegionServer {
96 addr: String,
97 code: Code,
98 source: BoxedError,
99 #[snafu(implicit)]
100 location: Location,
101 },
102
103 #[snafu(display("Failed to request FlowServer {}, code: {}", addr, code))]
104 FlowServer {
105 addr: String,
106 code: Code,
107 source: BoxedError,
108 #[snafu(implicit)]
109 location: Location,
110 },
111
112 #[snafu(display("{}", msg))]
114 Server {
115 code: StatusCode,
116 msg: String,
117 #[snafu(implicit)]
118 location: Location,
119 },
120
121 #[snafu(display("Illegal Database response: {err_msg}"))]
122 IllegalDatabaseResponse {
123 err_msg: String,
124 #[snafu(implicit)]
125 location: Location,
126 },
127
128 #[snafu(display("Invalid Tonic metadata value"))]
129 InvalidTonicMetadataValue {
130 #[snafu(source)]
131 error: InvalidMetadataValue,
132 #[snafu(implicit)]
133 location: Location,
134 },
135
136 #[snafu(display("Failed to convert Schema"))]
137 ConvertSchema {
138 #[snafu(implicit)]
139 location: Location,
140 source: datatypes::error::Error,
141 },
142
143 #[snafu(display("{}", msg))]
144 Tonic {
145 code: StatusCode,
146 msg: String,
147 tonic_code: Code,
148 retry_hint: RetryHint,
149 #[snafu(implicit)]
150 location: Location,
151 },
152
153 #[snafu(display("External error"))]
154 External {
155 #[snafu(implicit)]
156 location: Location,
157 source: BoxedError,
158 },
159}
160
161pub type Result<T> = std::result::Result<T, Error>;
162
163impl ErrorExt for Error {
164 fn status_code(&self) -> StatusCode {
165 match self {
166 Error::IllegalFlightMessages { .. }
167 | Error::MissingField { .. }
168 | Error::IllegalDatabaseResponse { .. } => StatusCode::Internal,
169
170 Error::Server { code, .. } | Error::Tonic { code, .. } => *code,
171 Error::FlightGet { source, .. }
172 | Error::FlightStream { source, .. }
173 | Error::RegionServer { source, .. }
174 | Error::FlowServer { source, .. } => source.status_code(),
175 Error::CreateChannel { source, .. }
176 | Error::ConvertFlightData { source, .. }
177 | Error::CreateTlsChannel { source, .. } => source.status_code(),
178 Error::IllegalGrpcClientState { .. } => StatusCode::Unexpected,
179 Error::InvalidTonicMetadataValue { .. } => StatusCode::InvalidArguments,
180 Error::ConvertSchema { source, .. } => source.status_code(),
181 Error::External { source, .. } => source.status_code(),
182 }
183 }
184
185 fn as_any(&self) -> &dyn Any {
186 self
187 }
188
189 fn retry_hint(&self) -> RetryHint {
190 match self {
191 Error::Tonic { retry_hint, .. } => *retry_hint,
192 Error::FlightGet { source, .. }
193 | Error::FlightStream { source, .. }
194 | Error::FlowServer { source, .. }
195 | Error::External { source, .. } => source.retry_hint(),
196 Error::RegionServer { code, source, .. } => {
197 let hint = source.retry_hint();
198 if !hint.is_retryable()
200 && matches!(
201 code,
202 Code::Unavailable | Code::Cancelled | Code::DeadlineExceeded
203 )
204 {
205 RetryHint::Retryable
206 } else {
207 hint
208 }
209 }
210 Error::ConvertFlightData { source, .. }
211 | Error::CreateChannel { source, .. }
212 | Error::CreateTlsChannel { source, .. } => source.retry_hint(),
213 Error::ConvertSchema { source, .. } => source.retry_hint(),
214 _ => RetryHint::NonRetryable,
215 }
216 }
217}
218
219define_from_tonic_status!(Error, Tonic);
220
221impl From<Error> for common_meta::error::Error {
222 fn from(error: Error) -> Self {
223 Self::External {
224 source: BoxedError::new(error),
225 location: location!(),
226 }
227 }
228}
229
230impl Error {
231 pub fn tonic_code(&self) -> Option<Code> {
233 match self {
234 Self::FlightGet { tonic_code, .. }
235 | Self::FlightStream { tonic_code, .. }
236 | Self::RegionServer {
237 code: tonic_code, ..
238 }
239 | Self::FlowServer {
240 code: tonic_code, ..
241 }
242 | Self::Tonic { tonic_code, .. } => Some(*tonic_code),
243 _ => None,
244 }
245 }
246
247 pub fn is_connection_error(&self) -> bool {
249 matches!(self.tonic_code(), Some(Code::Unavailable))
250 }
251
252 pub fn should_retry(&self) -> bool {
253 self.retry_hint().is_retryable()
254 || self.is_connection_error()
255 || matches!(
256 self.tonic_code(),
257 Some(Code::Cancelled) | Some(Code::DeadlineExceeded)
258 )
259 }
260}