Skip to main content

relational_engine/
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;
16
17use common_error::ext::{ErrorExt, RetryHint};
18use common_error::status_code::StatusCode;
19use common_macro::stack_trace_debug;
20use object_store::error::retry_hint_from_opendal_error;
21use serde_json::error::Error as JsonError;
22use snafu::{Location, Snafu};
23use store_api::storage::RegionId;
24
25#[derive(Snafu)]
26#[snafu(visibility(pub))]
27#[stack_trace_debug]
28pub enum Error {
29    #[snafu(display("Unsupported operation: {}", operation))]
30    Unsupported {
31        operation: String,
32        #[snafu(implicit)]
33        location: Location,
34    },
35
36    #[snafu(display("Unexpected engine: {}", engine))]
37    UnexpectedEngine {
38        engine: String,
39        #[snafu(implicit)]
40        location: Location,
41    },
42
43    #[snafu(display("Invalid region metadata"))]
44    InvalidMetadata {
45        source: store_api::metadata::MetadataError,
46        #[snafu(implicit)]
47        location: Location,
48    },
49
50    #[snafu(display("Region not found, region_id: {}", region_id))]
51    RegionNotFound {
52        region_id: RegionId,
53        #[snafu(implicit)]
54        location: Location,
55    },
56
57    #[snafu(display(
58        "Region already exists with a different definition, region_id: {}, reason: {}",
59        region_id,
60        reason
61    ))]
62    RegionExists {
63        region_id: RegionId,
64        reason: String,
65        #[snafu(implicit)]
66        location: Location,
67    },
68
69    #[snafu(display("Failed to check object from path: {}", path))]
70    CheckObject {
71        path: String,
72        #[snafu(implicit)]
73        location: Location,
74        #[snafu(source)]
75        error: object_store::Error,
76    },
77
78    #[snafu(display("Fail to encode object into json"))]
79    EncodeJson {
80        #[snafu(implicit)]
81        location: Location,
82        #[snafu(source)]
83        error: JsonError,
84    },
85
86    #[snafu(display("Fail to decode object from json"))]
87    DecodeJson {
88        #[snafu(implicit)]
89        location: Location,
90        #[snafu(source)]
91        error: JsonError,
92    },
93
94    #[snafu(display("Failed to store region manifest, region_id: {}", region_id))]
95    StoreRegionManifest {
96        #[snafu(source)]
97        error: object_store::Error,
98        region_id: RegionId,
99        #[snafu(implicit)]
100        location: Location,
101    },
102
103    #[snafu(display("Failed to load region manifest, region_id: {}", region_id))]
104    LoadRegionManifest {
105        #[snafu(source)]
106        error: object_store::Error,
107        region_id: RegionId,
108        #[snafu(implicit)]
109        location: Location,
110    },
111
112    #[snafu(display("Failed to delete region manifest, region_id: {},", region_id))]
113    DeleteRegionManifest {
114        #[snafu(source)]
115        error: object_store::Error,
116        region_id: RegionId,
117        #[snafu(implicit)]
118        location: Location,
119    },
120
121    #[snafu(display("Manifest already exists: {}", path))]
122    ManifestExists {
123        path: String,
124        #[snafu(implicit)]
125        location: Location,
126    },
127
128    #[snafu(display("Missing required field: {}", name))]
129    MissingRequiredField {
130        name: String,
131        #[snafu(implicit)]
132        location: Location,
133    },
134}
135
136pub type Result<T> = std::result::Result<T, Error>;
137
138impl ErrorExt for Error {
139    fn status_code(&self) -> StatusCode {
140        use Error::*;
141
142        match self {
143            // Unlike file-engine, an unsupported operation (e.g. scan) must surface as
144            // `Unimplemented` (StatusCode::Unsupported) so the client gets a clear
145            // "not implemented" signal instead of a generic "invalid arguments".
146            Unsupported { .. } => StatusCode::Unsupported,
147
148            InvalidMetadata { .. } | MissingRequiredField { .. } => StatusCode::InvalidArguments,
149
150            RegionNotFound { .. } => StatusCode::RegionNotFound,
151
152            RegionExists { .. } => StatusCode::Unexpected,
153
154            CheckObject { .. }
155            | StoreRegionManifest { .. }
156            | LoadRegionManifest { .. }
157            | DeleteRegionManifest { .. } => StatusCode::StorageUnavailable,
158
159            EncodeJson { .. }
160            | DecodeJson { .. }
161            | ManifestExists { .. }
162            | UnexpectedEngine { .. } => StatusCode::Unexpected,
163        }
164    }
165
166    fn as_any(&self) -> &dyn Any {
167        self
168    }
169
170    fn retry_hint(&self) -> RetryHint {
171        use Error::*;
172
173        match self {
174            CheckObject { error, .. }
175            | StoreRegionManifest { error, .. }
176            | LoadRegionManifest { error, .. }
177            | DeleteRegionManifest { error, .. } => retry_hint_from_opendal_error(error),
178            _ => RetryHint::NonRetryable,
179        }
180    }
181}