Skip to main content

cli/data/import_v2/
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, retry_hint_from_io_error};
18use common_error::status_code::StatusCode;
19use common_macro::stack_trace_debug;
20use snafu::{Location, Snafu};
21
22use crate::data::export_v2::manifest::ChunkStatus;
23
24#[derive(Snafu)]
25#[snafu(visibility(pub))]
26#[stack_trace_debug]
27pub enum Error {
28    #[snafu(display("Invalid packed snapshot: {reason}"))]
29    InvalidPackedSnapshot { reason: String },
30
31    #[snafu(display("Snapshot not found at '{}'", uri))]
32    SnapshotNotFound {
33        uri: String,
34        #[snafu(implicit)]
35        location: Location,
36    },
37
38    #[snafu(display("Schema '{}' not found in snapshot", schema))]
39    SchemaNotInSnapshot {
40        schema: String,
41        #[snafu(implicit)]
42        location: Location,
43    },
44
45    #[snafu(display("Incomplete snapshot: chunk {} has status {:?}", chunk_id, status))]
46    IncompleteSnapshot {
47        chunk_id: u32,
48        status: ChunkStatus,
49        #[snafu(implicit)]
50        location: Location,
51    },
52
53    #[snafu(display(
54        "Snapshot is inconsistent: chunk {} is marked completed but its file manifest is empty",
55        chunk_id
56    ))]
57    EmptyChunkManifest {
58        chunk_id: u32,
59        #[snafu(implicit)]
60        location: Location,
61    },
62
63    #[snafu(display(
64        "Snapshot is inconsistent: chunk {} for schema '{}' is marked completed but no files were found under '{}'",
65        chunk_id,
66        schema,
67        path
68    ))]
69    MissingChunkData {
70        chunk_id: u32,
71        schema: String,
72        path: String,
73        #[snafu(implicit)]
74        location: Location,
75    },
76
77    #[snafu(display("Chunk {} import failed for schema '{}'", chunk_id, schema))]
78    ChunkImportFailed {
79        chunk_id: u32,
80        schema: String,
81        #[snafu(source)]
82        error: crate::data::export_v2::error::Error,
83        #[snafu(implicit)]
84        location: Location,
85    },
86
87    #[snafu(display("Snapshot storage error"))]
88    SnapshotStorage {
89        #[snafu(source)]
90        error: crate::data::export_v2::error::Error,
91        #[snafu(implicit)]
92        location: Location,
93    },
94
95    #[snafu(display("Database error"))]
96    Database {
97        #[snafu(source)]
98        error: crate::error::Error,
99        #[snafu(implicit)]
100        location: Location,
101    },
102
103    #[snafu(display("Failed to parse import state file"))]
104    ImportStateParse {
105        #[snafu(source)]
106        error: serde_json::Error,
107        #[snafu(implicit)]
108        location: Location,
109    },
110
111    #[snafu(display("Import state I/O failed at '{}': {}", path, error))]
112    ImportStateIo {
113        path: String,
114        #[snafu(source)]
115        error: std::io::Error,
116        #[snafu(implicit)]
117        location: Location,
118    },
119
120    #[snafu(display("Import state is already locked at '{}'", path))]
121    ImportStateLocked {
122        path: String,
123        #[snafu(implicit)]
124        location: Location,
125    },
126
127    #[snafu(display(
128        "Failed to determine import state path for snapshot '{}'. Set HOME, USERPROFILE, or run from a valid current directory.",
129        snapshot_id
130    ))]
131    ImportStatePathUnavailable {
132        snapshot_id: String,
133        #[snafu(implicit)]
134        location: Location,
135    },
136
137    #[snafu(display(
138        "Import state at '{}' does not match current import: {}. Either rerun with matching import arguments, or delete the state file to start over (DDL will be re-executed).",
139        path,
140        reason
141    ))]
142    ImportStateMismatch {
143        path: String,
144        reason: String,
145        #[snafu(implicit)]
146        location: Location,
147    },
148
149    #[cfg(test)]
150    #[snafu(display("Test task failed: {}", message))]
151    TestTaskFailed {
152        message: String,
153        retryable: bool,
154        #[snafu(implicit)]
155        location: Location,
156    },
157
158    #[snafu(display(
159        "Import state references unknown task: chunk {}, schema '{}'",
160        chunk_id,
161        schema
162    ))]
163    ImportStateUnknownTask {
164        chunk_id: u32,
165        schema: String,
166        #[snafu(implicit)]
167        location: Location,
168    },
169
170    #[snafu(display(
171        "Import state at '{}' is not ready for data import: DDL has not been marked completed",
172        path
173    ))]
174    ImportStateDdlIncomplete {
175        path: String,
176        #[snafu(implicit)]
177        location: Location,
178    },
179}
180
181pub type Result<T> = std::result::Result<T, Error>;
182
183impl ErrorExt for Error {
184    fn status_code(&self) -> StatusCode {
185        match self {
186            Error::SnapshotNotFound { .. }
187            | Error::SchemaNotInSnapshot { .. }
188            | Error::IncompleteSnapshot { .. }
189            | Error::EmptyChunkManifest { .. }
190            | Error::InvalidPackedSnapshot { .. }
191            | Error::MissingChunkData { .. } => StatusCode::InvalidArguments,
192            Error::ImportStatePathUnavailable { .. }
193            | Error::ImportStateUnknownTask { .. }
194            | Error::ImportStateDdlIncomplete { .. } => StatusCode::Unexpected,
195            Error::ImportStateMismatch { .. } => StatusCode::InvalidArguments,
196            #[cfg(test)]
197            Error::TestTaskFailed { retryable, .. } => {
198                if *retryable {
199                    StatusCode::StorageUnavailable
200                } else {
201                    StatusCode::InvalidArguments
202                }
203            }
204            Error::Database { error, .. } => error.status_code(),
205            Error::SnapshotStorage { error, .. } | Error::ChunkImportFailed { error, .. } => {
206                error.status_code()
207            }
208            Error::ImportStateParse { .. } => StatusCode::Internal,
209            Error::ImportStateIo { .. } => StatusCode::StorageUnavailable,
210            Error::ImportStateLocked { .. } => StatusCode::IllegalState,
211        }
212    }
213
214    fn retry_hint(&self) -> RetryHint {
215        match self {
216            #[cfg(test)]
217            Error::TestTaskFailed { retryable, .. } => {
218                if *retryable {
219                    RetryHint::Retryable
220                } else {
221                    RetryHint::NonRetryable
222                }
223            }
224            Error::Database { error, .. } => error.retry_hint(),
225            Error::SnapshotStorage { error, .. } | Error::ChunkImportFailed { error, .. } => {
226                error.retry_hint()
227            }
228            Error::ImportStateIo { error, .. } => retry_hint_from_io_error(error),
229            _ => RetryHint::NonRetryable,
230        }
231    }
232
233    fn as_any(&self) -> &dyn Any {
234        self
235    }
236}