Skip to main content

flow/
error.rs

1// Copyright 2023 Greptime Team
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Error definition for flow module
16
17use std::any::Any;
18
19use api::v1::CreateTableExpr;
20use arrow_schema::ArrowError;
21use common_error::ext::BoxedError;
22use common_error::{define_into_tonic_status, from_err_code_msg_to_header};
23use common_macro::stack_trace_debug;
24use common_telemetry::common_error::ext::ErrorExt;
25use common_telemetry::common_error::status_code::StatusCode;
26use snafu::{Location, ResultExt, Snafu};
27use tonic::metadata::MetadataMap;
28
29use crate::FlowId;
30use crate::expr::EvalError;
31
32/// This error is used to represent all possible errors that can occur in the flow module.
33#[derive(Snafu)]
34#[snafu(visibility(pub))]
35#[stack_trace_debug]
36pub enum Error {
37    #[snafu(display(
38        "Failed to insert into flow: region_id={}, flow_ids={:?}",
39        region_id,
40        flow_ids
41    ))]
42    InsertIntoFlow {
43        region_id: u64,
44        flow_ids: Vec<u64>,
45        source: BoxedError,
46        #[snafu(implicit)]
47        location: Location,
48    },
49
50    #[snafu(display("Flow engine is still recovering"))]
51    FlowNotRecovered {
52        #[snafu(implicit)]
53        location: Location,
54    },
55
56    #[snafu(display("Error encountered while creating flow: {sql}"))]
57    CreateFlow {
58        sql: String,
59        source: BoxedError,
60        #[snafu(implicit)]
61        location: Location,
62    },
63
64    #[snafu(display("Error encountered while creating sink table for flow: {create:?}"))]
65    CreateSinkTable {
66        create: CreateTableExpr,
67        source: BoxedError,
68        #[snafu(implicit)]
69        location: Location,
70    },
71
72    #[snafu(display("Time error"))]
73    Time {
74        source: common_time::error::Error,
75        #[snafu(implicit)]
76        location: Location,
77    },
78
79    #[snafu(display("No available frontend found after timeout: {timeout:?}, context: {context}"))]
80    NoAvailableFrontend {
81        timeout: std::time::Duration,
82        context: String,
83        #[snafu(implicit)]
84        location: Location,
85    },
86
87    #[snafu(display("External error"))]
88    External {
89        source: BoxedError,
90        #[snafu(implicit)]
91        location: Location,
92    },
93
94    #[snafu(display("Internal error"))]
95    Internal {
96        reason: String,
97        #[snafu(implicit)]
98        location: Location,
99    },
100
101    /// TODO(discord9): add detailed location of column
102    #[snafu(display("Failed to eval stream"))]
103    Eval {
104        source: EvalError,
105        #[snafu(implicit)]
106        location: Location,
107    },
108
109    #[snafu(display("Table not found: {name}"))]
110    TableNotFound {
111        name: String,
112        #[snafu(implicit)]
113        location: Location,
114    },
115
116    #[snafu(display("Table not found: {msg}, meta error: {source}"))]
117    TableNotFoundMeta {
118        source: common_meta::error::Error,
119        msg: String,
120        #[snafu(implicit)]
121        location: Location,
122    },
123
124    #[snafu(display("Flow not found, id={id}"))]
125    FlowNotFound {
126        id: FlowId,
127        #[snafu(implicit)]
128        location: Location,
129    },
130
131    #[snafu(display("Failed to list flows in flownode={id:?}"))]
132    ListFlows {
133        id: Option<common_meta::FlownodeId>,
134        source: common_meta::error::Error,
135        #[snafu(implicit)]
136        location: Location,
137    },
138
139    #[snafu(display("Flow already exist, id={id}"))]
140    FlowAlreadyExist {
141        id: FlowId,
142        #[snafu(implicit)]
143        location: Location,
144    },
145
146    #[snafu(display("Failed to join task"))]
147    JoinTask {
148        #[snafu(source)]
149        error: tokio::task::JoinError,
150        #[snafu(implicit)]
151        location: Location,
152    },
153
154    #[snafu(display("Invalid query: {reason}"))]
155    InvalidQuery {
156        reason: String,
157        #[snafu(implicit)]
158        location: Location,
159    },
160
161    #[snafu(display("Not implement in flow: {reason}"))]
162    NotImplemented {
163        reason: String,
164        #[snafu(implicit)]
165        location: Location,
166    },
167
168    #[snafu(display("Flow plan error: {reason}"))]
169    Plan {
170        reason: String,
171        #[snafu(implicit)]
172        location: Location,
173    },
174
175    #[snafu(display("Unsupported: {reason}"))]
176    Unsupported {
177        reason: String,
178        #[snafu(implicit)]
179        location: Location,
180    },
181
182    #[snafu(display("Unsupported temporal filter: {reason}"))]
183    UnsupportedTemporalFilter {
184        reason: String,
185        #[snafu(implicit)]
186        location: Location,
187    },
188
189    #[snafu(display("Datatypes error: {source} with extra message: {extra}"))]
190    Datatypes {
191        source: datatypes::Error,
192        extra: String,
193        #[snafu(implicit)]
194        location: Location,
195    },
196
197    #[snafu(display("Arrow error: {raw:?} in context: {context}"))]
198    Arrow {
199        #[snafu(source)]
200        raw: ArrowError,
201        context: String,
202        #[snafu(implicit)]
203        location: Location,
204    },
205
206    #[snafu(display("Datafusion error: {raw:?} in context: {context}"))]
207    Datafusion {
208        #[snafu(source)]
209        raw: datafusion_common::DataFusionError,
210        context: String,
211        #[snafu(implicit)]
212        location: Location,
213    },
214
215    #[snafu(display("Unexpected: {reason}"))]
216    Unexpected {
217        reason: String,
218        #[snafu(implicit)]
219        location: Location,
220    },
221
222    #[snafu(display("Illegal check task state: {reason}"))]
223    IllegalCheckTaskState {
224        reason: String,
225        #[snafu(implicit)]
226        location: Location,
227    },
228
229    #[snafu(display(
230        "Failed to sync with check task for flow {} with allow_drop={}",
231        flow_id,
232        allow_drop
233    ))]
234    SyncCheckTask {
235        flow_id: FlowId,
236        allow_drop: bool,
237        #[snafu(implicit)]
238        location: Location,
239    },
240
241    #[snafu(display("Failed to start server"))]
242    StartServer {
243        #[snafu(implicit)]
244        location: Location,
245        source: servers::error::Error,
246    },
247
248    #[snafu(display("Failed to shutdown server"))]
249    ShutdownServer {
250        #[snafu(implicit)]
251        location: Location,
252        source: servers::error::Error,
253    },
254
255    #[snafu(display("Failed to initialize meta client"))]
256    MetaClientInit {
257        #[snafu(implicit)]
258        location: Location,
259        source: meta_client::error::Error,
260    },
261
262    #[snafu(display("Failed to parse address {}", addr))]
263    ParseAddr {
264        addr: String,
265        #[snafu(source)]
266        error: std::net::AddrParseError,
267    },
268
269    #[snafu(display("Failed to get cache from cache registry: {}", name))]
270    CacheRequired {
271        #[snafu(implicit)]
272        location: Location,
273        name: String,
274    },
275
276    #[snafu(display("Invalid request: {context}"))]
277    InvalidRequest {
278        context: String,
279        source: client::Error,
280        #[snafu(implicit)]
281        location: Location,
282    },
283
284    #[snafu(display("Failed to encode logical plan in substrait"))]
285    SubstraitEncodeLogicalPlan {
286        #[snafu(implicit)]
287        location: Location,
288        source: substrait::error::Error,
289    },
290
291    #[snafu(display("Failed to convert column schema to proto column def"))]
292    ConvertColumnSchema {
293        #[snafu(implicit)]
294        location: Location,
295        source: operator::error::Error,
296    },
297
298    #[snafu(display("Failed to create channel manager for gRPC client"))]
299    InvalidClientConfig {
300        #[snafu(implicit)]
301        location: Location,
302        source: common_grpc::error::Error,
303    },
304}
305
306/// the outer message is the full error stack, and inner message in header is the last error message that can be show directly to user
307pub fn to_status_with_last_err(err: impl ErrorExt) -> tonic::Status {
308    let msg = err.to_string();
309    let last_err_msg = common_error::ext::StackError::last(&err).to_string();
310    let code = err.status_code() as u32;
311    let header = from_err_code_msg_to_header(code, &last_err_msg);
312
313    tonic::Status::with_metadata(
314        tonic::Code::InvalidArgument,
315        msg,
316        MetadataMap::from_headers(header),
317    )
318}
319
320/// Result type for flow module
321pub type Result<T> = std::result::Result<T, Error>;
322
323impl ErrorExt for Error {
324    fn status_code(&self) -> StatusCode {
325        match self {
326            Self::Eval { .. }
327            | Self::JoinTask { .. }
328            | Self::Datafusion { .. }
329            | Self::InsertIntoFlow { .. }
330            | Self::NoAvailableFrontend { .. }
331            | Self::FlowNotRecovered { .. } => StatusCode::Internal,
332            Self::FlowAlreadyExist { .. } => StatusCode::TableAlreadyExists,
333            Self::TableNotFound { .. }
334            | Self::TableNotFoundMeta { .. }
335            | Self::ListFlows { .. } => StatusCode::TableNotFound,
336            Self::FlowNotFound { .. } => StatusCode::FlowNotFound,
337            Self::Plan { .. } | Self::Datatypes { .. } => StatusCode::PlanQuery,
338            Self::CreateFlow { .. }
339            | Self::CreateSinkTable { .. }
340            | Self::Arrow { .. }
341            | Self::Time { .. } => StatusCode::EngineExecuteQuery,
342            Self::Unexpected { .. }
343            | Self::SyncCheckTask { .. }
344            | Self::IllegalCheckTaskState { .. } => StatusCode::Unexpected,
345            Self::NotImplemented { .. }
346            | Self::UnsupportedTemporalFilter { .. }
347            | Self::Unsupported { .. } => StatusCode::Unsupported,
348            Self::External { source, .. } => source.status_code(),
349            Self::Internal { .. } | Self::CacheRequired { .. } => StatusCode::Internal,
350            Self::StartServer { source, .. } | Self::ShutdownServer { source, .. } => {
351                source.status_code()
352            }
353            Self::MetaClientInit { source, .. } => source.status_code(),
354
355            Self::InvalidQuery { .. }
356            | Self::InvalidRequest { .. }
357            | Self::ParseAddr { .. }
358            | Self::InvalidClientConfig { .. } => StatusCode::InvalidArguments,
359
360            Error::SubstraitEncodeLogicalPlan { source, .. } => source.status_code(),
361
362            Error::ConvertColumnSchema { source, .. } => source.status_code(),
363        }
364    }
365
366    fn as_any(&self) -> &dyn Any {
367        self
368    }
369}
370
371define_into_tonic_status!(Error);
372
373impl From<EvalError> for Error {
374    fn from(e: EvalError) -> Self {
375        Err::<(), _>(e).context(EvalSnafu).unwrap_err()
376    }
377}