1use 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#[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}