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(
323 "Decompressed request body is too large: {} bytes exceeds the limit {} bytes",
324 size,
325 limit
326 ))]
327 DecompressedBodyTooLarge {
328 size: u64,
329 limit: u64,
330 #[snafu(implicit)]
331 location: Location,
332 },
333
334 #[snafu(display("Failed to compress prometheus remote request"))]
335 CompressPromRemoteRequest {
336 #[snafu(implicit)]
337 location: Location,
338 #[snafu(source)]
339 error: snap::Error,
340 },
341
342 #[snafu(display("Invalid prometheus remote request, msg: {}", msg))]
343 InvalidPromRemoteRequest {
344 msg: String,
345 #[snafu(implicit)]
346 location: Location,
347 },
348
349 #[snafu(display("Invalid prometheus remote read query result, msg: {}", msg))]
350 InvalidPromRemoteReadQueryResult {
351 msg: String,
352 #[snafu(implicit)]
353 location: Location,
354 },
355
356 #[snafu(display("Invalid Flight ticket"))]
357 InvalidFlightTicket {
358 #[snafu(source)]
359 error: api::DecodeError,
360 #[snafu(implicit)]
361 location: Location,
362 },
363
364 #[snafu(display("Tls is required for {}, plain connection is rejected", server))]
365 TlsRequired { server: String },
366
367 #[snafu(display("Failed to get user info"))]
368 Auth {
369 #[snafu(implicit)]
370 location: Location,
371 source: auth::error::Error,
372 },
373
374 #[snafu(display("Not found http or grpc authorization header"))]
375 NotFoundAuthHeader {},
376
377 #[snafu(display("Not found influx http authorization info"))]
378 NotFoundInfluxAuth {},
379
380 #[snafu(display("Unsupported http auth scheme, name: {}", name))]
381 UnsupportedAuthScheme { name: String },
382
383 #[snafu(display("Invalid visibility ASCII chars"))]
384 InvalidAuthHeaderInvisibleASCII {
385 #[snafu(source)]
386 error: hyper::header::ToStrError,
387 #[snafu(implicit)]
388 location: Location,
389 },
390
391 #[snafu(display("Invalid utf-8 value"))]
392 InvalidAuthHeaderInvalidUtf8Value {
393 #[snafu(source)]
394 error: FromUtf8Error,
395 #[snafu(implicit)]
396 location: Location,
397 },
398
399 #[snafu(display("Invalid http authorization header"))]
400 InvalidAuthHeader {
401 #[snafu(implicit)]
402 location: Location,
403 },
404
405 #[snafu(display("Invalid base64 value"))]
406 InvalidBase64Value {
407 #[snafu(source)]
408 error: DecodeError,
409 #[snafu(implicit)]
410 location: Location,
411 },
412
413 #[snafu(display("Invalid utf-8 value"))]
414 InvalidUtf8Value {
415 #[snafu(source)]
416 error: FromUtf8Error,
417 #[snafu(implicit)]
418 location: Location,
419 },
420
421 #[snafu(display("Invalid http header value"))]
422 InvalidHeaderValue {
423 #[snafu(source)]
424 error: InvalidHeaderValue,
425 #[snafu(implicit)]
426 location: Location,
427 },
428
429 #[snafu(transparent)]
430 Catalog {
431 source: catalog::error::Error,
432 #[snafu(implicit)]
433 location: Location,
434 },
435
436 #[snafu(display("Cannot find requested table: {}.{}.{}", catalog, schema, table))]
437 TableNotFound {
438 catalog: String,
439 schema: String,
440 table: String,
441 #[snafu(implicit)]
442 location: Location,
443 },
444
445 #[cfg(feature = "mem-prof")]
446 #[snafu(display("Failed to dump profile data"))]
447 DumpProfileData {
448 #[snafu(implicit)]
449 location: Location,
450 source: common_mem_prof::error::Error,
451 },
452
453 #[snafu(display("Invalid prepare statement: {}", err_msg))]
454 InvalidPrepareStatement {
455 err_msg: String,
456 #[snafu(implicit)]
457 location: Location,
458 },
459
460 #[snafu(display("Failed to build HTTP response"))]
461 BuildHttpResponse {
462 #[snafu(source)]
463 error: http::Error,
464 #[snafu(implicit)]
465 location: Location,
466 },
467
468 #[snafu(display("Failed to parse PromQL: {query:?}"))]
469 ParsePromQL {
470 query: Box<PromQuery>,
471 #[snafu(implicit)]
472 location: Location,
473 source: query::error::Error,
474 },
475
476 #[snafu(display("Failed to parse timestamp: {}", timestamp))]
477 ParseTimestamp {
478 timestamp: String,
479 #[snafu(implicit)]
480 location: Location,
481 #[snafu(source)]
482 error: query::error::Error,
483 },
484
485 #[snafu(display("Failed to infer parameter types"))]
486 InferParameterTypes {
487 #[snafu(implicit)]
488 location: Location,
489 #[snafu(source)]
490 error: query::error::Error,
491 },
492
493 #[snafu(display("{}", reason))]
494 UnexpectedResult {
495 reason: String,
496 #[snafu(implicit)]
497 location: Location,
498 },
499
500 #[snafu(display("Other error"))]
503 Other {
504 source: BoxedError,
505 #[snafu(implicit)]
506 location: Location,
507 },
508
509 #[snafu(display("Failed to join task"))]
510 JoinTask {
511 #[snafu(source)]
512 error: tokio::task::JoinError,
513 #[snafu(implicit)]
514 location: Location,
515 },
516
517 #[cfg(feature = "pprof")]
518 #[snafu(display("Failed to dump pprof data"))]
519 DumpPprof { source: common_pprof::error::Error },
520
521 #[cfg(not(windows))]
522 #[snafu(display("Failed to update jemalloc metrics"))]
523 UpdateJemallocMetrics {
524 #[snafu(source)]
525 error: tikv_jemalloc_ctl::Error,
526 #[snafu(implicit)]
527 location: Location,
528 },
529
530 #[snafu(display("DataFrame operation error"))]
531 DataFrame {
532 #[snafu(source)]
533 error: datafusion::error::DataFusionError,
534 #[snafu(implicit)]
535 location: Location,
536 },
537
538 #[snafu(display("Failed to convert scalar value"))]
539 ConvertScalarValue {
540 source: datatypes::error::Error,
541 #[snafu(implicit)]
542 location: Location,
543 },
544
545 #[snafu(display("Expected type: {:?}, actual: {:?}", expected, actual))]
546 PreparedStmtTypeMismatch {
547 expected: ConcreteDataType,
548 actual: opensrv_mysql::ColumnType,
549 #[snafu(implicit)]
550 location: Location,
551 },
552
553 #[snafu(display(
554 "Column: {}, {} incompatible, expected: {}, actual: {}",
555 column_name,
556 datatype,
557 expected,
558 actual
559 ))]
560 IncompatibleSchema {
561 column_name: String,
562 datatype: String,
563 expected: i32,
564 actual: i32,
565 #[snafu(implicit)]
566 location: Location,
567 },
568
569 #[snafu(display("Failed to convert to json"))]
570 ToJson {
571 #[snafu(source)]
572 error: serde_json::error::Error,
573 #[snafu(implicit)]
574 location: Location,
575 },
576
577 #[snafu(display("Failed to parse payload as json"))]
578 ParseJson {
579 #[snafu(source)]
580 error: serde_json::error::Error,
581 #[snafu(implicit)]
582 location: Location,
583 },
584
585 #[snafu(display("Invalid Loki labels: {}", msg))]
586 InvalidLokiLabels {
587 msg: String,
588 #[snafu(implicit)]
589 location: Location,
590 },
591
592 #[snafu(display("Invalid Loki JSON request: {}", msg))]
593 InvalidLokiPayload {
594 msg: String,
595 #[snafu(implicit)]
596 location: Location,
597 },
598
599 #[snafu(display("Unsupported content type: {:?}", content_type))]
600 UnsupportedContentType {
601 content_type: ContentType,
602 #[snafu(implicit)]
603 location: Location,
604 },
605
606 #[snafu(display("Failed to decode url"))]
607 UrlDecode {
608 #[snafu(source)]
609 error: FromUtf8Error,
610 #[snafu(implicit)]
611 location: Location,
612 },
613
614 #[snafu(display("Failed to convert Mysql value, error: {}", err_msg))]
615 MysqlValueConversion {
616 err_msg: String,
617 #[snafu(implicit)]
618 location: Location,
619 },
620
621 #[snafu(display("Invalid table name"))]
622 InvalidTableName {
623 #[snafu(source)]
624 error: tonic::metadata::errors::ToStrError,
625 #[snafu(implicit)]
626 location: Location,
627 },
628
629 #[snafu(display("Failed to initialize a watcher for file {}", path))]
630 FileWatch {
631 path: String,
632 #[snafu(source)]
633 error: notify::Error,
634 },
635
636 #[snafu(display("Timestamp overflow: {}", error))]
637 TimestampOverflow {
638 error: String,
639 #[snafu(implicit)]
640 location: Location,
641 },
642
643 #[snafu(display("Unsupported json data type for tag: {} {}", key, ty))]
644 UnsupportedJsonDataTypeForTag {
645 key: String,
646 ty: String,
647 #[snafu(implicit)]
648 location: Location,
649 },
650
651 #[snafu(display("Convert SQL value error"))]
652 ConvertSqlValue {
653 source: datatypes::error::Error,
654 #[snafu(implicit)]
655 location: Location,
656 },
657
658 #[snafu(display("Prepare statement not found: {}", name))]
659 PrepareStatementNotFound {
660 name: String,
661 #[snafu(implicit)]
662 location: Location,
663 },
664
665 #[snafu(display("Invalid elasticsearch input, reason: {}", reason))]
666 InvalidElasticsearchInput {
667 reason: String,
668 #[snafu(implicit)]
669 location: Location,
670 },
671
672 #[snafu(display("Invalid Jaeger query, reason: {}", reason))]
673 InvalidJaegerQuery {
674 reason: String,
675 #[snafu(implicit)]
676 location: Location,
677 },
678
679 #[snafu(display("DataFusion error"))]
680 DataFusion {
681 #[snafu(source)]
682 error: DataFusionError,
683 #[snafu(implicit)]
684 location: Location,
685 },
686
687 #[snafu(display("Failed to handle otel-arrow request, error message: {}", err_msg))]
688 HandleOtelArrowRequest {
689 err_msg: String,
690 #[snafu(implicit)]
691 location: Location,
692 },
693
694 #[snafu(display("Unknown hint: {}", hint))]
695 UnknownHint { hint: String },
696
697 #[snafu(display("Query has been cancelled"))]
698 Cancelled {
699 #[snafu(implicit)]
700 location: Location,
701 },
702
703 #[snafu(display("Service suspended"))]
704 Suspended {
705 #[snafu(implicit)]
706 location: Location,
707 },
708
709 #[snafu(transparent)]
710 Operator {
711 source: operator::error::Error,
712 #[snafu(implicit)]
713 location: Location,
714 },
715
716 #[snafu(transparent)]
717 GreptimeProto {
718 source: api::error::Error,
719 #[snafu(implicit)]
720 location: Location,
721 },
722
723 #[snafu(transparent)]
724 DataTypes {
725 source: datatypes::error::Error,
726 #[snafu(implicit)]
727 location: Location,
728 },
729
730 #[snafu(transparent)]
731 Partition {
732 source: partition::error::Error,
733 #[snafu(implicit)]
734 location: Location,
735 },
736
737 #[snafu(transparent)]
738 MetricEngine {
739 source: metric_engine::error::Error,
740 #[snafu(implicit)]
741 location: Location,
742 },
743
744 #[snafu(display("Failed to submit batch: {}", source))]
745 SubmitBatch { source: Arc<Error> },
746}
747
748pub type Result<T, E = Error> = std::result::Result<T, E>;
749
750impl ErrorExt for Error {
751 fn status_code(&self) -> StatusCode {
752 use Error::*;
753 match self {
754 Internal { .. }
755 | BatcherChannelClosed
756 | InternalIo { .. }
757 | TokioIo { .. }
758 | StartHttp { .. }
759 | StartGrpc { .. }
760 | TcpBind { .. }
761 | BuildHttpResponse { .. }
762 | Arrow { .. }
763 | FileWatch { .. } => StatusCode::Internal,
764
765 AddressBind { .. }
766 | AlreadyStarted { .. }
767 | InvalidPromRemoteReadQueryResult { .. }
768 | OtlpMetricModeIncompatible { .. } => StatusCode::IllegalState,
769
770 UnsupportedDataType { .. } => StatusCode::Unsupported,
771
772 #[cfg(not(windows))]
773 UpdateJemallocMetrics { .. } => StatusCode::Internal,
774
775 CollectRecordbatch { source, .. } => source.status_code(),
776
777 ExecuteQuery { source, .. }
778 | ExecutePlan { source, .. }
779 | ExecuteGrpcQuery { source, .. }
780 | ExecuteGrpcRequest { source, .. }
781 | CheckDatabaseValidity { source, .. } => source.status_code(),
782
783 Pipeline { source, .. } => source.status_code(),
784 CommonMeta { source, .. } => source.status_code(),
785
786 NotSupported { .. }
787 | InvalidParameter { .. }
788 | InvalidOtlpMetricInput { .. }
789 | InvalidQuery { .. }
790 | InfluxdbLineProtocol { .. }
791 | InvalidOpentsdbJsonRequest { .. }
792 | DecodePromRemoteRequest { .. }
793 | DecodeOtlpRequest { .. }
794 | DecodeLokiRequest { .. }
795 | UnsupportedJsonContentType { .. }
796 | CompressPromRemoteRequest { .. }
797 | DecompressSnappyPromRemoteRequest { .. }
798 | DecompressSnappyLokiRequest { .. }
799 | DecompressZstdPromRemoteRequest { .. }
800 | DecompressedBodyTooLarge { .. }
801 | InvalidPromRemoteRequest { .. }
802 | InvalidFlightTicket { .. }
803 | InvalidPrepareStatement { .. }
804 | InferParameterTypes { .. }
805 | DataFrame { .. }
806 | PreparedStmtTypeMismatch { .. }
807 | TimePrecision { .. }
808 | UrlDecode { .. }
809 | IncompatibleSchema { .. }
810 | MysqlValueConversion { .. }
811 | ParseJson { .. }
812 | InvalidLokiLabels { .. }
813 | InvalidLokiPayload { .. }
814 | UnsupportedContentType { .. }
815 | TimestampOverflow { .. }
816 | UnsupportedJsonDataTypeForTag { .. }
817 | InvalidTableName { .. }
818 | PrepareStatementNotFound { .. }
819 | FailedToParseQuery { .. }
820 | InvalidElasticsearchInput { .. }
821 | InvalidJaegerQuery { .. }
822 | ParseTimestamp { .. }
823 | UnknownHint { .. } => StatusCode::InvalidArguments,
824
825 Catalog { source, .. } => source.status_code(),
826 RowWriter { source, .. } => source.status_code(),
827 DataTypes { source, .. } => source.status_code(),
828
829 TlsRequired { .. } => StatusCode::Unknown,
830 Auth { source, .. } => source.status_code(),
831 DescribeStatement { source } => source.status_code(),
832
833 NotFoundAuthHeader { .. } | NotFoundInfluxAuth { .. } => StatusCode::AuthHeaderNotFound,
834 InvalidAuthHeaderInvisibleASCII { .. }
835 | UnsupportedAuthScheme { .. }
836 | InvalidAuthHeader { .. }
837 | InvalidBase64Value { .. }
838 | InvalidAuthHeaderInvalidUtf8Value { .. } => StatusCode::InvalidAuthHeader,
839
840 TableNotFound { .. } => StatusCode::TableNotFound,
841
842 #[cfg(feature = "mem-prof")]
843 DumpProfileData { source, .. } => source.status_code(),
844
845 InvalidUtf8Value { .. } | InvalidHeaderValue { .. } => StatusCode::InvalidArguments,
846
847 TooManyConcurrentRequests { .. } => StatusCode::RuntimeResourcesExhausted,
848
849 ParsePromQL { source, .. } => source.status_code(),
850 Other { source, .. } => source.status_code(),
851
852 UnexpectedResult { .. } => StatusCode::Unexpected,
853
854 JoinTask { error, .. } => {
855 if error.is_cancelled() {
856 StatusCode::Cancelled
857 } else if error.is_panic() {
858 StatusCode::Unexpected
859 } else {
860 StatusCode::Unknown
861 }
862 }
863
864 #[cfg(feature = "pprof")]
865 DumpPprof { source, .. } => source.status_code(),
866
867 ConvertScalarValue { source, .. } => source.status_code(),
868
869 ToJson { .. } | DataFusion { .. } => StatusCode::Internal,
870
871 ConvertSqlValue { source, .. } => source.status_code(),
872
873 HandleOtelArrowRequest { .. } => StatusCode::Internal,
874
875 Cancelled { .. } => StatusCode::Cancelled,
876
877 Suspended { .. } => StatusCode::Suspended,
878
879 MemoryLimitExceeded { .. } | WriteRejected { .. } => StatusCode::RateLimited,
880
881 GreptimeProto { source, .. } => source.status_code(),
882 Partition { source, .. } => source.status_code(),
883 MetricEngine { source, .. } => source.status_code(),
884 Operator { source, .. } => source.status_code(),
885 SubmitBatch { source, .. } => source.status_code(),
886 }
887 }
888
889 fn retry_hint(&self) -> RetryHint {
890 use Error::*;
891 match self {
892 ExecuteQuery { source, .. }
893 | ExecutePlan { source, .. }
894 | ExecuteGrpcQuery { source, .. }
895 | ExecuteGrpcRequest { source, .. }
896 | CheckDatabaseValidity { source, .. }
897 | DescribeStatement { source } => source.retry_hint(),
898
899 Pipeline { source, .. } => source.retry_hint(),
900 CommonMeta { source, .. } => source.retry_hint(),
901 Catalog { source, .. } => source.retry_hint(),
902 RowWriter { source, .. } => source.retry_hint(),
903 Auth { source, .. } => source.retry_hint(),
904
905 #[cfg(feature = "mem-prof")]
906 DumpProfileData { source, .. } => source.retry_hint(),
907
908 ParsePromQL { source, .. } => source.retry_hint(),
909 Other { source, .. } => source.retry_hint(),
910
911 #[cfg(feature = "pprof")]
912 DumpPprof { source, .. } => source.retry_hint(),
913
914 ConvertScalarValue { source, .. } => source.retry_hint(),
915 ConvertSqlValue { source, .. } => source.retry_hint(),
916 GreptimeProto { source, .. } => source.retry_hint(),
917 Partition { source, .. } => source.retry_hint(),
918 MetricEngine { source, .. } => source.retry_hint(),
919 Operator { source, .. } => source.retry_hint(),
920 SubmitBatch { source, .. } => source.retry_hint(),
921
922 MemoryLimitExceeded { source, .. } => source.retry_hint(),
923 CollectRecordbatch { source, .. } => source.retry_hint(),
924
925 TooManyConcurrentRequests { .. } | WriteRejected { .. } => RetryHint::Retryable,
926
927 _ => RetryHint::NonRetryable,
928 }
929 }
930
931 fn as_any(&self) -> &dyn Any {
932 self
933 }
934}
935
936define_into_tonic_status!(Error);
937
938impl From<std::io::Error> for Error {
939 fn from(e: std::io::Error) -> Self {
940 Error::InternalIo { error: e }
941 }
942}
943
944fn log_error_if_necessary(error: &Error) {
945 if error.status_code().should_log_error() {
946 error!(error; "Failed to handle HTTP request ");
947 } else {
948 warn!(error; "Failed to handle HTTP request ");
949 }
950}
951
952impl IntoResponse for Error {
953 fn into_response(self) -> Response {
954 let error_msg = self.output_msg();
955 let status = status_code_to_http_status(&self.status_code());
956
957 log_error_if_necessary(&self);
958
959 let body = Json(json!({
960 "error": error_msg,
961 }));
962 (status, body).into_response()
963 }
964}
965
966pub fn status_code_to_http_status(status_code: &StatusCode) -> HttpStatusCode {
968 match status_code {
969 StatusCode::Success => HttpStatusCode::OK,
970
971 StatusCode::Cancelled | StatusCode::DeadlineExceeded => HttpStatusCode::GATEWAY_TIMEOUT,
974
975 StatusCode::Unsupported
976 | StatusCode::InvalidArguments
977 | StatusCode::InvalidSyntax
978 | StatusCode::RequestOutdated
979 | StatusCode::RegionAlreadyExists
980 | StatusCode::TableColumnExists
981 | StatusCode::TableAlreadyExists
982 | StatusCode::RegionNotFound
983 | StatusCode::DatabaseNotFound
984 | StatusCode::TableNotFound
985 | StatusCode::TableColumnNotFound
986 | StatusCode::PlanQuery
987 | StatusCode::DatabaseAlreadyExists
988 | StatusCode::TriggerAlreadyExists
989 | StatusCode::TriggerNotFound
990 | StatusCode::FlowNotFound
991 | StatusCode::FlowAlreadyExists => HttpStatusCode::BAD_REQUEST,
992
993 StatusCode::AuthHeaderNotFound
994 | StatusCode::InvalidAuthHeader
995 | StatusCode::UserNotFound
996 | StatusCode::UnsupportedPasswordType
997 | StatusCode::UserPasswordMismatch
998 | StatusCode::RegionReadonly => HttpStatusCode::UNAUTHORIZED,
999
1000 StatusCode::PermissionDenied | StatusCode::AccessDenied => HttpStatusCode::FORBIDDEN,
1001
1002 StatusCode::RateLimited => HttpStatusCode::TOO_MANY_REQUESTS,
1003
1004 StatusCode::RegionNotReady
1005 | StatusCode::TableUnavailable
1006 | StatusCode::RegionBusy
1007 | StatusCode::StorageUnavailable
1008 | StatusCode::External
1009 | StatusCode::Suspended => HttpStatusCode::SERVICE_UNAVAILABLE,
1010
1011 StatusCode::Internal
1012 | StatusCode::Unexpected
1013 | StatusCode::IllegalState
1014 | StatusCode::Unknown
1015 | StatusCode::RuntimeResourcesExhausted
1016 | StatusCode::EngineExecuteQuery => HttpStatusCode::INTERNAL_SERVER_ERROR,
1017 }
1018}
1019
1020#[cfg(test)]
1021mod tests {
1022 use common_error::GREPTIME_DB_HEADER_ERROR_CODE;
1023 use common_error::ext::PlainError;
1024
1025 use super::*;
1026
1027 #[test]
1028 fn collect_recordbatch_preserves_poll_stream_status_in_tonic_status() {
1029 let error = Error::CollectRecordbatch {
1030 source: common_recordbatch::error::Error::PollStream {
1031 error: DataFusionError::External(Box::new(BoxedError::new(PlainError::new(
1032 "neutral error".to_string(),
1033 StatusCode::RequestOutdated,
1034 )))),
1035 location: Location::default(),
1036 },
1037 location: Location::default(),
1038 };
1039
1040 let status: tonic::Status = error.into();
1041 assert_eq!(status.code(), tonic::Code::InvalidArgument);
1042 assert_eq!(
1043 status
1044 .metadata()
1045 .get(GREPTIME_DB_HEADER_ERROR_CODE)
1046 .unwrap()
1047 .to_str()
1048 .unwrap(),
1049 (StatusCode::RequestOutdated as u32).to_string()
1050 );
1051 }
1052}