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