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