1use std::any::Any;
16use std::net::SocketAddr;
17use std::string::FromUtf8Error;
18
19use axum::http::StatusCode as HttpStatusCode;
20use axum::response::{IntoResponse, Response};
21use axum::{Json, http};
22use base64::DecodeError;
23use common_base::readable_size::ReadableSize;
24use common_error::define_into_tonic_status;
25use common_error::ext::{BoxedError, ErrorExt, RetryHint};
26use common_error::status_code::StatusCode;
27use common_macro::stack_trace_debug;
28use common_telemetry::{error, warn};
29use datafusion::error::DataFusionError;
30use datatypes::prelude::ConcreteDataType;
31use headers::ContentType;
32use http::header::InvalidHeaderValue;
33use query::parser::PromQuery;
34use serde_json::json;
35use snafu::{Location, Snafu};
36
37#[derive(Snafu)]
38#[snafu(visibility(pub))]
39#[stack_trace_debug]
40pub enum Error {
41 #[snafu(display("Failed to bind address: {}", addr))]
42 AddressBind {
43 addr: SocketAddr,
44 #[snafu(source)]
45 error: std::io::Error,
46 #[snafu(implicit)]
47 location: Location,
48 },
49
50 #[snafu(display("Arrow error"))]
51 Arrow {
52 #[snafu(source)]
53 error: arrow_schema::ArrowError,
54 #[snafu(implicit)]
55 location: Location,
56 },
57
58 #[snafu(display("Internal error: {}", err_msg))]
59 Internal { err_msg: String },
60
61 #[snafu(display("Pending rows batcher channel closed"))]
62 BatcherChannelClosed,
63
64 #[snafu(display("Write rejected: {error}"))]
65 WriteRejected {
66 #[snafu(source)]
67 error: meter_core::collect::WriteRejected,
68 #[snafu(implicit)]
69 location: Location,
70 },
71
72 #[snafu(display("Unsupported data type: {}, reason: {}", data_type, reason))]
73 UnsupportedDataType {
74 data_type: ConcreteDataType,
75 reason: String,
76 },
77
78 #[snafu(display("Internal IO error"))]
79 InternalIo {
80 #[snafu(source)]
81 error: std::io::Error,
82 },
83
84 #[snafu(display("Tokio IO error: {}", err_msg))]
85 TokioIo {
86 err_msg: String,
87 #[snafu(source)]
88 error: std::io::Error,
89 },
90
91 #[snafu(display("Failed to collect recordbatch"))]
92 CollectRecordbatch {
93 #[snafu(implicit)]
94 location: Location,
95 source: common_recordbatch::error::Error,
96 },
97
98 #[snafu(display("Failed to start HTTP server"))]
99 StartHttp {
100 #[snafu(source)]
101 error: hyper::Error,
102 },
103
104 #[snafu(display("Failed to start gRPC server"))]
105 StartGrpc {
106 #[snafu(source)]
107 error: tonic::transport::Error,
108 },
109
110 #[snafu(display("Request memory limit exceeded"))]
111 MemoryLimitExceeded {
112 #[snafu(implicit)]
113 location: Location,
114 source: common_memory_manager::Error,
115 },
116
117 #[snafu(display("{} server is already started", server))]
118 AlreadyStarted {
119 server: String,
120 #[snafu(implicit)]
121 location: Location,
122 },
123
124 #[snafu(display("Failed to bind address {}", addr))]
125 TcpBind {
126 addr: SocketAddr,
127 #[snafu(source)]
128 error: std::io::Error,
129 },
130
131 #[snafu(display("Failed to execute query"))]
132 ExecuteQuery {
133 #[snafu(implicit)]
134 location: Location,
135 source: BoxedError,
136 },
137
138 #[snafu(display("Failed to execute plan"))]
139 ExecutePlan {
140 #[snafu(implicit)]
141 location: Location,
142 source: BoxedError,
143 },
144
145 #[snafu(display("Execute gRPC query error"))]
146 ExecuteGrpcQuery {
147 #[snafu(implicit)]
148 location: Location,
149 source: BoxedError,
150 },
151
152 #[snafu(display("Execute gRPC request error"))]
153 ExecuteGrpcRequest {
154 #[snafu(implicit)]
155 location: Location,
156 source: BoxedError,
157 },
158
159 #[snafu(display("Failed to check database validity"))]
160 CheckDatabaseValidity {
161 #[snafu(implicit)]
162 location: Location,
163 source: BoxedError,
164 },
165
166 #[snafu(display("Failed to describe statement"))]
167 DescribeStatement { source: BoxedError },
168
169 #[snafu(display("Pipeline error"))]
170 Pipeline {
171 #[snafu(source)]
172 source: pipeline::error::Error,
173 #[snafu(implicit)]
174 location: Location,
175 },
176
177 #[snafu(display("Not supported: {}", feat))]
178 NotSupported { feat: String },
179
180 #[snafu(display("Invalid request parameter: {}", reason))]
181 InvalidParameter {
182 reason: String,
183 #[snafu(implicit)]
184 location: Location,
185 },
186
187 #[snafu(display("Invalid OTLP metric input: {}", reason))]
188 InvalidOtlpMetricInput { reason: String },
189
190 #[snafu(display(
191 "Too many concurrent large requests, limit: {}, request size: {}",
192 ReadableSize(*limit as u64),
193 ReadableSize(*request_size as u64)
194 ))]
195 TooManyConcurrentRequests {
196 limit: usize,
197 request_size: usize,
198 #[snafu(implicit)]
199 location: Location,
200 },
201
202 #[snafu(display("Unsupported operation: {}", operation))]
203 UnsupportedOperation { operation: String },
204
205 #[snafu(display("Invalid query: {}", reason))]
206 InvalidQuery {
207 reason: String,
208 #[snafu(implicit)]
209 location: Location,
210 },
211
212 #[snafu(display("Failed to parse query"))]
213 FailedToParseQuery {
214 #[snafu(implicit)]
215 location: Location,
216 source: sql::error::Error,
217 },
218
219 #[snafu(display("Failed to parse InfluxDB line protocol"))]
220 InfluxdbLineProtocol {
221 #[snafu(implicit)]
222 location: Location,
223 #[snafu(source)]
224 error: influxdb_line_protocol::Error,
225 },
226
227 #[snafu(display("Failed to write row"))]
228 RowWriter {
229 #[snafu(implicit)]
230 location: Location,
231 source: common_grpc::error::Error,
232 },
233
234 #[snafu(display("Failed to convert time precision, name: {}", name))]
235 TimePrecision {
236 name: String,
237 #[snafu(implicit)]
238 location: Location,
239 },
240
241 #[snafu(display("Invalid OpenTSDB Json request"))]
242 InvalidOpentsdbJsonRequest {
243 #[snafu(source)]
244 error: serde_json::error::Error,
245 #[snafu(implicit)]
246 location: Location,
247 },
248
249 #[snafu(display("Failed to decode prometheus remote request"))]
250 DecodePromRemoteRequest {
251 #[snafu(implicit)]
252 location: Location,
253 #[snafu(source)]
254 error: prost::DecodeError,
255 },
256
257 #[snafu(display(
258 "Failed to decode OTLP request (content-type: {content_type}): {error}. The endpoint only accepts 'application/x-protobuf' format."
259 ))]
260 DecodeOtlpRequest {
261 content_type: String,
262 #[snafu(implicit)]
263 location: Location,
264 #[snafu(source)]
265 error: prost::DecodeError,
266 },
267
268 #[snafu(display("Failed to decode Loki request: {error}"))]
269 DecodeLokiRequest {
270 #[snafu(implicit)]
271 location: Location,
272 #[snafu(source)]
273 error: prost::DecodeError,
274 },
275
276 #[snafu(display(
277 "Unsupported content type 'application/json'. OTLP endpoint only supports 'application/x-protobuf'. Please configure your OTLP exporter to use protobuf encoding."
278 ))]
279 UnsupportedJsonContentType {
280 #[snafu(implicit)]
281 location: Location,
282 },
283
284 #[snafu(display("Common Meta error"))]
285 CommonMeta {
286 #[snafu(implicit)]
287 location: Location,
288 #[snafu(source)]
289 source: common_meta::error::Error,
290 },
291
292 #[snafu(display("Failed to decompress snappy prometheus remote request"))]
293 DecompressSnappyPromRemoteRequest {
294 #[snafu(implicit)]
295 location: Location,
296 #[snafu(source)]
297 error: snap::Error,
298 },
299
300 #[snafu(display("Failed to decompress snappy Loki request"))]
301 DecompressSnappyLokiRequest {
302 #[snafu(implicit)]
303 location: Location,
304 #[snafu(source)]
305 error: snap::Error,
306 },
307
308 #[snafu(display("Failed to decompress zstd prometheus remote request"))]
309 DecompressZstdPromRemoteRequest {
310 #[snafu(implicit)]
311 location: Location,
312 #[snafu(source)]
313 error: std::io::Error,
314 },
315
316 #[snafu(display(
317 "Decompressed request body is too large: {} bytes exceeds the limit {} bytes",
318 size,
319 limit
320 ))]
321 DecompressedBodyTooLarge {
322 size: u64,
323 limit: u64,
324 #[snafu(implicit)]
325 location: Location,
326 },
327
328 #[snafu(display("Failed to compress prometheus remote request"))]
329 CompressPromRemoteRequest {
330 #[snafu(implicit)]
331 location: Location,
332 #[snafu(source)]
333 error: snap::Error,
334 },
335
336 #[snafu(display("Invalid prometheus remote request, msg: {}", msg))]
337 InvalidPromRemoteRequest {
338 msg: String,
339 #[snafu(implicit)]
340 location: Location,
341 },
342
343 #[snafu(display("Invalid prometheus remote read query result, msg: {}", msg))]
344 InvalidPromRemoteReadQueryResult {
345 msg: String,
346 #[snafu(implicit)]
347 location: Location,
348 },
349
350 #[snafu(display("Invalid Flight ticket"))]
351 InvalidFlightTicket {
352 #[snafu(source)]
353 error: api::DecodeError,
354 #[snafu(implicit)]
355 location: Location,
356 },
357
358 #[snafu(display("Tls is required for {}, plain connection is rejected", server))]
359 TlsRequired { server: String },
360
361 #[snafu(display("Failed to get user info"))]
362 Auth {
363 #[snafu(implicit)]
364 location: Location,
365 source: auth::error::Error,
366 },
367
368 #[snafu(display("Not found http or grpc authorization header"))]
369 NotFoundAuthHeader {},
370
371 #[snafu(display("Not found influx http authorization info"))]
372 NotFoundInfluxAuth {},
373
374 #[snafu(display("Unsupported http auth scheme, name: {}", name))]
375 UnsupportedAuthScheme { name: String },
376
377 #[snafu(display("Invalid visibility ASCII chars"))]
378 InvalidAuthHeaderInvisibleASCII {
379 #[snafu(source)]
380 error: hyper::header::ToStrError,
381 #[snafu(implicit)]
382 location: Location,
383 },
384
385 #[snafu(display("Invalid utf-8 value"))]
386 InvalidAuthHeaderInvalidUtf8Value {
387 #[snafu(source)]
388 error: FromUtf8Error,
389 #[snafu(implicit)]
390 location: Location,
391 },
392
393 #[snafu(display("Invalid http authorization header"))]
394 InvalidAuthHeader {
395 #[snafu(implicit)]
396 location: Location,
397 },
398
399 #[snafu(display("Invalid base64 value"))]
400 InvalidBase64Value {
401 #[snafu(source)]
402 error: DecodeError,
403 #[snafu(implicit)]
404 location: Location,
405 },
406
407 #[snafu(display("Invalid utf-8 value"))]
408 InvalidUtf8Value {
409 #[snafu(source)]
410 error: FromUtf8Error,
411 #[snafu(implicit)]
412 location: Location,
413 },
414
415 #[snafu(display("Invalid http header value"))]
416 InvalidHeaderValue {
417 #[snafu(source)]
418 error: InvalidHeaderValue,
419 #[snafu(implicit)]
420 location: Location,
421 },
422
423 #[snafu(transparent)]
424 Catalog {
425 source: catalog::error::Error,
426 #[snafu(implicit)]
427 location: Location,
428 },
429
430 #[snafu(display("Cannot find requested table: {}.{}.{}", catalog, schema, table))]
431 TableNotFound {
432 catalog: String,
433 schema: String,
434 table: String,
435 #[snafu(implicit)]
436 location: Location,
437 },
438
439 #[cfg(feature = "mem-prof")]
440 #[snafu(display("Failed to dump profile data"))]
441 DumpProfileData {
442 #[snafu(implicit)]
443 location: Location,
444 source: common_mem_prof::error::Error,
445 },
446
447 #[snafu(display("Invalid prepare statement: {}", err_msg))]
448 InvalidPrepareStatement {
449 err_msg: String,
450 #[snafu(implicit)]
451 location: Location,
452 },
453
454 #[snafu(display("Failed to build HTTP response"))]
455 BuildHttpResponse {
456 #[snafu(source)]
457 error: http::Error,
458 #[snafu(implicit)]
459 location: Location,
460 },
461
462 #[snafu(display("Failed to parse PromQL: {query:?}"))]
463 ParsePromQL {
464 query: Box<PromQuery>,
465 #[snafu(implicit)]
466 location: Location,
467 source: query::error::Error,
468 },
469
470 #[snafu(display("Failed to parse timestamp: {}", timestamp))]
471 ParseTimestamp {
472 timestamp: String,
473 #[snafu(implicit)]
474 location: Location,
475 #[snafu(source)]
476 error: query::error::Error,
477 },
478
479 #[snafu(display("Failed to infer parameter types"))]
480 InferParameterTypes {
481 #[snafu(implicit)]
482 location: Location,
483 #[snafu(source)]
484 error: query::error::Error,
485 },
486
487 #[snafu(display("{}", reason))]
488 UnexpectedResult {
489 reason: String,
490 #[snafu(implicit)]
491 location: Location,
492 },
493
494 #[snafu(display("Other error"))]
497 Other {
498 source: BoxedError,
499 #[snafu(implicit)]
500 location: Location,
501 },
502
503 #[snafu(display("Failed to join task"))]
504 JoinTask {
505 #[snafu(source)]
506 error: tokio::task::JoinError,
507 #[snafu(implicit)]
508 location: Location,
509 },
510
511 #[cfg(feature = "pprof")]
512 #[snafu(display("Failed to dump pprof data"))]
513 DumpPprof { source: common_pprof::error::Error },
514
515 #[cfg(not(windows))]
516 #[snafu(display("Failed to update jemalloc metrics"))]
517 UpdateJemallocMetrics {
518 #[snafu(source)]
519 error: tikv_jemalloc_ctl::Error,
520 #[snafu(implicit)]
521 location: Location,
522 },
523
524 #[snafu(display("DataFrame operation error"))]
525 DataFrame {
526 #[snafu(source)]
527 error: datafusion::error::DataFusionError,
528 #[snafu(implicit)]
529 location: Location,
530 },
531
532 #[snafu(display("Failed to convert scalar value"))]
533 ConvertScalarValue {
534 source: datatypes::error::Error,
535 #[snafu(implicit)]
536 location: Location,
537 },
538
539 #[snafu(display("Expected type: {:?}, actual: {:?}", expected, actual))]
540 PreparedStmtTypeMismatch {
541 expected: ConcreteDataType,
542 actual: opensrv_mysql::ColumnType,
543 #[snafu(implicit)]
544 location: Location,
545 },
546
547 #[snafu(display(
548 "Column: {}, {} incompatible, expected: {}, actual: {}",
549 column_name,
550 datatype,
551 expected,
552 actual
553 ))]
554 IncompatibleSchema {
555 column_name: String,
556 datatype: String,
557 expected: i32,
558 actual: i32,
559 #[snafu(implicit)]
560 location: Location,
561 },
562
563 #[snafu(display("Failed to convert to json"))]
564 ToJson {
565 #[snafu(source)]
566 error: serde_json::error::Error,
567 #[snafu(implicit)]
568 location: Location,
569 },
570
571 #[snafu(display("Failed to parse payload as json"))]
572 ParseJson {
573 #[snafu(source)]
574 error: serde_json::error::Error,
575 #[snafu(implicit)]
576 location: Location,
577 },
578
579 #[snafu(display("Invalid Loki labels: {}", msg))]
580 InvalidLokiLabels {
581 msg: String,
582 #[snafu(implicit)]
583 location: Location,
584 },
585
586 #[snafu(display("Invalid Loki JSON request: {}", msg))]
587 InvalidLokiPayload {
588 msg: String,
589 #[snafu(implicit)]
590 location: Location,
591 },
592
593 #[snafu(display("Unsupported content type: {:?}", content_type))]
594 UnsupportedContentType {
595 content_type: ContentType,
596 #[snafu(implicit)]
597 location: Location,
598 },
599
600 #[snafu(display("Failed to decode url"))]
601 UrlDecode {
602 #[snafu(source)]
603 error: FromUtf8Error,
604 #[snafu(implicit)]
605 location: Location,
606 },
607
608 #[snafu(display("Failed to convert Mysql value, error: {}", err_msg))]
609 MysqlValueConversion {
610 err_msg: String,
611 #[snafu(implicit)]
612 location: Location,
613 },
614
615 #[snafu(display("Invalid table name"))]
616 InvalidTableName {
617 #[snafu(source)]
618 error: tonic::metadata::errors::ToStrError,
619 #[snafu(implicit)]
620 location: Location,
621 },
622
623 #[snafu(display("Failed to initialize a watcher for file {}", path))]
624 FileWatch {
625 path: String,
626 #[snafu(source)]
627 error: notify::Error,
628 },
629
630 #[snafu(display("Timestamp overflow: {}", error))]
631 TimestampOverflow {
632 error: String,
633 #[snafu(implicit)]
634 location: Location,
635 },
636
637 #[snafu(display("Unsupported json data type for tag: {} {}", key, ty))]
638 UnsupportedJsonDataTypeForTag {
639 key: String,
640 ty: String,
641 #[snafu(implicit)]
642 location: Location,
643 },
644
645 #[snafu(display("Convert SQL value error"))]
646 ConvertSqlValue {
647 source: datatypes::error::Error,
648 #[snafu(implicit)]
649 location: Location,
650 },
651
652 #[snafu(display("Prepare statement not found: {}", name))]
653 PrepareStatementNotFound {
654 name: String,
655 #[snafu(implicit)]
656 location: Location,
657 },
658
659 #[snafu(display("Invalid elasticsearch input, reason: {}", reason))]
660 InvalidElasticsearchInput {
661 reason: String,
662 #[snafu(implicit)]
663 location: Location,
664 },
665
666 #[snafu(display("Invalid Jaeger query, reason: {}", reason))]
667 InvalidJaegerQuery {
668 reason: String,
669 #[snafu(implicit)]
670 location: Location,
671 },
672
673 #[snafu(display("DataFusion error"))]
674 DataFusion {
675 #[snafu(source)]
676 error: DataFusionError,
677 #[snafu(implicit)]
678 location: Location,
679 },
680
681 #[snafu(display("Failed to handle otel-arrow request, error message: {}", err_msg))]
682 HandleOtelArrowRequest {
683 err_msg: String,
684 #[snafu(implicit)]
685 location: Location,
686 },
687
688 #[snafu(display("Unknown hint: {}", hint))]
689 UnknownHint { hint: String },
690
691 #[snafu(display("Query has been cancelled"))]
692 Cancelled {
693 #[snafu(implicit)]
694 location: Location,
695 },
696
697 #[snafu(display("Service suspended"))]
698 Suspended {
699 #[snafu(implicit)]
700 location: Location,
701 },
702
703 #[snafu(transparent)]
704 Operator {
705 source: operator::error::Error,
706 #[snafu(implicit)]
707 location: Location,
708 },
709
710 #[snafu(transparent)]
711 GreptimeProto {
712 source: api::error::Error,
713 #[snafu(implicit)]
714 location: Location,
715 },
716
717 #[snafu(transparent)]
718 DataTypes {
719 source: datatypes::error::Error,
720 #[snafu(implicit)]
721 location: Location,
722 },
723
724 #[snafu(transparent)]
725 Partition {
726 source: partition::error::Error,
727 #[snafu(implicit)]
728 location: Location,
729 },
730
731 #[snafu(transparent)]
732 MetricEngine {
733 source: metric_engine::error::Error,
734 #[snafu(implicit)]
735 location: Location,
736 },
737}
738
739pub type Result<T, E = Error> = std::result::Result<T, E>;
740
741impl ErrorExt for Error {
742 fn status_code(&self) -> StatusCode {
743 use Error::*;
744 match self {
745 Internal { .. }
746 | BatcherChannelClosed
747 | InternalIo { .. }
748 | TokioIo { .. }
749 | StartHttp { .. }
750 | StartGrpc { .. }
751 | TcpBind { .. }
752 | BuildHttpResponse { .. }
753 | Arrow { .. }
754 | FileWatch { .. } => StatusCode::Internal,
755
756 AddressBind { .. }
757 | AlreadyStarted { .. }
758 | InvalidPromRemoteReadQueryResult { .. } => StatusCode::IllegalState,
759
760 UnsupportedDataType { .. } | UnsupportedOperation { .. } => StatusCode::Unsupported,
761
762 #[cfg(not(windows))]
763 UpdateJemallocMetrics { .. } => StatusCode::Internal,
764
765 CollectRecordbatch { source, .. } => source.status_code(),
766
767 ExecuteQuery { source, .. }
768 | ExecutePlan { source, .. }
769 | ExecuteGrpcQuery { source, .. }
770 | ExecuteGrpcRequest { source, .. }
771 | CheckDatabaseValidity { source, .. } => source.status_code(),
772
773 Pipeline { source, .. } => source.status_code(),
774 CommonMeta { source, .. } => source.status_code(),
775
776 NotSupported { .. }
777 | InvalidParameter { .. }
778 | InvalidOtlpMetricInput { .. }
779 | InvalidQuery { .. }
780 | InfluxdbLineProtocol { .. }
781 | InvalidOpentsdbJsonRequest { .. }
782 | DecodePromRemoteRequest { .. }
783 | DecodeOtlpRequest { .. }
784 | DecodeLokiRequest { .. }
785 | UnsupportedJsonContentType { .. }
786 | CompressPromRemoteRequest { .. }
787 | DecompressSnappyPromRemoteRequest { .. }
788 | DecompressSnappyLokiRequest { .. }
789 | DecompressZstdPromRemoteRequest { .. }
790 | DecompressedBodyTooLarge { .. }
791 | InvalidPromRemoteRequest { .. }
792 | InvalidFlightTicket { .. }
793 | InvalidPrepareStatement { .. }
794 | InferParameterTypes { .. }
795 | DataFrame { .. }
796 | PreparedStmtTypeMismatch { .. }
797 | TimePrecision { .. }
798 | UrlDecode { .. }
799 | IncompatibleSchema { .. }
800 | MysqlValueConversion { .. }
801 | ParseJson { .. }
802 | InvalidLokiLabels { .. }
803 | InvalidLokiPayload { .. }
804 | UnsupportedContentType { .. }
805 | TimestampOverflow { .. }
806 | UnsupportedJsonDataTypeForTag { .. }
807 | InvalidTableName { .. }
808 | PrepareStatementNotFound { .. }
809 | FailedToParseQuery { .. }
810 | InvalidElasticsearchInput { .. }
811 | InvalidJaegerQuery { .. }
812 | ParseTimestamp { .. }
813 | UnknownHint { .. } => StatusCode::InvalidArguments,
814
815 Catalog { source, .. } => source.status_code(),
816 RowWriter { source, .. } => source.status_code(),
817 DataTypes { source, .. } => source.status_code(),
818
819 TlsRequired { .. } => StatusCode::Unknown,
820 Auth { source, .. } => source.status_code(),
821 DescribeStatement { source } => source.status_code(),
822
823 NotFoundAuthHeader { .. } | NotFoundInfluxAuth { .. } => StatusCode::AuthHeaderNotFound,
824 InvalidAuthHeaderInvisibleASCII { .. }
825 | UnsupportedAuthScheme { .. }
826 | InvalidAuthHeader { .. }
827 | InvalidBase64Value { .. }
828 | InvalidAuthHeaderInvalidUtf8Value { .. } => StatusCode::InvalidAuthHeader,
829
830 TableNotFound { .. } => StatusCode::TableNotFound,
831
832 #[cfg(feature = "mem-prof")]
833 DumpProfileData { source, .. } => source.status_code(),
834
835 InvalidUtf8Value { .. } | InvalidHeaderValue { .. } => StatusCode::InvalidArguments,
836
837 TooManyConcurrentRequests { .. } => StatusCode::RuntimeResourcesExhausted,
838
839 ParsePromQL { source, .. } => source.status_code(),
840 Other { source, .. } => source.status_code(),
841
842 UnexpectedResult { .. } => StatusCode::Unexpected,
843
844 JoinTask { error, .. } => {
845 if error.is_cancelled() {
846 StatusCode::Cancelled
847 } else if error.is_panic() {
848 StatusCode::Unexpected
849 } else {
850 StatusCode::Unknown
851 }
852 }
853
854 #[cfg(feature = "pprof")]
855 DumpPprof { source, .. } => source.status_code(),
856
857 ConvertScalarValue { source, .. } => source.status_code(),
858
859 ToJson { .. } | DataFusion { .. } => StatusCode::Internal,
860
861 ConvertSqlValue { source, .. } => source.status_code(),
862
863 HandleOtelArrowRequest { .. } => StatusCode::Internal,
864
865 Cancelled { .. } => StatusCode::Cancelled,
866
867 Suspended { .. } => StatusCode::Suspended,
868
869 MemoryLimitExceeded { .. } | WriteRejected { .. } => StatusCode::RateLimited,
870
871 GreptimeProto { source, .. } => source.status_code(),
872 Partition { source, .. } => source.status_code(),
873 MetricEngine { source, .. } => source.status_code(),
874 Operator { source, .. } => source.status_code(),
875 }
876 }
877
878 fn retry_hint(&self) -> RetryHint {
879 use Error::*;
880 match self {
881 ExecuteQuery { source, .. }
882 | ExecutePlan { source, .. }
883 | ExecuteGrpcQuery { source, .. }
884 | ExecuteGrpcRequest { source, .. }
885 | CheckDatabaseValidity { source, .. }
886 | DescribeStatement { source } => source.retry_hint(),
887
888 Pipeline { source, .. } => source.retry_hint(),
889 CommonMeta { source, .. } => source.retry_hint(),
890 Catalog { source, .. } => source.retry_hint(),
891 RowWriter { source, .. } => source.retry_hint(),
892 Auth { source, .. } => source.retry_hint(),
893
894 #[cfg(feature = "mem-prof")]
895 DumpProfileData { source, .. } => source.retry_hint(),
896
897 ParsePromQL { source, .. } => source.retry_hint(),
898 Other { source, .. } => source.retry_hint(),
899
900 #[cfg(feature = "pprof")]
901 DumpPprof { source, .. } => source.retry_hint(),
902
903 ConvertScalarValue { source, .. } => source.retry_hint(),
904 ConvertSqlValue { source, .. } => source.retry_hint(),
905 GreptimeProto { source, .. } => source.retry_hint(),
906 Partition { source, .. } => source.retry_hint(),
907 MetricEngine { source, .. } => source.retry_hint(),
908 Operator { source, .. } => source.retry_hint(),
909
910 MemoryLimitExceeded { source, .. } => source.retry_hint(),
911 CollectRecordbatch { source, .. } => source.retry_hint(),
912
913 TooManyConcurrentRequests { .. } | WriteRejected { .. } => RetryHint::Retryable,
914
915 _ => RetryHint::NonRetryable,
916 }
917 }
918
919 fn as_any(&self) -> &dyn Any {
920 self
921 }
922}
923
924define_into_tonic_status!(Error);
925
926impl From<std::io::Error> for Error {
927 fn from(e: std::io::Error) -> Self {
928 Error::InternalIo { error: e }
929 }
930}
931
932pub(crate) fn log_error_if_necessary(error: &Error) {
933 if error.status_code().should_log_error() {
934 error!(error; "Failed to handle HTTP request ");
935 } else {
936 warn!(error; "Failed to handle HTTP request ");
937 }
938}
939
940impl IntoResponse for Error {
941 fn into_response(self) -> Response {
942 let error_msg = self.output_msg();
943 let status = status_code_to_http_status(&self.status_code());
944
945 log_error_if_necessary(&self);
946
947 let body = Json(json!({
948 "error": error_msg,
949 }));
950 (status, body).into_response()
951 }
952}
953
954pub fn status_code_to_http_status(status_code: &StatusCode) -> HttpStatusCode {
956 match status_code {
957 StatusCode::Success => HttpStatusCode::OK,
958
959 StatusCode::Cancelled | StatusCode::DeadlineExceeded => HttpStatusCode::GATEWAY_TIMEOUT,
962
963 StatusCode::Unsupported
964 | StatusCode::InvalidArguments
965 | StatusCode::InvalidSyntax
966 | StatusCode::RequestOutdated
967 | StatusCode::RegionAlreadyExists
968 | StatusCode::TableColumnExists
969 | StatusCode::TableAlreadyExists
970 | StatusCode::RegionNotFound
971 | StatusCode::DatabaseNotFound
972 | StatusCode::TableNotFound
973 | StatusCode::TableColumnNotFound
974 | StatusCode::PlanQuery
975 | StatusCode::DatabaseAlreadyExists
976 | StatusCode::TriggerAlreadyExists
977 | StatusCode::TriggerNotFound
978 | StatusCode::FlowNotFound
979 | StatusCode::FlowAlreadyExists => HttpStatusCode::BAD_REQUEST,
980
981 StatusCode::AuthHeaderNotFound
982 | StatusCode::InvalidAuthHeader
983 | StatusCode::UserNotFound
984 | StatusCode::UnsupportedPasswordType
985 | StatusCode::UserPasswordMismatch
986 | StatusCode::RegionReadonly => HttpStatusCode::UNAUTHORIZED,
987
988 StatusCode::PermissionDenied | StatusCode::AccessDenied => HttpStatusCode::FORBIDDEN,
989
990 StatusCode::RateLimited => HttpStatusCode::TOO_MANY_REQUESTS,
991
992 StatusCode::RegionNotReady
993 | StatusCode::TableUnavailable
994 | StatusCode::RegionBusy
995 | StatusCode::StorageUnavailable
996 | StatusCode::External
997 | StatusCode::Suspended => HttpStatusCode::SERVICE_UNAVAILABLE,
998
999 StatusCode::Internal
1000 | StatusCode::Unexpected
1001 | StatusCode::IllegalState
1002 | StatusCode::PartialFailure
1003 | StatusCode::Unknown
1004 | StatusCode::RuntimeResourcesExhausted
1005 | StatusCode::EngineExecuteQuery => HttpStatusCode::INTERNAL_SERVER_ERROR,
1006 }
1007}
1008
1009#[cfg(test)]
1010mod tests {
1011 use common_error::ext::PlainError;
1012 use common_error::mock::MockError;
1013 use common_error::{GREPTIME_DB_HEADER_ERROR_CODE, GREPTIME_DB_HEADER_ERROR_RETRY_HINT};
1014
1015 use super::*;
1016
1017 #[test]
1018 fn collect_recordbatch_preserves_poll_stream_status_in_tonic_status() {
1019 let error = Error::CollectRecordbatch {
1020 source: common_recordbatch::error::Error::PollStream {
1021 error: DataFusionError::External(Box::new(BoxedError::new(PlainError::new(
1022 "neutral error".to_string(),
1023 StatusCode::RequestOutdated,
1024 )))),
1025 location: Location::default(),
1026 },
1027 location: Location::default(),
1028 };
1029
1030 let status: tonic::Status = error.into();
1031 assert_eq!(status.code(), tonic::Code::InvalidArgument);
1032 assert_eq!(
1033 status
1034 .metadata()
1035 .get(GREPTIME_DB_HEADER_ERROR_CODE)
1036 .unwrap()
1037 .to_str()
1038 .unwrap(),
1039 (StatusCode::RequestOutdated as u32).to_string()
1040 );
1041 }
1042
1043 #[tokio::test]
1044 async fn test_storage_write_retry_hints_reach_tonic_status() {
1045 for hint in [
1046 RetryHint::NonRetryable,
1047 RetryHint::Retryable,
1048 RetryHint::RetryAfterRouteRefresh,
1049 ] {
1050 let source =
1051 BoxedError::new(MockError::new(StatusCode::RequestOutdated).with_retry_hint(hint));
1052 let completion = common_query::WriteCompletion::new(std::future::ready(Err(source)));
1053 let error = Error::ExecuteGrpcQuery {
1054 source: completion.wait().await.unwrap_err(),
1055 location: Location::default(),
1056 };
1057 let status: tonic::Status = error.into();
1058 assert_eq!(status.code(), tonic::Code::InvalidArgument);
1059 assert_eq!(
1060 status
1061 .metadata()
1062 .get(GREPTIME_DB_HEADER_ERROR_CODE)
1063 .unwrap()
1064 .to_str()
1065 .unwrap(),
1066 (StatusCode::RequestOutdated as u32).to_string()
1067 );
1068 assert_eq!(
1069 status
1070 .metadata()
1071 .get(GREPTIME_DB_HEADER_ERROR_RETRY_HINT)
1072 .unwrap()
1073 .to_str()
1074 .unwrap(),
1075 hint.as_str()
1076 );
1077 }
1078 }
1079}