1mod codec;
16
17use std::borrow::Borrow;
18use std::sync::Arc;
19use std::time::Duration;
20
21use api::region::RegionResponse;
22use api::v1::ResponseHeader;
23use api::v1::region::{
24 RegionRequest, RegionRequestHeader, RemoteDynFilterRequest, RemoteDynFilterUnregister,
25 RemoteDynFilterUpdate, region_request, remote_dyn_filter_request,
26};
27use arc_swap::ArcSwapOption;
28use arrow_flight::Ticket;
29use async_stream::stream;
30use async_trait::async_trait;
31use common_error::ext::{BoxedError, ErrorExt};
32use common_error::status_code::StatusCode;
33use common_grpc::flight::{FlightDecoder, FlightMessage};
34use common_meta::error::{self as meta_error, Result as MetaResult};
35use common_meta::node_manager::Datanode;
36use common_query::request::QueryRequest;
37use common_recordbatch::error::ExternalSnafu;
38use common_recordbatch::{RecordBatch, RecordBatchStreamWrapper, SendableRecordBatchStream};
39use common_telemetry::error;
40use common_telemetry::tracing::Span;
41use common_telemetry::tracing_context::TracingContext;
42use futures_util::Stream;
43use prost::Message;
44use query::query_engine::DefaultSerializer;
45use snafu::{OptionExt, ResultExt, location};
46use substrait::{DFLogicalSubstraitConvertor, SubstraitPlan};
47use tokio_stream::StreamExt;
48use tonic::codec::CompressionEncoding;
49
50use crate::error::{
51 self, FlightGetSnafu, IllegalDatabaseResponseSnafu, IllegalFlightMessagesSnafu,
52 MissingFieldSnafu, Result, ServerSnafu,
53};
54use crate::flight::{FlightMessageReader, decode_flight_data};
55use crate::region::codec::RegionCodec;
56use crate::{Client, metrics};
57
58const FLIGHT_DO_GET_TIMEOUT: Duration = Duration::from_secs(10);
59
60#[derive(Debug)]
62pub struct RegionRequester {
63 client: Client,
64 send_compression: bool,
65 accept_compression: bool,
66}
67
68#[async_trait]
69impl Datanode for RegionRequester {
70 async fn handle_request(&self, request: Arc<RegionRequest>) -> MetaResult<RegionResponse> {
71 self.handle_inner(request).await.map_err(Into::into)
72 }
73
74 async fn handle_query(&self, request: QueryRequest) -> MetaResult<SendableRecordBatchStream> {
75 let plan = DFLogicalSubstraitConvertor
76 .encode(&request.plan, DefaultSerializer)
77 .map_err(BoxedError::new)
78 .context(meta_error::ExternalSnafu)?
79 .to_vec();
80 let request = api::v1::region::QueryRequest {
81 header: request.header,
82 region_id: request.region_id.as_u64(),
83 plan,
84 };
85
86 let ticket = Ticket {
87 ticket: request.encode_to_vec().into(),
88 };
89 self.do_get_inner(ticket)
90 .await
91 .map_err(BoxedError::new)
92 .context(meta_error::ExternalSnafu)
93 }
94}
95
96impl RegionRequester {
97 pub fn new(client: Client, send_compression: bool, accept_compression: bool) -> Self {
98 Self {
99 client,
100 send_compression,
101 accept_compression,
102 }
103 }
104
105 pub async fn do_get_inner(&self, ticket: Ticket) -> Result<SendableRecordBatchStream> {
106 let mut flight_client = self
107 .client
108 .make_flight_client(self.send_compression, self.accept_compression)?;
109 let addr = flight_client.addr().to_string();
111 let mut request = tonic::Request::new(ticket);
112 request.set_timeout(FLIGHT_DO_GET_TIMEOUT);
113 let response = flight_client
114 .mut_inner()
115 .do_get(request)
116 .await
117 .or_else(|e| {
118 let tonic_code = e.code();
119 let e: error::Error = e.into();
120 error!(
121 e; "Failed to do Flight get, addr: {}, code: {}",
122 addr,
123 tonic_code
124 );
125 Err(BoxedError::new(e)).with_context(|_| FlightGetSnafu {
126 addr: addr.clone(),
127 tonic_code,
128 })
129 })?;
130
131 let flight_data_stream = response.into_inner();
132 let mut decoder = FlightDecoder::default();
133
134 let flight_message_stream = flight_data_stream
135 .filter_map(move |flight_data| decode_flight_data(&mut decoder, flight_data));
136
137 recordbatches_from_flight_message_stream(addr, flight_message_stream).await
138 }
139
140 pub async fn handle<R>(&self, request: R) -> Result<RegionResponse>
146 where
147 R: Borrow<RegionRequest> + Send + Sync + 'static,
148 {
149 self.handle_inner(request).await
150 }
151
152 pub async fn handle_remote_dyn_filter_update(
154 &self,
155 query_id: impl Into<String>,
156 update: RemoteDynFilterUpdate,
157 ) -> Result<RegionResponse> {
158 self.handle_inner(build_remote_dyn_filter_update_request(query_id, update))
159 .await
160 }
161
162 pub async fn handle_remote_dyn_filter_unregister(
164 &self,
165 query_id: impl Into<String>,
166 unregister: RemoteDynFilterUnregister,
167 ) -> Result<RegionResponse> {
168 self.handle_inner(build_remote_dyn_filter_unregister_request(
169 query_id, unregister,
170 ))
171 .await
172 }
173
174 async fn handle_inner<R>(&self, request: R) -> Result<RegionResponse>
175 where
176 R: Borrow<RegionRequest> + Send + Sync + 'static,
177 {
178 let request_body = request
179 .borrow()
180 .body
181 .as_ref()
182 .context(MissingFieldSnafu { field: "body" })?;
183 let is_insert = matches!(
184 request_body,
185 region_request::Body::Inserts(_) | region_request::Body::BulkInsert(_)
186 );
187 let request_type = request_body.as_ref().to_string();
188 let _timer = metrics::METRIC_REGION_REQUEST_GRPC
189 .with_label_values(&[request_type.as_str()])
190 .start_timer();
191
192 let (addr, mut client) = self.client.raw_region_client()?;
193 if is_insert {
194 if self.send_compression {
195 client = client.send_compressed(CompressionEncoding::Zstd);
196 }
197 if self.accept_compression {
198 client = client.accept_compressed(CompressionEncoding::Zstd);
199 }
200 }
201
202 let response = async {
203 client
204 .ready()
205 .await
206 .map_err(|e| tonic::Status::unknown(format!("Service was not ready: {e}")))?;
207 let mut request = tonic::Request::new(request);
208 request.extensions_mut().insert(tonic::GrpcMethod::new(
209 "greptime.v1.region.Region",
210 "Handle",
211 ));
212 client
213 .unary(
214 request,
215 tonic::codegen::http::uri::PathAndQuery::from_static(
216 "/greptime.v1.region.Region/Handle",
217 ),
218 RegionCodec::default(),
219 )
220 .await
221 }
222 .await
223 .map_err(|status| map_region_error(status, addr))?
224 .into_inner();
225
226 check_response_header(&response.header)?;
227
228 Ok(RegionResponse::from_region_response(response))
229 }
230}
231
232fn map_region_error(status: tonic::Status, addr: String) -> error::Error {
233 error::Error::RegionServer {
234 addr,
235 code: status.code(),
236 source: BoxedError::new(error::Error::from(status)),
237 location: location!(),
238 }
239}
240
241async fn recordbatches_from_flight_message_stream<S>(
242 addr: String,
243 flight_message_stream: S,
244) -> Result<SendableRecordBatchStream>
245where
246 S: Stream<Item = Result<FlightMessage>> + Send + Unpin + 'static,
247{
248 let mut reader = FlightMessageReader::new(addr.clone(), flight_message_stream);
249 let FlightMessage::Schema(schema) = reader
250 .read_first()
251 .await
252 .map_err(|error| flight_stream_error(reader.remote_addr(), error))?
253 else {
254 return IllegalFlightMessagesSnafu {
255 reason: "Expect schema to be the first flight message",
256 }
257 .fail()
258 .map_err(|error| flight_stream_error(reader.remote_addr(), error));
259 };
260
261 let metrics = Arc::new(ArcSwapOption::from(None));
262 let metrics_ref = metrics.clone();
263
264 let tracing_context = TracingContext::from_current_span();
265
266 let schema =
267 Arc::new(datatypes::schema::Schema::try_from(schema).context(error::ConvertSchemaSnafu)?);
268 let schema_cloned = schema.clone();
269 let stream_addr = addr;
270 let stream = Box::pin(stream!({
271 let _span = tracing_context.attach(common_telemetry::tracing::info_span!(
272 "poll_flight_data_stream"
273 ));
274
275 loop {
276 let flight_message = match reader.read_next().await {
277 Ok(Some(message)) => message,
278 Ok(None) => break,
279 Err(error) => {
280 yield Err(BoxedError::new(flight_stream_error(&stream_addr, error)))
281 .context(ExternalSnafu);
282 break;
283 }
284 };
285
286 match flight_message {
287 FlightMessage::RecordBatch(record_batch) => {
288 yield Ok(RecordBatch::from_df_record_batch(
292 schema_cloned.clone(),
293 record_batch,
294 ));
295 }
296 FlightMessage::Metrics(s) => {
297 match serde_json::from_str(&s) {
299 Ok(metrics) => {
300 metrics_ref.swap(Some(Arc::new(metrics)));
301 }
302 Err(error) => {
303 common_telemetry::warn!(
304 "Failed to decode region Flight metrics: {}",
305 error
306 );
307 }
308 }
309 continue;
310 }
311 _ => {
312 yield IllegalFlightMessagesSnafu {
313 reason: "A Schema message must be succeeded exclusively by a set of RecordBatch messages"
314 }
315 .fail()
316 .map_err(BoxedError::new)
317 .context(ExternalSnafu);
318 break;
319 }
320 }
321 }
322 }));
323 let record_batch_stream = RecordBatchStreamWrapper {
324 schema,
325 stream,
326 output_ordering: None,
327 metrics,
328 span: Span::current(),
329 };
330 Ok(Box::pin(record_batch_stream))
331}
332
333fn flight_stream_error(addr: &str, error: error::Error) -> error::Error {
334 let tonic_code = error.tonic_code().unwrap_or(tonic::Code::Unknown);
335 if error.status_code().should_log_error() {
336 error!(
337 error; "Failed to receive Flight data, addr: {}, code: {}",
338 addr,
339 tonic_code
340 );
341 }
342
343 error::Error::FlightGet {
344 addr: addr.to_string(),
345 tonic_code,
346 source: BoxedError::new(error),
347 }
348}
349
350pub fn build_remote_dyn_filter_update_request(
351 query_id: impl Into<String>,
352 update: RemoteDynFilterUpdate,
353) -> RegionRequest {
354 build_remote_dyn_filter_request(
355 query_id.into(),
356 remote_dyn_filter_request::Action::Update(update),
357 )
358}
359
360pub fn build_remote_dyn_filter_unregister_request(
361 query_id: impl Into<String>,
362 unregister: RemoteDynFilterUnregister,
363) -> RegionRequest {
364 build_remote_dyn_filter_request(
365 query_id.into(),
366 remote_dyn_filter_request::Action::Unregister(unregister),
367 )
368}
369
370fn build_remote_dyn_filter_request(
371 query_id: String,
372 action: remote_dyn_filter_request::Action,
373) -> RegionRequest {
374 RegionRequest {
375 header: Some(RegionRequestHeader {
376 tracing_context: TracingContext::from_current_span().to_w3c(),
377 ..Default::default()
378 }),
379 body: Some(region_request::Body::RemoteDynFilter(
380 RemoteDynFilterRequest {
381 query_id,
382 action: Some(action),
383 },
384 )),
385 }
386}
387
388pub fn check_response_header(header: &Option<ResponseHeader>) -> Result<()> {
389 let status = header
390 .as_ref()
391 .and_then(|header| header.status.as_ref())
392 .context(IllegalDatabaseResponseSnafu {
393 err_msg: "either response header or status is missing",
394 })?;
395
396 if StatusCode::is_success(status.status_code) {
397 Ok(())
398 } else {
399 let code =
400 StatusCode::from_u32(status.status_code).context(IllegalDatabaseResponseSnafu {
401 err_msg: format!("unknown server status: {:?}", status),
402 })?;
403 ServerSnafu {
404 code,
405 msg: status.err_msg.clone(),
406 }
407 .fail()
408 }
409}
410
411#[cfg(test)]
412#[allow(deprecated)]
413mod tests {
414 use std::collections::VecDeque;
415 use std::sync::Mutex;
416
417 use api::v1::region::region_server::{Region, RegionServer};
418 use api::v1::region::{
419 BulkInsertRequest, InsertRequest, InsertRequests, RegionResponse as PbRegionResponse,
420 RemoteDynFilterUnregister, RemoteDynFilterUpdate, region_request,
421 remote_dyn_filter_request,
422 };
423 use api::v1::value::ValueData;
424 use api::v1::{
425 ColumnDataType, ColumnSchema as PbColumnSchema, Row, Rows, Status as PbStatus, Value,
426 };
427 use common_error::ext::RetryHint;
428 use common_grpc::channel_manager::{ChannelConfig, ChannelManager};
429 use common_recordbatch::adapter::RecordBatchMetrics;
430 use datatypes::arrow::array::Int32Array;
431 use datatypes::prelude::{ConcreteDataType, VectorRef};
432 use datatypes::schema::{ColumnSchema, Schema};
433 use datatypes::vectors::Int32Vector;
434 use futures_util::stream;
435 use tokio::net::TcpListener;
436 use tokio::task::JoinHandle;
437 use tokio_stream::wrappers::TcpListenerStream;
438 use tonic::codec::CompressionEncoding;
439 use tonic::{Request, Response, Status};
440
441 use super::*;
442 use crate::Error;
443
444 #[derive(Clone, Default)]
445 struct RecordingRegionService {
446 requests: Arc<Mutex<Vec<(RegionRequest, bool, bool)>>>,
447 responses: Arc<Mutex<VecDeque<std::result::Result<PbRegionResponse, Status>>>>,
448 }
449
450 #[tonic::async_trait]
451 impl Region for RecordingRegionService {
452 async fn handle(
453 &self,
454 request: Request<RegionRequest>,
455 ) -> std::result::Result<Response<PbRegionResponse>, Status> {
456 let metadata = request.metadata();
457 let sends_zstd = metadata.get("grpc-encoding").is_some_and(|v| v == "zstd");
458 let accepts_zstd = metadata
459 .get("grpc-accept-encoding")
460 .and_then(|v| v.to_str().ok())
461 .is_some_and(|v| v.split(',').any(|encoding| encoding == "zstd"));
462 self.requests
463 .lock()
464 .unwrap()
465 .push((request.into_inner(), sends_zstd, accepts_zstd));
466 self.responses
467 .lock()
468 .unwrap()
469 .pop_front()
470 .unwrap_or_else(|| Ok(build_response(StatusCode::Success)))
471 .map(Response::new)
472 }
473 }
474
475 struct RegionTestServer {
476 addr: String,
477 service: RecordingRegionService,
478 task: JoinHandle<()>,
479 }
480
481 impl RegionTestServer {
482 async fn start() -> Self {
483 let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
484 let addr = listener.local_addr().unwrap().to_string();
485 let service = RecordingRegionService::default();
486 let server_service = service.clone();
487 let task = tokio::spawn(async move {
488 tonic::transport::Server::builder()
489 .add_service(
490 RegionServer::new(server_service)
491 .accept_compressed(CompressionEncoding::Zstd)
492 .send_compressed(CompressionEncoding::Zstd),
493 )
494 .serve_with_incoming(TcpListenerStream::new(listener))
495 .await
496 .unwrap();
497 });
498 Self {
499 addr,
500 service,
501 task,
502 }
503 }
504
505 fn create_requester(
506 &self,
507 config: ChannelConfig,
508 send: bool,
509 accept: bool,
510 ) -> RegionRequester {
511 RegionRequester::new(
512 Client::with_manager_and_urls(
513 ChannelManager::with_config(config, None),
514 [self.addr.clone()],
515 ),
516 send,
517 accept,
518 )
519 }
520 }
521
522 impl Drop for RegionTestServer {
523 fn drop(&mut self) {
524 self.task.abort();
525 }
526 }
527
528 fn build_response(code: StatusCode) -> PbRegionResponse {
529 PbRegionResponse {
530 header: Some(ResponseHeader {
531 status: Some(PbStatus {
532 status_code: code as u32,
533 ..Default::default()
534 }),
535 }),
536 affected_rows: 1,
537 ..Default::default()
538 }
539 }
540
541 fn build_insert_request() -> RegionRequest {
542 RegionRequest {
543 header: Some(RegionRequestHeader {
544 dbname: "shared-request-test".to_string(),
545 ..Default::default()
546 }),
547 body: Some(region_request::Body::Inserts(InsertRequests {
548 requests: vec![InsertRequest {
549 region_id: 42,
550 rows: Some(Rows {
551 schema: vec![
552 PbColumnSchema {
553 column_name: "message".to_string(),
554 datatype: ColumnDataType::String as i32,
555 ..Default::default()
556 },
557 PbColumnSchema {
558 column_name: "payload".to_string(),
559 datatype: ColumnDataType::Binary as i32,
560 ..Default::default()
561 },
562 ],
563 rows: vec![Row {
564 values: vec![
565 Value {
566 value_data: Some(ValueData::StringValue(
567 "hello 世界".to_string(),
568 )),
569 },
570 Value {
571 value_data: Some(ValueData::BinaryValue(vec![0, 1, 127, 255])),
572 },
573 ],
574 }],
575 }),
576 ..Default::default()
577 }],
578 })),
579 }
580 }
581
582 #[tokio::test]
583 async fn test_owned_and_shared_requests_preserve_payload_and_compression() {
584 let server = RegionTestServer::start().await;
585 let shared = Arc::new(build_insert_request());
586 for (send, accept) in [(false, false), (true, false), (false, true), (true, true)] {
587 let requester = server.create_requester(ChannelConfig::default(), send, accept);
588 let channel =
589 tonic::transport::Endpoint::from_shared(format!("http://{}", server.addr))
590 .unwrap()
591 .connect()
592 .await
593 .unwrap();
594 let mut baseline = api::v1::region::region_client::RegionClient::new(channel);
595 if send {
596 baseline = baseline.send_compressed(CompressionEncoding::Zstd);
597 }
598 if accept {
599 baseline = baseline.accept_compressed(CompressionEncoding::Zstd);
600 }
601 assert_eq!(
602 baseline
603 .handle(build_insert_request())
604 .await
605 .unwrap()
606 .into_inner()
607 .affected_rows,
608 1
609 );
610 assert_eq!(
611 requester
612 .handle(build_insert_request())
613 .await
614 .unwrap()
615 .affected_rows,
616 1
617 );
618 assert_eq!(
619 requester
620 .handle(Arc::clone(&shared))
621 .await
622 .unwrap()
623 .affected_rows,
624 1
625 );
626 }
627 let requests = server.service.requests.lock().unwrap();
628 assert_eq!(requests.len(), 12);
629 for ((request, sends, accepts), (send, accept)) in requests.iter().zip(
630 [(false, false), (true, false), (false, true), (true, true)]
631 .into_iter()
632 .flat_map(|settings| [settings, settings, settings]),
633 ) {
634 assert_eq!(request, shared.as_ref());
635 assert_eq!((*sends, *accepts), (send, accept));
636 }
637 }
638
639 #[tokio::test]
640 async fn test_shared_request_can_be_resent_after_transport_error() {
641 let server = RegionTestServer::start().await;
642 let mut status = Status::unavailable("migrating");
643 status.metadata_mut().insert(
644 common_error::GREPTIME_DB_HEADER_ERROR_RETRY_HINT,
645 RetryHint::Retryable.as_str().parse().unwrap(),
646 );
647 server
648 .service
649 .responses
650 .lock()
651 .unwrap()
652 .push_back(Err(status));
653 let requester = server.create_requester(ChannelConfig::default(), false, false);
654 let shared = Arc::new(build_insert_request());
655 let error = requester.handle(Arc::clone(&shared)).await.unwrap_err();
656 assert_eq!(error.retry_hint(), RetryHint::Retryable);
657 assert!(
658 matches!(error, Error::RegionServer { addr, code: tonic::Code::Unavailable, .. } if addr == server.addr)
659 );
660 requester.handle(Arc::clone(&shared)).await.unwrap();
661 let requests = server.service.requests.lock().unwrap();
662 assert_eq!(requests.len(), 2);
663 assert_eq!(&requests[0].0, shared.as_ref());
664 assert_eq!(&requests[1].0, shared.as_ref());
665 }
666
667 #[test]
668 fn test_retry_hint_wire_compatibility() {
669 for (name, wire_hint, expected_hint) in [
670 ("legacy_missing", None, RetryHint::NonRetryable),
671 ("legacy_retryable", Some("retryable"), RetryHint::Retryable),
672 (
673 "legacy_non_retryable",
674 Some("non_retryable"),
675 RetryHint::NonRetryable,
676 ),
677 (
678 "route_refresh",
679 Some("retry_after_route_refresh"),
680 RetryHint::RetryAfterRouteRefresh,
681 ),
682 (
683 "unknown_future_hint",
684 Some("future_retry_hint"),
685 RetryHint::NonRetryable,
686 ),
687 ] {
688 let mut status = Status::failed_precondition("region unavailable");
689 status.metadata_mut().insert(
690 common_error::GREPTIME_DB_HEADER_ERROR_CODE,
691 (StatusCode::RegionNotReady as u32)
692 .to_string()
693 .parse()
694 .unwrap(),
695 );
696 if let Some(hint) = wire_hint {
697 status.metadata_mut().insert(
698 common_error::GREPTIME_DB_HEADER_ERROR_RETRY_HINT,
699 hint.parse().unwrap(),
700 );
701 }
702 let error: Error = status.into();
703 assert_eq!(error.retry_hint(), expected_hint, "{name}");
704 assert_eq!(error.status_code(), StatusCode::RegionNotReady, "{name}");
705 assert_eq!(
706 error.tonic_code(),
707 Some(tonic::Code::FailedPrecondition),
708 "{name}"
709 );
710 }
711 }
712
713 #[tokio::test]
714 async fn test_transport_error_parity_and_datanode_mapping() {
715 let server = RegionTestServer::start().await;
716 let requester = server.create_requester(ChannelConfig::default(), false, false);
717 for hint in [
718 RetryHint::Retryable,
719 RetryHint::RetryAfterRouteRefresh,
720 RetryHint::NonRetryable,
721 ] {
722 let mut status = Status::failed_precondition("region unavailable");
723 status.metadata_mut().insert(
724 common_error::GREPTIME_DB_HEADER_ERROR_RETRY_HINT,
725 hint.as_str().parse().unwrap(),
726 );
727 status.metadata_mut().insert(
728 common_error::GREPTIME_DB_HEADER_ERROR_CODE,
729 (StatusCode::RegionNotFound as u32)
730 .to_string()
731 .parse()
732 .unwrap(),
733 );
734 server
735 .service
736 .responses
737 .lock()
738 .unwrap()
739 .extend((0..4).map(|_| Err(status.clone())));
740 let mut baseline = api::v1::region::region_client::RegionClient::connect(format!(
741 "http://{}",
742 server.addr
743 ))
744 .await
745 .unwrap();
746 let baseline: Error = baseline
747 .handle(build_insert_request())
748 .await
749 .unwrap_err()
750 .into();
751 assert_eq!(baseline.status_code(), StatusCode::RegionNotFound);
752 assert_eq!(baseline.retry_hint(), hint);
753 assert_eq!(baseline.tonic_code(), Some(tonic::Code::FailedPrecondition));
754 let owned = requester.handle(build_insert_request()).await.unwrap_err();
755 let shared = requester
756 .handle(Arc::new(build_insert_request()))
757 .await
758 .unwrap_err();
759 for error in [owned, shared] {
760 assert_eq!(error.status_code(), baseline.status_code());
761 assert_eq!(error.retry_hint(), baseline.retry_hint());
762 assert_eq!(error.tonic_code(), baseline.tonic_code());
763 }
764 let datanode: &dyn Datanode = &requester;
765 let error = datanode.handle(build_insert_request()).await.unwrap_err();
766 assert_eq!(error.status_code(), StatusCode::RegionNotFound);
767 assert_eq!(error.retry_hint(), hint);
768 assert!(matches!(error, meta_error::Error::External { .. }));
769 }
770 }
771
772 #[tokio::test]
773 async fn test_datanode_transport_retry_hints() {
774 let server = RegionTestServer::start().await;
775 let requester = server.create_requester(ChannelConfig::default(), false, false);
776 let datanode: &dyn Datanode = &requester;
777 let statuses = [
778 tonic::Code::Unavailable,
779 tonic::Code::Cancelled,
780 tonic::Code::DeadlineExceeded,
781 tonic::Code::FailedPrecondition,
782 ]
783 .into_iter()
784 .flat_map(|code| {
785 [
786 None,
787 Some(RetryHint::NonRetryable),
788 Some(RetryHint::Retryable),
789 Some(RetryHint::RetryAfterRouteRefresh),
790 ]
791 .into_iter()
792 .map(move |hint| (code, hint))
793 });
794 for (code, hint) in statuses {
795 for body in [
796 region_request::Body::Inserts(Default::default()),
797 region_request::Body::Deletes(Default::default()),
798 region_request::Body::BulkInsert(Default::default()),
799 region_request::Body::Create(Default::default()),
800 region_request::Body::RemoteDynFilter(Default::default()),
801 ] {
802 let request_type = body.as_ref().to_string();
803 let mut status = Status::new(code, "request failed");
804 status.metadata_mut().insert(
806 common_error::GREPTIME_DB_HEADER_ERROR_CODE,
807 (StatusCode::RegionNotReady as u32)
808 .to_string()
809 .parse()
810 .unwrap(),
811 );
812 if let Some(hint) = hint {
813 status.metadata_mut().insert(
814 common_error::GREPTIME_DB_HEADER_ERROR_RETRY_HINT,
815 hint.as_str().parse().unwrap(),
816 );
817 }
818 let original = Error::from(status.clone());
819 let expected = if original.should_retry() && !original.retry_hint().is_retryable() {
820 RetryHint::Retryable
821 } else {
822 original.retry_hint()
823 };
824 server
825 .service
826 .responses
827 .lock()
828 .unwrap()
829 .extend([Err(status.clone()), Err(status)]);
830 let request = RegionRequest {
831 header: None,
832 body: Some(body),
833 };
834 let shared = Arc::new(request);
835 let shared_error = datanode.handle(Arc::clone(&shared)).await.unwrap_err();
836 assert_eq!(Arc::strong_count(&shared), 1);
837 let request = Arc::try_unwrap(shared).unwrap();
838 let owned_error = datanode.handle(request).await.unwrap_err();
839 for error in [owned_error, shared_error] {
840 assert_eq!(
841 error.retry_hint(),
842 expected,
843 "{code:?}, {hint:?}, {request_type}"
844 );
845 assert_eq!(error.retry_hint().is_retryable(), original.should_retry());
846 assert_eq!(error.status_code(), StatusCode::RegionNotReady);
847 let meta_error::Error::External { source, .. } = error else {
848 panic!("expected an external error");
849 };
850 assert_eq!(source.retry_hint(), expected);
851 let Error::RegionServer {
852 source,
853 code: actual_code,
854 ..
855 } = source.as_any().downcast_ref::<Error>().unwrap()
856 else {
857 panic!("expected a region server error");
858 };
859 assert_eq!(*actual_code, code);
860 assert_eq!(source.retry_hint(), original.retry_hint());
861 assert_eq!(source.to_string(), original.to_string());
862 }
863 }
864 }
865 }
866
867 #[tokio::test]
868 async fn test_shared_request_preserves_response_header_errors() {
869 let server = RegionTestServer::start().await;
870 server.service.responses.lock().unwrap().extend([
871 Ok(PbRegionResponse::default()),
872 Ok(build_response(StatusCode::RegionNotFound)),
873 ]);
874 let requester = server.create_requester(ChannelConfig::default(), false, false);
875 let shared = Arc::new(build_insert_request());
876 assert!(matches!(
877 requester.handle(Arc::clone(&shared)).await.unwrap_err(),
878 Error::IllegalDatabaseResponse { .. }
879 ));
880 assert!(matches!(
881 requester.handle(shared).await.unwrap_err(),
882 Error::Server {
883 code: StatusCode::RegionNotFound,
884 ..
885 }
886 ));
887 }
888
889 #[tokio::test]
890 async fn test_shared_request_respects_configured_message_limits() {
891 let server = RegionTestServer::start().await;
892 let shared = Arc::new(build_insert_request());
893 for send_limit in [true, false] {
894 let mut config = ChannelConfig::default();
895 if send_limit {
896 config.max_send_message_size = "1B".parse().unwrap();
897 } else {
898 config.max_recv_message_size = "1B".parse().unwrap();
899 }
900 let mut baseline = api::v1::region::region_client::RegionClient::connect(format!(
901 "http://{}",
902 server.addr
903 ))
904 .await
905 .unwrap()
906 .max_encoding_message_size(config.max_send_message_size.as_bytes() as usize)
907 .max_decoding_message_size(config.max_recv_message_size.as_bytes() as usize);
908 let baseline: Error = baseline
909 .handle(build_insert_request())
910 .await
911 .unwrap_err()
912 .into();
913 if !send_limit {
914 assert_eq!(
915 baseline.tonic_code(),
916 Some(tonic::Code::OutOfRange),
917 "{baseline:?}"
918 );
919 }
920 let requester = server.create_requester(config, false, false);
921 let error = requester.handle(Arc::clone(&shared)).await.unwrap_err();
922 assert_eq!(
923 error.tonic_code(),
924 baseline.tonic_code(),
925 "shared: {error:?}; generated: {baseline:?}"
926 );
927 assert_eq!(
928 error.status_code(),
929 baseline.status_code(),
930 "shared: {error:?}; generated: {baseline:?}"
931 );
932 let Error::RegionServer { source, .. } = &error else {
933 panic!("expected a region server error: {error:?}");
934 };
935 assert_eq!(
936 source.retry_hint(),
937 baseline.retry_hint(),
938 "shared: {error:?}; generated: {baseline:?}"
939 );
940 assert_eq!(
941 server.service.requests.lock().unwrap().len(),
942 if send_limit { 0 } else { 2 }
943 );
944 }
945 let requester = server.create_requester(ChannelConfig::default(), false, false);
946 requester.handle(shared).await.unwrap();
947 }
948
949 #[tokio::test]
950 async fn test_inserts_and_bulk_insert_use_transport_compression() {
951 let server = RegionTestServer::start().await;
952 let requester = server.create_requester(ChannelConfig::default(), true, true);
953 let send_only_requester = server.create_requester(ChannelConfig::default(), true, false);
954 let accept_only_requester = server.create_requester(ChannelConfig::default(), false, true);
955 let disabled_requester = server.create_requester(ChannelConfig::default(), false, false);
956 let inserts = || RegionRequest {
957 body: Some(region_request::Body::Inserts(Default::default())),
958 ..Default::default()
959 };
960 let bulk_insert = || RegionRequest {
961 body: Some(region_request::Body::BulkInsert(
962 BulkInsertRequest::default(),
963 )),
964 ..Default::default()
965 };
966
967 requester.handle(inserts()).await.unwrap();
968 send_only_requester.handle(inserts()).await.unwrap();
969 accept_only_requester.handle(inserts()).await.unwrap();
970 requester.handle(bulk_insert()).await.unwrap();
971 disabled_requester.handle(bulk_insert()).await.unwrap();
972 requester
973 .handle(build_remote_dyn_filter_unregister_request(
974 "query-1",
975 RemoteDynFilterUnregister {
976 filter_id: "filter-1".to_string(),
977 },
978 ))
979 .await
980 .unwrap();
981
982 assert_eq!(
983 vec![
984 (true, true),
985 (true, false),
986 (false, true),
987 (true, true),
988 (false, false),
989 (false, false)
990 ],
991 server
992 .service
993 .requests
994 .lock()
995 .unwrap()
996 .iter()
997 .map(|(_, send, accept)| (*send, *accept))
998 .collect::<Vec<_>>()
999 );
1000 }
1001
1002 #[test]
1003 fn test_flight_stream_error_preserves_peer_address() {
1004 let error = flight_stream_error(
1005 "127.0.0.1:4001",
1006 tonic::Status::unavailable("datanode unavailable").into(),
1007 );
1008
1009 assert!(matches!(
1010 error,
1011 error::Error::FlightGet {
1012 addr,
1013 tonic_code: tonic::Code::Unavailable,
1014 ..
1015 } if addr == "127.0.0.1:4001"
1016 ));
1017 }
1018
1019 #[tokio::test]
1020 async fn test_empty_flight_stream_preserves_peer_address() {
1021 let Err(error) = recordbatches_from_flight_message_stream(
1022 "127.0.0.1:4001".to_string(),
1023 stream::empty::<Result<FlightMessage>>(),
1024 )
1025 .await
1026 else {
1027 panic!("expected empty Flight stream to fail");
1028 };
1029
1030 assert!(matches!(
1031 error,
1032 error::Error::FlightGet {
1033 addr,
1034 tonic_code: tonic::Code::Unknown,
1035 ..
1036 } if addr == "127.0.0.1:4001"
1037 ));
1038 }
1039
1040 fn test_schema() -> Arc<Schema> {
1041 Arc::new(Schema::new(vec![ColumnSchema::new(
1042 "v",
1043 ConcreteDataType::int32_datatype(),
1044 false,
1045 )]))
1046 }
1047
1048 fn test_metrics_json() -> String {
1049 serde_json::to_string(&RecordBatchMetrics {
1050 elapsed_compute: 7,
1051 ..Default::default()
1052 })
1053 .unwrap()
1054 }
1055
1056 #[test]
1057 fn test_check_response_header() {
1058 let result = check_response_header(&None);
1059 assert!(matches!(
1060 result.unwrap_err(),
1061 Error::IllegalDatabaseResponse { .. }
1062 ));
1063
1064 let result = check_response_header(&Some(ResponseHeader { status: None }));
1065 assert!(matches!(
1066 result.unwrap_err(),
1067 Error::IllegalDatabaseResponse { .. }
1068 ));
1069
1070 let result = check_response_header(&Some(ResponseHeader {
1071 status: Some(PbStatus {
1072 status_code: StatusCode::Success as u32,
1073 err_msg: String::default(),
1074 }),
1075 }));
1076 assert!(result.is_ok());
1077
1078 let result = check_response_header(&Some(ResponseHeader {
1079 status: Some(PbStatus {
1080 status_code: u32::MAX,
1081 err_msg: String::default(),
1082 }),
1083 }));
1084 assert!(matches!(
1085 result.unwrap_err(),
1086 Error::IllegalDatabaseResponse { .. }
1087 ));
1088
1089 let result = check_response_header(&Some(ResponseHeader {
1090 status: Some(PbStatus {
1091 status_code: StatusCode::Internal as u32,
1092 err_msg: "blabla".to_string(),
1093 }),
1094 }));
1095 let Error::Server { code, msg, .. } = result.unwrap_err() else {
1096 unreachable!()
1097 };
1098 assert_eq!(code, StatusCode::Internal);
1099 assert_eq!(msg, "blabla");
1100 }
1101
1102 #[test]
1103 fn test_build_remote_dyn_filter_request_sets_header_and_body() {
1104 let request = build_remote_dyn_filter_update_request(
1105 "query-1",
1106 RemoteDynFilterUpdate {
1107 filter_id: "filter-1".to_string(),
1108 payload: vec![1, 2, 3],
1109 generation: 7,
1110 is_complete: false,
1111 },
1112 );
1113
1114 request.header.expect("remote dyn filter header must exist");
1115
1116 let body = request.body.expect("remote dyn filter body must exist");
1117 let region_request::Body::RemoteDynFilter(remote_request) = body else {
1118 panic!("expected remote dyn filter request body");
1119 };
1120
1121 assert_eq!(remote_request.query_id, "query-1");
1122 assert!(matches!(
1123 remote_request.action,
1124 Some(remote_dyn_filter_request::Action::Update(_))
1125 ));
1126 }
1127
1128 #[test]
1129 fn test_build_remote_dyn_filter_unregister_request_sets_header_and_body() {
1130 let request = build_remote_dyn_filter_unregister_request(
1131 "query-1",
1132 RemoteDynFilterUnregister {
1133 filter_id: "filter-9".to_string(),
1134 },
1135 );
1136
1137 request.header.expect("remote dyn filter header must exist");
1138
1139 let body = request.body.expect("remote dyn filter body must exist");
1140 let region_request::Body::RemoteDynFilter(remote_request) = body else {
1141 panic!("expected remote dyn filter request body");
1142 };
1143
1144 assert_eq!(remote_request.query_id, "query-1");
1145 assert!(matches!(
1146 remote_request.action,
1147 Some(remote_dyn_filter_request::Action::Unregister(_))
1148 ));
1149 }
1150
1151 #[tokio::test]
1152 async fn test_record_batch_stream_continues_after_pre_batch_metrics() {
1153 let schema = test_schema();
1154 let batch = RecordBatch::new(
1155 schema.clone(),
1156 vec![Arc::new(Int32Vector::from_slice([1])) as VectorRef],
1157 )
1158 .unwrap();
1159
1160 let mut recordbatches = recordbatches_from_flight_message_stream(
1161 "test-peer".to_string(),
1162 stream::iter(vec![
1163 Ok(FlightMessage::Schema(schema.arrow_schema().clone())),
1164 Ok(FlightMessage::Metrics(test_metrics_json())),
1165 Ok(FlightMessage::RecordBatch(batch.into_df_record_batch())),
1166 ]),
1167 )
1168 .await
1169 .unwrap();
1170
1171 let batch = recordbatches.next().await.unwrap().unwrap();
1172 assert_eq!(batch.num_rows(), 1);
1173 assert!(recordbatches.next().await.is_none());
1174
1175 let metrics = recordbatches.metrics().unwrap();
1176 assert_eq!(metrics.elapsed_compute, 7);
1177 }
1178
1179 #[tokio::test]
1180 async fn test_record_batch_is_yielded_without_waiting_for_next_message() {
1181 let schema = test_schema();
1182 let batch = RecordBatch::new(
1183 schema.clone(),
1184 vec![Arc::new(Int32Vector::from_slice([1])) as VectorRef],
1185 )
1186 .unwrap();
1187
1188 let messages = stream::iter(vec![
1189 Ok(FlightMessage::Schema(schema.arrow_schema().clone())),
1190 Ok(FlightMessage::RecordBatch(batch.into_df_record_batch())),
1191 ])
1192 .chain(stream::pending::<Result<FlightMessage>>());
1193 let mut recordbatches =
1194 recordbatches_from_flight_message_stream("test-peer".to_string(), messages)
1195 .await
1196 .unwrap();
1197
1198 let batch = tokio::time::timeout(Duration::from_secs(1), recordbatches.next())
1199 .await
1200 .expect("the first batch must not wait for lookahead")
1201 .unwrap()
1202 .unwrap();
1203 assert_eq!(batch.num_rows(), 1);
1204 }
1205
1206 #[tokio::test]
1207 async fn test_malformed_region_metrics_are_non_fatal() {
1208 let schema = test_schema();
1209 let batch = RecordBatch::new(
1210 schema.clone(),
1211 vec![Arc::new(Int32Vector::from_slice([1])) as VectorRef],
1212 )
1213 .unwrap();
1214 let mut recordbatches = recordbatches_from_flight_message_stream(
1215 "test-peer".to_string(),
1216 stream::iter(vec![
1217 Ok(FlightMessage::Schema(schema.arrow_schema().clone())),
1218 Ok(FlightMessage::Metrics("{not-json}".to_string())),
1219 Ok(FlightMessage::RecordBatch(batch.into_df_record_batch())),
1220 ]),
1221 )
1222 .await
1223 .unwrap();
1224
1225 assert_eq!(recordbatches.next().await.unwrap().unwrap().num_rows(), 1);
1226 assert!(recordbatches.next().await.is_none());
1227 }
1228
1229 #[tokio::test]
1230 async fn test_record_batch_stream_preserves_following_record_batch() {
1231 let schema = test_schema();
1232 let first_batch = RecordBatch::new(
1233 schema.clone(),
1234 vec![Arc::new(Int32Vector::from_slice([1])) as VectorRef],
1235 )
1236 .unwrap();
1237 let second_batch = RecordBatch::new(
1238 schema.clone(),
1239 vec![Arc::new(Int32Vector::from_slice([2])) as VectorRef],
1240 )
1241 .unwrap();
1242
1243 let mut recordbatches = recordbatches_from_flight_message_stream(
1244 "test-peer".to_string(),
1245 stream::iter(vec![
1246 Ok(FlightMessage::Schema(schema.arrow_schema().clone())),
1247 Ok(FlightMessage::RecordBatch(
1248 first_batch.into_df_record_batch(),
1249 )),
1250 Ok(FlightMessage::RecordBatch(
1251 second_batch.into_df_record_batch(),
1252 )),
1253 ]),
1254 )
1255 .await
1256 .unwrap();
1257
1258 let first_batch = recordbatches.next().await.unwrap().unwrap();
1259 let second_batch = recordbatches.next().await.unwrap().unwrap();
1260 assert_eq!(
1261 first_batch
1262 .column(0)
1263 .as_any()
1264 .downcast_ref::<Int32Array>()
1265 .unwrap()
1266 .value(0),
1267 1
1268 );
1269 assert_eq!(
1270 second_batch
1271 .column(0)
1272 .as_any()
1273 .downcast_ref::<Int32Array>()
1274 .unwrap()
1275 .value(0),
1276 2
1277 );
1278 assert!(recordbatches.next().await.is_none());
1279 }
1280
1281 #[tokio::test]
1282 async fn test_record_batch_stream_captures_final_metrics_after_record_batch() {
1283 let schema = test_schema();
1284 let batch = RecordBatch::new(
1285 schema.clone(),
1286 vec![Arc::new(Int32Vector::from_slice([1])) as VectorRef],
1287 )
1288 .unwrap();
1289 let final_metrics = serde_json::to_string(&RecordBatchMetrics {
1290 elapsed_compute: 99,
1291 ..Default::default()
1292 })
1293 .unwrap();
1294 let mut recordbatches = recordbatches_from_flight_message_stream(
1295 "test-peer".to_string(),
1296 stream::iter(vec![
1297 Ok(FlightMessage::Schema(schema.arrow_schema().clone())),
1298 Ok(FlightMessage::RecordBatch(batch.into_df_record_batch())),
1299 Ok(FlightMessage::Metrics(final_metrics)),
1300 ]),
1301 )
1302 .await
1303 .unwrap();
1304
1305 assert_eq!(recordbatches.next().await.unwrap().unwrap().num_rows(), 1);
1306 assert!(recordbatches.next().await.is_none());
1307 assert_eq!(recordbatches.metrics().unwrap().elapsed_compute, 99);
1308 }
1309
1310 #[tokio::test]
1311 async fn test_record_batch_stream_exposes_error_after_pre_batch_metrics() {
1312 let schema = test_schema();
1313 let mut recordbatches = recordbatches_from_flight_message_stream(
1314 "test-peer".to_string(),
1315 stream::iter(vec![
1316 Ok(FlightMessage::Schema(schema.arrow_schema().clone())),
1317 Ok(FlightMessage::Metrics(test_metrics_json())),
1318 Err(Error::from(Status::internal("boom after metrics"))),
1319 ]),
1320 )
1321 .await
1322 .unwrap();
1323
1324 let err = recordbatches.next().await.unwrap().unwrap_err();
1325 assert_eq!("External error", err.to_string());
1326 assert!(
1327 format!("{err:?}").contains("boom after metrics"),
1328 "unexpected error: {err:?}"
1329 );
1330 assert!(recordbatches.next().await.is_none());
1331 }
1332}