Skip to main content

datanode/
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
15use std::any::Any;
16use std::sync::Arc;
17
18use common_error::define_into_tonic_status;
19use common_error::ext::{BoxedError, ErrorExt, RetryHint};
20use common_error::status_code::StatusCode;
21use common_macro::stack_trace_debug;
22use common_runtime::JoinError;
23use snafu::{Location, Snafu};
24use store_api::storage::RegionId;
25use table::error::Error as TableError;
26use tokio::time::error::Elapsed;
27
28/// Business error of datanode.
29#[derive(Snafu)]
30#[snafu(visibility(pub))]
31#[stack_trace_debug]
32pub enum Error {
33    #[snafu(display("Failed to write a batch group"))]
34    BatchWrite { source: std::sync::Arc<Error> },
35
36    #[snafu(display("Failed to execute async task"))]
37    AsyncTaskExecute {
38        #[snafu(implicit)]
39        location: Location,
40        source: Arc<Error>,
41    },
42
43    #[snafu(display("Failed to watch change"))]
44    WatchAsyncTaskChange {
45        #[snafu(implicit)]
46        location: Location,
47        #[snafu(source)]
48        error: tokio::sync::watch::error::RecvError,
49    },
50
51    #[snafu(display("Failed to handle heartbeat response"))]
52    HandleHeartbeatResponse {
53        #[snafu(implicit)]
54        location: Location,
55        source: common_meta::error::Error,
56    },
57
58    #[snafu(display("Failed to get info from meta server"))]
59    GetMetadata {
60        #[snafu(implicit)]
61        location: Location,
62        source: common_meta::error::Error,
63    },
64
65    #[snafu(display("Failed to execute logical plan"))]
66    ExecuteLogicalPlan {
67        #[snafu(implicit)]
68        location: Location,
69        source: query::error::Error,
70    },
71
72    #[snafu(display("Failed to join datanode runtime task, request_type: {}", request_type))]
73    RuntimeJoin {
74        request_type: &'static str,
75        #[snafu(source)]
76        error: JoinError,
77        #[snafu(implicit)]
78        location: Location,
79    },
80
81    #[snafu(display("Failed to create plan decoder"))]
82    NewPlanDecoder {
83        #[snafu(implicit)]
84        location: Location,
85        source: query::error::Error,
86    },
87
88    #[snafu(display("Failed to decode logical plan"))]
89    DecodeLogicalPlan {
90        #[snafu(implicit)]
91        location: Location,
92        source: common_query::error::Error,
93    },
94
95    #[snafu(display("Schema not found: {}", name))]
96    SchemaNotFound {
97        name: String,
98        #[snafu(implicit)]
99        location: Location,
100    },
101
102    #[snafu(display("Missing timestamp column in request"))]
103    MissingTimestampColumn {
104        #[snafu(implicit)]
105        location: Location,
106    },
107
108    #[snafu(display("Failed to delete value from table: {}", table_name))]
109    Delete {
110        table_name: String,
111        #[snafu(implicit)]
112        location: Location,
113        source: TableError,
114    },
115
116    #[snafu(display("Failed to start server"))]
117    StartServer {
118        #[snafu(implicit)]
119        location: Location,
120        source: servers::error::Error,
121    },
122
123    #[snafu(display("Failed to parse address {}", addr))]
124    ParseAddr {
125        addr: String,
126        #[snafu(source)]
127        error: std::net::AddrParseError,
128    },
129
130    #[snafu(display("Failed to create directory {}", dir))]
131    CreateDir {
132        dir: String,
133        #[snafu(source)]
134        error: std::io::Error,
135    },
136
137    #[snafu(display("Failed to remove directory {}", dir))]
138    RemoveDir {
139        dir: String,
140        #[snafu(source)]
141        error: std::io::Error,
142    },
143
144    #[snafu(display("Failed to open log store"))]
145    OpenLogStore {
146        #[snafu(implicit)]
147        location: Location,
148        source: Box<log_store::error::Error>,
149    },
150
151    #[snafu(display("Invalid query context: {}", reason))]
152    InvalidQueryContext {
153        reason: String,
154        #[snafu(implicit)]
155        location: Location,
156    },
157
158    #[snafu(display("Invalid SQL, error: {}", msg))]
159    InvalidSql { msg: String },
160
161    #[snafu(display("Illegal primary keys definition: {}", msg))]
162    IllegalPrimaryKeysDef {
163        msg: String,
164        #[snafu(implicit)]
165        location: Location,
166    },
167
168    #[snafu(display("Schema {} already exists", name))]
169    SchemaExists {
170        name: String,
171        #[snafu(implicit)]
172        location: Location,
173    },
174
175    #[snafu(display("Failed to initialize meta client"))]
176    MetaClientInit {
177        #[snafu(implicit)]
178        location: Location,
179        source: meta_client::error::Error,
180    },
181
182    #[snafu(display("Missing node id in Datanode config"))]
183    MissingNodeId {
184        #[snafu(implicit)]
185        location: Location,
186    },
187
188    #[snafu(display("Failed to build datanode"))]
189    BuildDatanode {
190        #[snafu(implicit)]
191        location: Location,
192        source: BoxedError,
193    },
194
195    #[snafu(display("Failed to build http client"))]
196    BuildHttpClient {
197        #[snafu(implicit)]
198        location: Location,
199        #[snafu(source)]
200        error: reqwest::Error,
201    },
202
203    #[snafu(display("Missing required field: {}", name))]
204    MissingRequiredField {
205        name: String,
206        #[snafu(implicit)]
207        location: Location,
208    },
209
210    #[snafu(display(
211        "No valid default value can be built automatically, column: {}",
212        column,
213    ))]
214    ColumnNoneDefaultValue {
215        column: String,
216        #[snafu(implicit)]
217        location: Location,
218    },
219
220    #[snafu(display("Failed to shutdown server"))]
221    ShutdownServer {
222        #[snafu(implicit)]
223        location: Location,
224        #[snafu(source)]
225        source: servers::error::Error,
226    },
227
228    #[snafu(display("Failed to shutdown instance"))]
229    ShutdownInstance {
230        #[snafu(implicit)]
231        location: Location,
232        #[snafu(source)]
233        source: BoxedError,
234    },
235
236    #[snafu(display("Payload not exist"))]
237    PayloadNotExist {
238        #[snafu(implicit)]
239        location: Location,
240    },
241
242    #[snafu(display("Unexpected, violated: {}", violated))]
243    Unexpected {
244        violated: String,
245        #[snafu(implicit)]
246        location: Location,
247    },
248
249    #[snafu(display("Failed to handle request for region {}", region_id))]
250    HandleRegionRequest {
251        region_id: RegionId,
252        #[snafu(implicit)]
253        location: Location,
254        source: BoxedError,
255    },
256
257    #[snafu(display("Failed to open batch regions"))]
258    HandleBatchOpenRequest {
259        #[snafu(implicit)]
260        location: Location,
261        source: BoxedError,
262    },
263
264    #[snafu(display("Failed to handle batch ddl request, ddl_type: {}", ddl_type))]
265    HandleBatchDdlRequest {
266        #[snafu(implicit)]
267        location: Location,
268        source: BoxedError,
269        ddl_type: String,
270    },
271
272    #[snafu(display("RegionId {} not found", region_id))]
273    RegionNotFound {
274        region_id: RegionId,
275        #[snafu(implicit)]
276        location: Location,
277    },
278
279    #[snafu(display("Region {} not ready", region_id))]
280    RegionNotReady {
281        region_id: RegionId,
282        #[snafu(implicit)]
283        location: Location,
284    },
285
286    #[snafu(display("Region {} is busy", region_id))]
287    RegionBusy {
288        region_id: RegionId,
289        #[snafu(implicit)]
290        location: Location,
291    },
292
293    #[snafu(display("Region engine {} is not registered", name))]
294    RegionEngineNotFound {
295        name: String,
296        #[snafu(implicit)]
297        location: Location,
298    },
299
300    #[snafu(display(
301        "GC configuration mismatch: metasrv.gc.enable={}, datanode.region_engine.mito.gc.enable={}",
302        metasrv_gc_enabled,
303        datanode_gc_enabled,
304    ))]
305    GcConfigMismatch {
306        metasrv_gc_enabled: bool,
307        datanode_gc_enabled: bool,
308        #[snafu(implicit)]
309        location: Location,
310    },
311
312    #[snafu(display(
313        "Duplicate region engine config '{engine}' at region_engine[{first_index}] and region_engine[{duplicate_index}] (indices are zero-based); each engine type may be configured only once"
314    ))]
315    DuplicateRegionEngineConfig {
316        engine: &'static str,
317        first_index: usize,
318        duplicate_index: usize,
319        #[snafu(implicit)]
320        location: Location,
321    },
322
323    #[snafu(display("Invalid object store WAL config, {} = {:?}: {}", field, value, reason))]
324    InvalidObjectStoreWalConfig {
325        field: &'static str,
326        value: String,
327        reason: String,
328        #[snafu(implicit)]
329        location: Location,
330    },
331
332    #[snafu(display("Object store WAL is only supported in standalone mode"))]
333    ObjectStoreWalNotStandalone {
334        #[snafu(implicit)]
335        location: Location,
336    },
337
338    #[snafu(display("Unsupported output type, expected: {}", expected))]
339    UnsupportedOutput {
340        expected: String,
341        #[snafu(implicit)]
342        location: Location,
343    },
344
345    #[snafu(display("Failed to build region requests"))]
346    BuildRegionRequests {
347        #[snafu(implicit)]
348        location: Location,
349        source: store_api::metadata::MetadataError,
350    },
351
352    #[snafu(display("Failed to serialize WAL options for region {}", region_id))]
353    SerializeWalOptions {
354        region_id: RegionId,
355        #[snafu(source)]
356        error: serde_json::Error,
357        #[snafu(implicit)]
358        location: Location,
359    },
360
361    #[snafu(display("Failed to stop region engine {}", name))]
362    StopRegionEngine {
363        name: String,
364        #[snafu(implicit)]
365        location: Location,
366        source: BoxedError,
367    },
368
369    #[snafu(display(
370        "Failed to find logical regions in physical region {}",
371        physical_region_id
372    ))]
373    FindLogicalRegions {
374        physical_region_id: RegionId,
375        source: metric_engine::error::Error,
376        #[snafu(implicit)]
377        location: Location,
378    },
379
380    #[snafu(display("Failed to build mito engine"))]
381    BuildMitoEngine {
382        source: mito2::error::Error,
383        #[snafu(implicit)]
384        location: Location,
385    },
386
387    #[snafu(display("Failed to build metric engine"))]
388    BuildMetricEngine {
389        source: metric_engine::error::Error,
390        #[snafu(implicit)]
391        location: Location,
392    },
393
394    #[snafu(display("Failed to run gc for region {}", region_id))]
395    GcMitoEngine {
396        region_id: RegionId,
397        source: mito2::error::Error,
398        #[snafu(implicit)]
399        location: Location,
400    },
401
402    #[snafu(display("Failed to list SST entries from storage"))]
403    ListStorageSsts {
404        #[snafu(implicit)]
405        location: Location,
406        source: mito2::error::Error,
407    },
408
409    #[snafu(display("Failed to serialize options to TOML"))]
410    TomlFormat {
411        #[snafu(implicit)]
412        location: Location,
413        #[snafu(source(from(common_config::error::Error, Box::new)))]
414        source: Box<common_config::error::Error>,
415    },
416
417    #[snafu(display(
418        "Failed to get region metadata from engine {} for region_id {}",
419        engine,
420        region_id,
421    ))]
422    GetRegionMetadata {
423        engine: String,
424        region_id: RegionId,
425        #[snafu(implicit)]
426        location: Location,
427        source: BoxedError,
428    },
429
430    #[snafu(display("DataFusion"))]
431    DataFusion {
432        #[snafu(source)]
433        error: datafusion::error::DataFusionError,
434        #[snafu(implicit)]
435        location: Location,
436    },
437
438    #[snafu(display("Failed to acquire permit, source closed"))]
439    ConcurrentQueryLimiterClosed {
440        #[snafu(source)]
441        error: tokio::sync::AcquireError,
442        #[snafu(implicit)]
443        location: Location,
444    },
445
446    #[snafu(display("Failed to acquire permit under timeouts"))]
447    ConcurrentQueryLimiterTimeout {
448        #[snafu(source)]
449        error: Elapsed,
450        #[snafu(implicit)]
451        location: Location,
452    },
453
454    #[snafu(display("Cache not found in registry"))]
455    MissingCache {
456        #[snafu(implicit)]
457        location: Location,
458    },
459
460    #[snafu(display("Failed to serialize json"))]
461    SerializeJson {
462        #[snafu(source)]
463        error: serde_json::Error,
464        #[snafu(implicit)]
465        location: Location,
466    },
467
468    #[snafu(display("Failed object store operation"))]
469    ObjectStore {
470        source: object_store::error::Error,
471        #[snafu(implicit)]
472        location: Location,
473    },
474
475    #[snafu(display("Not yet implemented: {what}"))]
476    NotYetImplemented { what: String },
477}
478
479pub type Result<T> = std::result::Result<T, Error>;
480
481impl ErrorExt for Error {
482    fn status_code(&self) -> StatusCode {
483        use Error::*;
484        match self {
485            Self::BatchWrite { source } => source.status_code(),
486            NewPlanDecoder { source, .. } | ExecuteLogicalPlan { source, .. } => {
487                source.status_code()
488            }
489
490            BuildRegionRequests { source, .. } => source.status_code(),
491            HandleHeartbeatResponse { source, .. } | GetMetadata { source, .. } => {
492                source.status_code()
493            }
494
495            DecodeLogicalPlan { source, .. } => source.status_code(),
496
497            Delete { source, .. } => source.status_code(),
498
499            InvalidQueryContext { .. }
500            | InvalidSql { .. }
501            | IllegalPrimaryKeysDef { .. }
502            | MissingTimestampColumn { .. }
503            | SchemaNotFound { .. }
504            | SchemaExists { .. }
505            | MissingNodeId { .. }
506            | ColumnNoneDefaultValue { .. }
507            | MissingRequiredField { .. }
508            | RegionEngineNotFound { .. }
509            | GcConfigMismatch { .. }
510            | ParseAddr { .. }
511            | TomlFormat { .. }
512            | DuplicateRegionEngineConfig { .. }
513            | InvalidObjectStoreWalConfig { .. }
514            | ObjectStoreWalNotStandalone { .. }
515            | BuildDatanode { .. } => StatusCode::InvalidArguments,
516
517            PayloadNotExist { .. }
518            | Unexpected { .. }
519            | SerializeWalOptions { .. }
520            | WatchAsyncTaskChange { .. }
521            | BuildHttpClient { .. } => StatusCode::Unexpected,
522
523            AsyncTaskExecute { source, .. } => source.status_code(),
524
525            CreateDir { .. }
526            | RemoveDir { .. }
527            | ShutdownInstance { .. }
528            | DataFusion { .. }
529            | RuntimeJoin { .. } => StatusCode::Internal,
530
531            RegionNotFound { .. } => StatusCode::RegionNotFound,
532            RegionNotReady { .. } => StatusCode::RegionNotReady,
533            RegionBusy { .. } => StatusCode::RegionBusy,
534
535            StartServer { source, .. } | ShutdownServer { source, .. } => source.status_code(),
536
537            OpenLogStore { source, .. } => source.status_code(),
538            MetaClientInit { source, .. } => source.status_code(),
539            UnsupportedOutput { .. } | NotYetImplemented { .. } => StatusCode::Unsupported,
540            HandleRegionRequest { source, .. }
541            | GetRegionMetadata { source, .. }
542            | HandleBatchOpenRequest { source, .. }
543            | HandleBatchDdlRequest { source, .. } => source.status_code(),
544            StopRegionEngine { source, .. } => source.status_code(),
545
546            FindLogicalRegions { source, .. } => source.status_code(),
547            BuildMitoEngine { source, .. } | GcMitoEngine { source, .. } => source.status_code(),
548            BuildMetricEngine { source, .. } => source.status_code(),
549            ListStorageSsts { source, .. } => source.status_code(),
550            ConcurrentQueryLimiterClosed { .. } | ConcurrentQueryLimiterTimeout { .. } => {
551                StatusCode::RegionBusy
552            }
553            MissingCache { .. } => StatusCode::Internal,
554            SerializeJson { .. } => StatusCode::Internal,
555
556            ObjectStore { source, .. } => source.status_code(),
557        }
558    }
559
560    fn as_any(&self) -> &dyn Any {
561        self
562    }
563
564    fn retry_hint(&self) -> RetryHint {
565        use Error::*;
566
567        match self {
568            Self::BatchWrite { source } => source.retry_hint(),
569            RegionNotReady { .. } | RegionNotFound { .. } => RetryHint::RetryAfterRouteRefresh,
570            RegionBusy { .. }
571            | ConcurrentQueryLimiterClosed { .. }
572            | ConcurrentQueryLimiterTimeout { .. } => RetryHint::Retryable,
573            NewPlanDecoder { source, .. } | ExecuteLogicalPlan { source, .. } => {
574                source.retry_hint()
575            }
576            HandleHeartbeatResponse { source, .. } | GetMetadata { source, .. } => {
577                source.retry_hint()
578            }
579            DecodeLogicalPlan { source, .. } => source.retry_hint(),
580            Delete { source, .. } => source.retry_hint(),
581            AsyncTaskExecute { source, .. } => source.retry_hint(),
582            StartServer { source, .. } | ShutdownServer { source, .. } => source.retry_hint(),
583            OpenLogStore { source, .. } => source.retry_hint(),
584            MetaClientInit { source, .. } => source.retry_hint(),
585            HandleRegionRequest { source, .. }
586            | GetRegionMetadata { source, .. }
587            | HandleBatchOpenRequest { source, .. }
588            | HandleBatchDdlRequest { source, .. }
589            | StopRegionEngine { source, .. } => source.retry_hint(),
590            FindLogicalRegions { source, .. } => source.retry_hint(),
591            BuildMitoEngine { source, .. } => source.retry_hint(),
592            GcMitoEngine { source, .. } => source.retry_hint(),
593            BuildMetricEngine { source, .. } => source.retry_hint(),
594            ListStorageSsts { source, .. } => source.retry_hint(),
595            ObjectStore { source, .. } => source.retry_hint(),
596            _ => RetryHint::NonRetryable,
597        }
598    }
599}
600
601define_into_tonic_status!(Error);
602
603#[cfg(test)]
604mod tests {
605    use common_error::ext::RetryHint;
606
607    use super::*;
608
609    #[test]
610    fn test_region_state_retry_hints() {
611        let region_id = RegionId::new(1024, 1);
612        for (error, status, hint) in [
613            (
614                RegionBusySnafu { region_id }.build(),
615                StatusCode::RegionBusy,
616                RetryHint::Retryable,
617            ),
618            (
619                RegionNotReadySnafu { region_id }.build(),
620                StatusCode::RegionNotReady,
621                RetryHint::RetryAfterRouteRefresh,
622            ),
623            (
624                RegionNotFoundSnafu { region_id }.build(),
625                StatusCode::RegionNotFound,
626                RetryHint::RetryAfterRouteRefresh,
627            ),
628        ] {
629            assert_eq!(error.status_code(), status);
630            assert_eq!(error.retry_hint(), hint);
631        }
632    }
633
634    #[test]
635    fn test_default_hint_is_non_retryable() {
636        let err = UnexpectedSnafu {
637            violated: "mock error",
638        }
639        .build();
640
641        assert_eq!(err.retry_hint(), RetryHint::NonRetryable);
642    }
643}