Skip to main content

file_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 datafusion::arrow::error::ArrowError;
21use datafusion::error::DataFusionError;
22use object_store::error::retry_hint_from_opendal_error;
23use serde_json::error::Error as JsonError;
24use snafu::{Location, Snafu};
25use store_api::storage::RegionId;
26
27#[derive(Snafu)]
28#[snafu(visibility(pub))]
29#[stack_trace_debug]
30pub enum Error {
31    #[snafu(display("Unsupported operation: {}", operation))]
32    Unsupported {
33        operation: String,
34        #[snafu(implicit)]
35        location: Location,
36    },
37
38    #[snafu(display("Unexpected engine: {}", engine))]
39    UnexpectedEngine {
40        engine: String,
41        #[snafu(implicit)]
42        location: Location,
43    },
44
45    #[snafu(display("Invalid region metadata"))]
46    InvalidMetadata {
47        source: store_api::metadata::MetadataError,
48        #[snafu(implicit)]
49        location: Location,
50    },
51
52    #[snafu(display("Region not found, region_id: {}", region_id))]
53    RegionNotFound {
54        region_id: RegionId,
55        #[snafu(implicit)]
56        location: Location,
57    },
58
59    #[snafu(display("Failed to check object from path: {}", path))]
60    CheckObject {
61        path: String,
62        #[snafu(implicit)]
63        location: Location,
64        #[snafu(source)]
65        error: object_store::Error,
66    },
67
68    #[snafu(display("Fail to encode object into json"))]
69    EncodeJson {
70        #[snafu(implicit)]
71        location: Location,
72        #[snafu(source)]
73        error: JsonError,
74    },
75
76    #[snafu(display("Fail to decode object from json"))]
77    DecodeJson {
78        #[snafu(implicit)]
79        location: Location,
80        #[snafu(source)]
81        error: JsonError,
82    },
83
84    #[snafu(display("Failed to store region manifest, region_id: {}", region_id))]
85    StoreRegionManifest {
86        #[snafu(source)]
87        error: object_store::Error,
88        region_id: RegionId,
89        #[snafu(implicit)]
90        location: Location,
91    },
92
93    #[snafu(display("Failed to load region manifest, region_id: {}", region_id))]
94    LoadRegionManifest {
95        #[snafu(source)]
96        error: object_store::Error,
97        region_id: RegionId,
98        #[snafu(implicit)]
99        location: Location,
100    },
101
102    #[snafu(display("Failed to delete region manifest, region_id: {},", region_id))]
103    DeleteRegionManifest {
104        #[snafu(source)]
105        error: object_store::Error,
106        region_id: RegionId,
107        #[snafu(implicit)]
108        location: Location,
109    },
110
111    #[snafu(display("Manifest already exists: {}", path))]
112    ManifestExists {
113        path: String,
114        #[snafu(implicit)]
115        location: Location,
116    },
117
118    #[snafu(display("Missing required field: {}", name))]
119    MissingRequiredField {
120        name: String,
121        #[snafu(implicit)]
122        location: Location,
123    },
124
125    #[snafu(display("Failed to build backend"))]
126    BuildBackend {
127        #[snafu(implicit)]
128        location: Location,
129        source: common_datasource::error::Error,
130    },
131
132    #[snafu(display("Failed to build stream"))]
133    BuildStream {
134        #[snafu(source)]
135        error: DataFusionError,
136        #[snafu(implicit)]
137        location: Location,
138    },
139
140    #[snafu(display("Failed to project schema"))]
141    ProjectArrowSchema {
142        #[snafu(source)]
143        error: ArrowError,
144        #[snafu(implicit)]
145        location: Location,
146    },
147
148    #[snafu(display("Failed to project schema"))]
149    ProjectSchema {
150        source: datatypes::error::Error,
151        #[snafu(implicit)]
152        location: Location,
153    },
154
155    #[snafu(display("Failed to parse file format"))]
156    ParseFileFormat {
157        #[snafu(implicit)]
158        location: Location,
159        source: common_datasource::error::Error,
160    },
161
162    #[snafu(display("Failed to generate parquet scan plan"))]
163    ParquetScanPlan {
164        #[snafu(source)]
165        error: DataFusionError,
166        #[snafu(implicit)]
167        location: Location,
168    },
169
170    #[snafu(display(
171        "Projection out of bounds, column_index: {}, bounds: {}",
172        column_index,
173        bounds
174    ))]
175    ProjectionOutOfBounds {
176        column_index: usize,
177        bounds: usize,
178        #[snafu(implicit)]
179        location: Location,
180    },
181
182    #[snafu(transparent)]
183    DataFusion {
184        #[snafu(source)]
185        error: DataFusionError,
186        #[snafu(implicit)]
187        location: Location,
188    },
189
190    #[snafu(display("Failed to create default value for column: {}", column))]
191    CreateDefault {
192        column: String,
193        source: datatypes::error::Error,
194        #[snafu(implicit)]
195        location: Location,
196    },
197}
198
199pub type Result<T> = std::result::Result<T, Error>;
200
201impl ErrorExt for Error {
202    fn status_code(&self) -> StatusCode {
203        use Error::*;
204
205        match self {
206            ProjectArrowSchema { .. }
207            | ProjectSchema { .. }
208            | MissingRequiredField { .. }
209            | Unsupported { .. }
210            | InvalidMetadata { .. }
211            | ProjectionOutOfBounds { .. }
212            | CreateDefault { .. } => StatusCode::InvalidArguments,
213
214            RegionNotFound { .. } => StatusCode::RegionNotFound,
215
216            BuildBackend { source, .. } => source.status_code(),
217            ParseFileFormat { source, .. } => source.status_code(),
218
219            CheckObject { .. }
220            | StoreRegionManifest { .. }
221            | LoadRegionManifest { .. }
222            | DeleteRegionManifest { .. } => StatusCode::StorageUnavailable,
223
224            EncodeJson { .. }
225            | DecodeJson { .. }
226            | ManifestExists { .. }
227            | BuildStream { .. }
228            | ParquetScanPlan { .. }
229            | UnexpectedEngine { .. } => StatusCode::Unexpected,
230
231            DataFusion { .. } => StatusCode::Internal,
232        }
233    }
234
235    fn as_any(&self) -> &dyn Any {
236        self
237    }
238
239    fn retry_hint(&self) -> RetryHint {
240        use Error::*;
241
242        match self {
243            BuildBackend { source, .. } | ParseFileFormat { source, .. } => source.retry_hint(),
244            CheckObject { error, .. }
245            | StoreRegionManifest { error, .. }
246            | LoadRegionManifest { error, .. }
247            | DeleteRegionManifest { error, .. } => retry_hint_from_opendal_error(error),
248            _ => RetryHint::NonRetryable,
249        }
250    }
251}