1mod catalog;
16#[cfg(feature = "enterprise")]
17mod flow_write;
18#[cfg(feature = "enterprise")]
19use flow_write::TrackedPayload;
20mod registrations;
21mod remote_dyn_filter;
22
23use std::collections::HashMap;
24use std::fmt::Debug;
25use std::ops::Deref;
26use std::pin::Pin;
27use std::sync::atomic::{AtomicBool, Ordering};
28use std::sync::{Arc, RwLock};
29use std::task::{Context, Poll};
30use std::time::{Duration, Instant};
31
32use api::region::{BatchRegionResponse, RegionResponse};
33use api::v1::meta::TopicStat;
34use api::v1::region::sync_request::ManifestInfo;
35use api::v1::region::{
36 ListMetadataRequest, RegionRequest as RegionRequestV1, RegionRequestFailure,
37 RegionResponse as RegionResponseV1, SyncRequest, region_request,
38};
39use api::v1::{ResponseHeader, Status};
40use arrow_flight::{FlightData, Ticket};
41use async_trait::async_trait;
42use bytes::Bytes;
43use common_error::ext::{BoxedError, ErrorExt};
44use common_error::status_code::StatusCode;
45#[cfg(feature = "enterprise")]
46use common_grpc::flow_write::{COMMAND_KEY, FlowWriteRegistry, Reply};
47use common_meta::datanode::TopicStatsReporter;
48use common_query::OutputData;
49use common_query::request::QueryRequest;
50use common_recordbatch::adapter::RecordBatchMetrics;
51use common_recordbatch::{OrderOption, RecordBatch, RecordBatchStream, SendableRecordBatchStream};
52use common_runtime::{JoinHandle, Runtime};
53use common_telemetry::tracing::{self, info_span};
54use common_telemetry::tracing_context::{FutureExt, TracingContext};
55use common_telemetry::{debug, error, info, warn};
56use dashmap::DashMap;
57use datafusion::datasource::TableProvider;
58use datafusion_common::tree_node::TreeNode;
59use datatypes::schema::SchemaRef;
60use either::Either;
61use futures_util::stream::FuturesUnordered;
62use futures_util::{Stream, StreamExt};
63use metric_engine::engine::MetricEngine;
64use mito2::engine::{MITO_ENGINE_NAME, MitoEngine};
65use prost::Message;
66use query::QueryEngineRef;
67pub use query::dummy_catalog::{
68 DummyCatalogList, DummyTableProviderFactory, TableProviderFactoryRef,
69};
70use query::options::should_collect_region_watermark_from_extensions;
71use serde_json;
72use servers::error::{
73 self as servers_error, ExecuteGrpcRequestSnafu, Result as ServerResult, SuspendedSnafu,
74};
75use servers::grpc::FlightCompression;
76use servers::grpc::flight::{
77 FlightCraft, FlightRecordBatchSource, FlightRecordBatchStream, FlightRecordBatchStreamInput,
78 TonicStream,
79};
80use servers::grpc::region_server::RegionServerHandler;
81use session::context::{
82 FLIGHT_METRICS_HEARTBEAT_INTERVAL, QueryContext, QueryContextBuilder, QueryContextRef,
83};
84use snafu::{IntoError, OptionExt, ResultExt, ensure};
85use store_api::metric_engine_consts::{
86 FILE_ENGINE_NAME, LOGICAL_TABLE_METADATA_KEY, METRIC_ENGINE_NAME,
87};
88use store_api::region_engine::{
89 RegionEngineRef, RegionManifestInfo, RegionRole, RegionStatistic, RemapManifestsRequest,
90 RemapManifestsResponse, SetRegionRoleStateResponse, SettableRegionRoleState,
91 SyncRegionFromRequest,
92};
93use store_api::region_request::{
94 self as store_region_request, AffectedRows, BatchRegionDdlRequest, RegionCatchupRequest,
95 RegionCloseRequest, RegionOpenRequest, RegionRequest,
96};
97use store_api::storage::RegionId;
98use tokio::sync::{OwnedSemaphorePermit, Semaphore, mpsc};
99use tokio::time::{self, timeout};
100use tonic::{Request, Response, Result as TonicResult};
101
102use crate::datanode::record_shutdown_error;
103use crate::error::{
104 self, BuildRegionRequestsSnafu, ConcurrentQueryLimiterClosedSnafu,
105 ConcurrentQueryLimiterTimeoutSnafu, DataFusionSnafu, DecodeLogicalPlanSnafu,
106 ExecuteLogicalPlanSnafu, FindLogicalRegionsSnafu, GetRegionMetadataSnafu,
107 HandleBatchDdlRequestSnafu, HandleBatchOpenRequestSnafu, HandleRegionRequestSnafu,
108 NewPlanDecoderSnafu, RegionEngineNotFoundSnafu, RegionNotFoundSnafu, RegionNotReadySnafu,
109 Result, RuntimeJoinSnafu, SerializeJsonSnafu, StopRegionEngineSnafu, UnexpectedSnafu,
110 UnsupportedOutputSnafu,
111};
112use crate::event_listener::RegionServerEventListenerRef;
113use crate::query_stream::QueryRuntimeStream;
114use crate::region_server::catalog::{NameAwareCatalogList, NameAwareDataSourceInjectorBuilder};
115use crate::region_server::registrations::RemoteDynFilterRegistry;
116use crate::region_server::remote_dyn_filter::wrap_remote_dyn_filter_guarded_stream;
117
118const QUERY_RUNTIME_STREAM_BUFFER_SIZE: usize = 8;
119
120#[derive(Clone)]
121pub struct RegionServer {
122 inner: Arc<RegionServerInner>,
123 flight_compression: FlightCompression,
124 suspend: Arc<AtomicBool>,
125 #[cfg(feature = "enterprise")]
126 flow_write_registry: Arc<FlowWriteRegistry<TrackedPayload, Reply>>,
127}
128
129pub struct RegionStat {
130 pub region_id: RegionId,
131 pub engine: String,
132 pub role: RegionRole,
133}
134
135impl RegionServer {
136 pub fn new(
137 query_engine: QueryEngineRef,
138 runtime: Runtime,
139 event_listener: RegionServerEventListenerRef,
140 flight_compression: FlightCompression,
141 ) -> Self {
142 Self::with_table_provider(
143 query_engine,
144 runtime,
145 event_listener,
146 Arc::new(DummyTableProviderFactory),
147 0,
148 Duration::from_millis(0),
149 flight_compression,
150 )
151 }
152
153 pub fn with_table_provider(
154 query_engine: QueryEngineRef,
155 runtime: Runtime,
156 event_listener: RegionServerEventListenerRef,
157 table_provider_factory: TableProviderFactoryRef,
158 max_concurrent_queries: usize,
159 concurrent_query_limiter_timeout: Duration,
160 flight_compression: FlightCompression,
161 ) -> Self {
162 Self {
163 inner: Arc::new(RegionServerInner::new(
164 query_engine,
165 runtime,
166 event_listener,
167 table_provider_factory,
168 RegionServerParallelism::from_opts(
169 max_concurrent_queries,
170 concurrent_query_limiter_timeout,
171 ),
172 )),
173 flight_compression,
174 suspend: Arc::new(AtomicBool::new(false)),
175 #[cfg(feature = "enterprise")]
176 flow_write_registry: Arc::new(FlowWriteRegistry::default()),
177 }
178 }
179
180 pub fn register_engine(&mut self, engine: RegionEngineRef) {
182 self.inner.register_engine(engine);
183 }
184
185 pub fn set_topic_stats_reporter(&mut self, topic_stats_reporter: Box<dyn TopicStatsReporter>) {
187 self.inner.set_topic_stats_reporter(topic_stats_reporter);
188 }
189
190 pub fn find_engine(&self, region_id: RegionId) -> Result<Option<RegionEngineRef>> {
192 match self.inner.get_engine(region_id, &RegionChange::None) {
193 Ok(CurrentEngine::Engine(engine)) => Ok(Some(engine)),
194 Ok(CurrentEngine::EarlyReturn(_)) => Ok(None),
195 Err(error::Error::RegionNotFound { .. }) => Ok(None),
196 Err(err) => Err(err),
197 }
198 }
199
200 pub fn mito_engine(&self) -> Option<MitoEngine> {
202 if let Some(mito) = self.inner.mito_engine.read().unwrap().clone() {
203 Some(mito)
204 } else {
205 self.inner
206 .engines
207 .read()
208 .unwrap()
209 .get(MITO_ENGINE_NAME)
210 .cloned()
211 .and_then(|e| {
212 let mito = e.as_any().downcast_ref::<MitoEngine>().cloned();
213 if mito.is_none() {
214 warn!("Mito engine not found in region server engines");
215 }
216 mito
217 })
218 }
219 }
220
221 #[tracing::instrument(skip_all)]
222 pub async fn handle_batch_open_requests(
223 &self,
224 parallelism: usize,
225 requests: Vec<(RegionId, RegionOpenRequest)>,
226 ignore_nonexistent_region: bool,
227 ) -> Result<Vec<RegionId>> {
228 self.inner
229 .handle_batch_open_requests(parallelism, requests, ignore_nonexistent_region)
230 .await
231 }
232
233 #[tracing::instrument(skip_all)]
234 pub async fn handle_batch_catchup_requests(
235 &self,
236 parallelism: usize,
237 requests: Vec<(RegionId, RegionCatchupRequest)>,
238 ) -> Result<Vec<(RegionId, std::result::Result<(), BoxedError>)>> {
239 self.inner
240 .handle_batch_catchup_requests(parallelism, requests)
241 .await
242 }
243
244 #[tracing::instrument(skip_all, fields(request_type = request.request_type()))]
245 pub async fn handle_request(
246 &self,
247 region_id: RegionId,
248 request: RegionRequest,
249 ) -> Result<RegionResponse> {
250 if RegionServerInner::is_ingest_request(&request) {
251 let inner = self.inner.clone();
252 let request_type = request.request_type();
253 let task = common_runtime::spawn_ingest(async move {
254 inner.handle_request(region_id, request).await
255 });
256 return join_ingest_task(task, request_type).await?;
257 }
258
259 self.inner.handle_request(region_id, request).await
260 }
261
262 async fn table_provider(
264 &self,
265 region_id: RegionId,
266 ctx: Option<QueryContextRef>,
267 ) -> Result<Arc<dyn TableProvider>> {
268 let status = self
269 .inner
270 .region_map
271 .get(®ion_id)
272 .context(RegionNotFoundSnafu { region_id })?
273 .clone();
274 ensure!(
275 matches!(status, RegionEngineWithStatus::Ready(_)),
276 RegionNotReadySnafu { region_id }
277 );
278
279 let provider = self
280 .inner
281 .table_provider_factory
282 .create(region_id, status.into_engine(), ctx)
283 .await
284 .context(ExecuteLogicalPlanSnafu)?;
285
286 Ok(provider)
287 }
288
289 pub async fn handle_remote_read(
291 &self,
292 request: api::v1::region::QueryRequest,
293 query_ctx: QueryContextRef,
294 ) -> Result<SendableRecordBatchStream> {
295 self.handle_remote_read_inner(request, query_ctx).await
296 }
297
298 async fn handle_remote_read_inner(
299 &self,
300 request: api::v1::region::QueryRequest,
301 query_ctx: QueryContextRef,
302 ) -> Result<SendableRecordBatchStream> {
303 let permit = if let Some(p) = &self.inner.parallelism {
304 Some(p.acquire().await?)
305 } else {
306 None
307 };
308
309 let region_id = RegionId::from_u64(request.region_id);
310 let engine_ctx = self
311 .inner
312 .query_engine
313 .engine_context(query_ctx)
314 .map_err(|error| {
315 crate::error::InvalidQueryContextSnafu {
316 reason: error.to_string(),
317 }
318 .build()
319 })?;
320 let query_ctx = engine_ctx.query_ctx();
321 let catalog_list = Arc::new(NameAwareCatalogList::new(
322 self.clone(),
323 region_id,
324 query_ctx.clone(),
325 ));
326
327 if query_ctx.explain_verbose() {
328 common_telemetry::info!("Handle remote read for region: {}", region_id);
329 }
330
331 let decoder = engine_ctx.new_plan_decoder().context(NewPlanDecoderSnafu)?;
332
333 let plan = decoder
334 .decode(Bytes::from(request.plan), catalog_list, false)
335 .await
336 .context(DecodeLogicalPlanSnafu)?;
337
338 let cleanup = self.register_initial_remote_dyn_filter_cleanup(&query_ctx, region_id);
339
340 let stream = self
341 .inner
342 .handle_read(
343 QueryRequest {
344 header: request.header,
345 region_id,
346 plan,
347 },
348 query_ctx.clone(),
349 )
350 .await?;
351
352 let stream = wrap_flow_region_watermark_stream(stream, region_id, &query_ctx);
353 let stream = if let Some(cleanup) = cleanup {
354 wrap_remote_dyn_filter_guarded_stream(stream, cleanup)
355 } else {
356 stream
357 };
358 Ok(maybe_guard_stream(stream, permit))
359 }
360
361 #[tracing::instrument(skip_all)]
362 pub async fn handle_read(&self, request: QueryRequest) -> Result<SendableRecordBatchStream> {
363 self.handle_read_inner(request).await
364 }
365
366 async fn handle_read_inner(&self, request: QueryRequest) -> Result<SendableRecordBatchStream> {
367 let permit = if let Some(p) = &self.inner.parallelism {
368 Some(p.acquire().await?)
369 } else {
370 None
371 };
372
373 let ctx = request
374 .header
375 .as_ref()
376 .map(QueryContext::try_from)
377 .transpose()
378 .map_err(|err| {
379 crate::error::InvalidQueryContextSnafu {
380 reason: err.to_string(),
381 }
382 .build()
383 })?;
384 let query_ctx = Arc::new(ctx.unwrap_or_else(|| QueryContextBuilder::default().build()));
385
386 let region_id = request.region_id;
387 let injector_builder = NameAwareDataSourceInjectorBuilder::from_plan(&request.plan)
388 .context(DataFusionSnafu)?;
389 let mut injector = injector_builder
390 .build(self, request.region_id, query_ctx.clone())
391 .await?;
392
393 let plan = request
394 .plan
395 .rewrite(&mut injector)
396 .context(DataFusionSnafu)?
397 .data;
398
399 let cleanup = self.register_initial_remote_dyn_filter_cleanup(&query_ctx, region_id);
400
401 let stream = self
402 .inner
403 .handle_read(QueryRequest { plan, ..request }, query_ctx.clone())
404 .await?;
405
406 let stream = wrap_flow_region_watermark_stream(stream, region_id, &query_ctx);
407 let stream = if let Some(cleanup) = cleanup {
408 wrap_remote_dyn_filter_guarded_stream(stream, cleanup)
409 } else {
410 stream
411 };
412 Ok(maybe_guard_stream(stream, permit))
413 }
414
415 pub fn reportable_regions(&self) -> Vec<RegionStat> {
419 self.inner
420 .region_map
421 .iter()
422 .filter_map(|e| {
423 let region_id = *e.key();
424 e.role(region_id).map(|role| RegionStat {
426 region_id,
427 engine: e.value().name().to_string(),
428 role,
429 })
430 })
431 .collect()
432 }
433
434 pub fn topic_stats(&self) -> Vec<TopicStat> {
436 let mut reporter = self.inner.topic_stats_reporter.write().unwrap();
437 let Some(reporter) = reporter.as_mut() else {
438 return vec![];
439 };
440 reporter
441 .reportable_topics()
442 .into_iter()
443 .map(|stat| TopicStat {
444 topic_name: stat.topic,
445 record_size: stat.record_size,
446 record_num: stat.record_num,
447 latest_entry_id: stat.latest_entry_id,
448 })
449 .collect()
450 }
451
452 pub fn is_region_leader(&self, region_id: RegionId) -> Option<bool> {
453 self.inner.region_map.get(®ion_id).and_then(|engine| {
454 engine.role(region_id).map(|role| match role {
455 RegionRole::Follower => false,
456 RegionRole::Leader => true,
457 RegionRole::StagingLeader => true,
458 RegionRole::DowngradingLeader => true,
459 })
460 })
461 }
462
463 pub fn set_region_role(&self, region_id: RegionId, role: RegionRole) -> Result<()> {
464 let engine = self
465 .inner
466 .region_map
467 .get(®ion_id)
468 .with_context(|| RegionNotFoundSnafu { region_id })?;
469 engine
470 .set_region_role(region_id, role)
471 .with_context(|_| HandleRegionRequestSnafu { region_id })
472 }
473
474 pub fn set_region_role_with_lease(
476 &self,
477 region_id: RegionId,
478 role: RegionRole,
479 deadline: std::time::Instant,
480 ) -> Result<()> {
481 let engine = self
482 .inner
483 .region_map
484 .get(®ion_id)
485 .with_context(|| RegionNotFoundSnafu { region_id })?;
486 engine
487 .set_region_role_with_lease(region_id, role, deadline)
488 .with_context(|_| HandleRegionRequestSnafu { region_id })
489 }
490
491 pub async fn set_region_role_state_gracefully(
501 &self,
502 region_id: RegionId,
503 state: SettableRegionRoleState,
504 ) -> Result<SetRegionRoleStateResponse> {
505 let engine = self
507 .inner
508 .region_map
509 .get(®ion_id)
510 .map(|entry| entry.value().clone());
511
512 match engine {
513 Some(engine) => Ok(engine
514 .set_region_role_state_gracefully(region_id, state)
515 .await
516 .with_context(|_| HandleRegionRequestSnafu { region_id })?),
517 None => Ok(SetRegionRoleStateResponse::NotFound),
518 }
519 }
520
521 pub fn runtime(&self) -> Runtime {
522 self.inner.runtime.clone()
523 }
524
525 pub fn region_statistic(&self, region_id: RegionId) -> Option<RegionStatistic> {
526 match self.inner.region_map.get(®ion_id) {
527 Some(e) => e.region_statistic(region_id),
528 None => None,
529 }
530 }
531
532 pub async fn stop(&self) -> Result<()> {
534 self.inner.stop().await
535 }
536
537 #[cfg(test)]
538 pub(crate) fn register_test_region(&self, region_id: RegionId, engine: RegionEngineRef) {
540 {
541 let mut engines = self.inner.engines.write().unwrap();
542 if !engines.contains_key(engine.name()) {
543 debug!("Registering test engine: {}", engine.name());
544 engines.insert(engine.name().to_string(), engine.clone());
545 }
546 }
547
548 self.inner
549 .region_map
550 .insert(region_id, RegionEngineWithStatus::Ready(engine));
551 }
552
553 async fn handle_batch_ddl_requests(
554 &self,
555 request: region_request::Body,
556 ) -> Result<RegionResponse> {
557 let batch_request = BatchRegionDdlRequest::try_from_request_body(request)
559 .context(BuildRegionRequestsSnafu)?
560 .unwrap();
561 let tracing_context = TracingContext::from_current_span();
562
563 let span = tracing_context.attach(info_span!("RegionServer::handle_batch_ddl_requests"));
564 self.inner
565 .handle_batch_ddl_requests(batch_request)
566 .trace(span)
567 .await
568 }
569
570 async fn handle_requests_in_parallel(
571 &self,
572 request: region_request::Body,
573 ) -> Result<BatchRegionResponse<error::Error>> {
574 let requests = prepare_batch_write_requests(request);
575 let requests = match self.try_handle_mito_batch_puts(requests).await? {
576 Either::Left(response) => return Ok(response),
577 Either::Right(requests) => requests,
578 };
579 let requests = match self.try_handle_metric_batch_puts(requests).await? {
580 Either::Left(response) => return Ok(response),
581 Either::Right(requests) => requests,
582 };
583 let tracing_context = TracingContext::from_current_span();
584 let results = futures::future::join_all(requests.into_iter().map(|request| {
585 let span = tracing_context.attach(info_span!(
586 "RegionServer::handle_region_request",
587 region_id = request.region_id.to_string()
588 ));
589 async move {
590 (
591 request.index,
592 self.handle_request(request.region_id, request.request)
593 .trace(span)
594 .await,
595 )
596 }
597 }))
598 .await;
599 let mut response = BatchRegionResponse {
600 affected_rows: 0,
601 extensions: HashMap::new(),
602 failures: Vec::new(),
603 };
604 for (index, result) in results {
605 match result {
606 Ok(result) => {
607 response.affected_rows += result.affected_rows;
608 response.extensions.extend(result.extensions);
609 }
610 Err(error) => {
611 log_batch_write_error(&error, index);
612 response.failures.push((index, error));
613 }
614 }
615 }
616 Ok(response)
617 }
618
619 async fn handle_requests_in_serial(
620 &self,
621 request: region_request::Body,
622 ) -> Result<RegionResponse> {
623 let requests =
624 RegionRequest::try_from_request_body(request).context(BuildRegionRequestsSnafu)?;
625 let tracing_context = TracingContext::from_current_span();
626
627 let mut affected_rows = 0;
628 let mut extensions = HashMap::new();
629 for (region_id, req) in requests {
630 let span = tracing_context.attach(info_span!(
631 "RegionServer::handle_region_request",
632 region_id = region_id.to_string()
633 ));
634 let result = self.handle_request(region_id, req).trace(span).await?;
635
636 affected_rows += result.affected_rows;
637 extensions.extend(result.extensions);
638 }
639
640 Ok(RegionResponse {
641 affected_rows,
642 extensions,
643 metadata: Vec::new(),
644 })
645 }
646
647 async fn try_handle_metric_batch_puts(
649 &self,
650 requests: Vec<IndexedRegionRequest>,
651 ) -> Result<Either<BatchRegionResponse<error::Error>, Vec<IndexedRegionRequest>>> {
652 let Some(first) = requests.first() else {
653 return Ok(Either::Right(requests));
654 };
655 if !matches!(first.request, RegionRequest::Put(_)) {
656 return Ok(Either::Right(requests));
657 }
658
659 let first_region_id = first.region_id;
660 let request_type = first.request.request_type();
661
662 let Ok(CurrentEngine::Engine(engine)) =
665 self.inner.get_engine(first_region_id, &RegionChange::None)
666 else {
667 return Ok(Either::Right(requests));
668 };
669 if engine.name() != METRIC_ENGINE_NAME
670 || !requests
671 .iter()
672 .all(|request| matches!(request.request, RegionRequest::Put(_)))
673 {
674 return Ok(Either::Right(requests));
675 }
676 let metric_engine = engine
677 .as_any()
678 .downcast_ref::<MetricEngine>()
679 .context(UnexpectedSnafu {
680 violated: "Failed to downcast to MetricEngine",
681 })?
682 .clone();
683
684 let indices: Vec<_> = requests
685 .iter()
686 .map(|request| (request.index, request.region_id))
687 .collect();
688 let put_requests = requests.into_iter().map(|request| match request.request {
689 RegionRequest::Put(put) => (request.region_id, put),
690 _ => unreachable!("Already checked all are Put"),
691 });
692 let tracing_context = TracingContext::from_current_span();
693 let batch_size = put_requests.len();
694 let span = tracing_context.attach(info_span!(
695 "RegionServer::handle_metric_batch_puts",
696 batch_size = batch_size
697 ));
698 let task = common_runtime::spawn_ingest(async move {
699 metric_engine
700 .put_regions_batch(put_requests)
701 .trace(span)
702 .await
703 });
704 let results = join_ingest_task(task, request_type).await?;
705 let mut response = BatchRegionResponse {
706 affected_rows: 0,
707 extensions: HashMap::new(),
708 failures: Vec::new(),
709 };
710 for group in results {
711 let region_id = indices[group.request_indices[0]].1;
712 let result = group
713 .result
714 .map_err(BoxedError::new)
715 .context(HandleRegionRequestSnafu { region_id })
716 .map(RegionResponse::new);
717 record_ingest_result(request_type, group.request_indices.len(), &result);
718 match result {
719 Ok(result) => {
720 response.affected_rows += result.affected_rows;
721 }
722 Err(error) => {
723 log_batch_write_error(&error, indices[group.request_indices[0]].0);
724 let error = Arc::new(error);
725 response
726 .failures
727 .extend(group.request_indices.into_iter().map(|index| {
728 (
729 indices[index].0,
730 error::BatchWriteSnafu.into_error(Arc::clone(&error)),
731 )
732 }));
733 }
734 }
735 }
736 Ok(Either::Left(response))
737 }
738
739 async fn try_handle_mito_batch_puts(
741 &self,
742 requests: Vec<IndexedRegionRequest>,
743 ) -> Result<Either<BatchRegionResponse<error::Error>, Vec<IndexedRegionRequest>>> {
744 let Some(engine) = self.mito_engine_for_batch_puts(&requests) else {
745 return Ok(Either::Right(requests));
746 };
747 let request_type = requests[0].request.request_type();
748 let mut indices: HashMap<RegionId, std::collections::VecDeque<usize>> = HashMap::new();
749 let puts = requests
750 .into_iter()
751 .map(|request| {
752 indices
753 .entry(request.region_id)
754 .or_default()
755 .push_back(request.index);
756 match request.request {
757 RegionRequest::Put(put) => (request.region_id, put),
758 _ => unreachable!("Already checked all are Put"),
759 }
760 })
761 .collect();
762 let started = Instant::now();
763 let tracing_context = TracingContext::from_current_span();
764 let task = common_runtime::spawn_ingest(async move {
765 let mut pending = engine
766 .handle_batch_put_requests(puts)
767 .into_iter()
768 .map(|(region_ids, response)| {
769 let request_indices = region_ids
770 .iter()
771 .filter_map(|region_id| {
772 indices
773 .get_mut(region_id)
774 .and_then(|indices| indices.pop_front())
775 })
776 .collect::<Vec<_>>();
777 let span = tracing_context.attach(info_span!(
778 "RegionServer::handle_region_request",
779 region_id = region_ids[0].to_string()
780 ));
781 async move { (region_ids, request_indices, response.await) }.trace(span)
782 })
783 .collect::<FuturesUnordered<_>>();
784 let histogram =
785 crate::metrics::HANDLE_REGION_REQUEST_ELAPSED.with_label_values(&[request_type]);
786 let mut response = BatchRegionResponse {
787 affected_rows: 0,
788 extensions: HashMap::new(),
789 failures: Vec::new(),
790 };
791 while let Some((region_ids, request_indices, result)) = pending.next().await {
792 let elapsed = started.elapsed().as_secs_f64();
793 for _ in ®ion_ids {
794 histogram.observe(elapsed);
795 }
796 let result = result.context(HandleRegionRequestSnafu {
797 region_id: region_ids[0],
798 });
799 record_ingest_result(request_type, region_ids.len(), &result);
800 match result {
801 Ok(result) => {
802 response.affected_rows += result.affected_rows;
803 response.extensions.extend(result.extensions);
804 }
805 Err(error) => {
806 log_batch_write_error(&error, request_indices[0]);
807 let error = Arc::new(error);
808 response
809 .failures
810 .extend(request_indices.into_iter().map(|index| {
811 (index, error::BatchWriteSnafu.into_error(Arc::clone(&error)))
812 }));
813 }
814 }
815 }
816 response.failures.sort_unstable_by_key(|(index, _)| *index);
817 response
818 });
819 join_ingest_task(task, request_type).await.map(Either::Left)
820 }
821
822 fn mito_engine_for_batch_puts(
825 &self,
826 requests: &[IndexedRegionRequest],
827 ) -> Option<RegionEngineRef> {
828 if requests.len() < 2 {
829 return None;
830 }
831 let first_region_id = requests[0].region_id;
832 let all_puts_to_ready_mito_regions = requests.iter().all(|request| {
833 matches!(request.request, RegionRequest::Put(_))
834 && matches!(
835 self.inner.region_map.get(&request.region_id).as_deref(),
836 Some(RegionEngineWithStatus::Ready(engine)) if engine.name() == MITO_ENGINE_NAME
837 )
838 });
839 if !all_puts_to_ready_mito_regions {
840 return None;
841 }
842 match self.inner.region_map.get(&first_region_id).as_deref() {
843 Some(RegionEngineWithStatus::Ready(engine)) => Some(engine.clone()),
844 _ => None,
845 }
846 }
847
848 async fn handle_sync_region_request(&self, request: &SyncRequest) -> Result<RegionResponse> {
849 let region_id = RegionId::from_u64(request.region_id);
850 let manifest_info = request
851 .manifest_info
852 .context(error::MissingRequiredFieldSnafu {
853 name: "manifest_info",
854 })?;
855
856 let manifest_info = match manifest_info {
857 ManifestInfo::MitoManifestInfo(info) => {
858 RegionManifestInfo::mito(info.data_manifest_version, 0, 0)
859 }
860 ManifestInfo::MetricManifestInfo(info) => RegionManifestInfo::metric(
861 info.data_manifest_version,
862 0,
863 info.metadata_manifest_version,
864 0,
865 ),
866 };
867
868 let tracing_context = TracingContext::from_current_span();
869 let span = tracing_context.attach(info_span!("RegionServer::handle_sync_region_request"));
870
871 self.sync_region(
872 region_id,
873 SyncRegionFromRequest::from_manifest(manifest_info),
874 )
875 .trace(span)
876 .await
877 .map(|_| RegionResponse::new(AffectedRows::default()))
878 }
879
880 #[tracing::instrument(skip_all)]
885 async fn handle_list_metadata_request(
886 &self,
887 request: &ListMetadataRequest,
888 ) -> Result<RegionResponse> {
889 let mut region_metadatas = Vec::new();
890 for region_id in &request.region_ids {
892 let region_id = RegionId::from_u64(*region_id);
893 let Some(engine) = self.find_engine(region_id)? else {
895 region_metadatas.push(None);
896 continue;
897 };
898
899 match engine.get_metadata(region_id).await {
900 Ok(metadata) => region_metadatas.push(Some(metadata)),
901 Err(err) => {
902 if err.status_code() == StatusCode::RegionNotFound {
903 region_metadatas.push(None);
904 } else {
905 Err(err).with_context(|_| GetRegionMetadataSnafu {
906 engine: engine.name(),
907 region_id,
908 })?;
909 }
910 }
911 }
912 }
913
914 let json_result = serde_json::to_vec(®ion_metadatas).context(SerializeJsonSnafu)?;
916
917 let response = RegionResponse::from_metadata(json_result);
918
919 Ok(response)
920 }
921
922 pub async fn sync_region(
924 &self,
925 region_id: RegionId,
926 request: SyncRegionFromRequest,
927 ) -> Result<()> {
928 let engine = match self.inner.get_engine(region_id, &RegionChange::None)? {
929 CurrentEngine::Engine(engine) => engine,
930 _ => {
931 return UnexpectedSnafu {
932 violated: "unexpected EarlyReturn engine status for a ready region",
933 }
934 .fail();
935 }
936 };
937
938 self.inner
939 .handle_sync_region(&engine, region_id, request)
940 .await
941 }
942
943 pub async fn remap_manifests(
945 &self,
946 request: RemapManifestsRequest,
947 ) -> Result<RemapManifestsResponse> {
948 let region_id = request.region_id;
949 let engine = match self.inner.get_engine(region_id, &RegionChange::None)? {
950 CurrentEngine::Engine(engine) => engine,
951 _ => {
952 return UnexpectedSnafu {
953 violated: "unexpected EarlyReturn engine status for a ready region",
954 }
955 .fail();
956 }
957 };
958
959 engine
960 .remap_manifests(request)
961 .await
962 .with_context(|_| HandleRegionRequestSnafu { region_id })
963 }
964
965 fn is_suspended(&self) -> bool {
966 self.suspend.load(Ordering::Relaxed)
967 }
968
969 pub(crate) fn suspend_state(&self) -> Arc<AtomicBool> {
970 self.suspend.clone()
971 }
972}
973
974fn wrap_flow_region_watermark_stream(
975 stream: SendableRecordBatchStream,
976 region_id: RegionId,
977 query_ctx: &QueryContextRef,
978) -> SendableRecordBatchStream {
979 if should_collect_region_watermark_from_extensions(&query_ctx.extensions())
980 && let Some(seq) = query_ctx.get_snapshot(region_id.as_u64())
981 {
982 Box::pin(RegionWatermarkStream::new(stream, region_id, seq)) as SendableRecordBatchStream
983 } else {
984 stream
985 }
986}
987
988struct RegionWatermarkStream {
990 stream: SendableRecordBatchStream,
991 region_id: u64,
992 snapshot_seq: u64,
993 finished: bool,
994}
995
996impl RegionWatermarkStream {
997 fn new(stream: SendableRecordBatchStream, region_id: RegionId, snapshot_seq: u64) -> Self {
998 Self {
999 stream,
1000 region_id: region_id.as_u64(),
1001 snapshot_seq,
1002 finished: false,
1003 }
1004 }
1005
1006 fn merged_metrics(&self, mut metrics: RecordBatchMetrics) -> RecordBatchMetrics {
1007 if metrics
1008 .region_watermarks
1009 .iter()
1010 .any(|entry| entry.region_id == self.region_id)
1011 {
1012 return metrics;
1013 }
1014
1015 metrics
1016 .region_watermarks
1017 .push(common_recordbatch::adapter::RegionWatermarkEntry {
1018 region_id: self.region_id,
1019 watermark: Some(self.snapshot_seq),
1020 });
1021 metrics
1022 }
1023}
1024
1025impl RecordBatchStream for RegionWatermarkStream {
1026 fn name(&self) -> &str {
1027 self.stream.name()
1028 }
1029
1030 fn schema(&self) -> datatypes::schema::SchemaRef {
1031 self.stream.schema()
1032 }
1033
1034 fn output_ordering(&self) -> Option<&[OrderOption]> {
1035 self.stream.output_ordering()
1036 }
1037
1038 fn metrics(&self) -> Option<RecordBatchMetrics> {
1039 let base = self.stream.metrics();
1040 if !self.finished {
1041 return base;
1042 }
1043
1044 Some(self.merged_metrics(base.unwrap_or_default()))
1045 }
1046}
1047
1048impl Stream for RegionWatermarkStream {
1049 type Item = common_recordbatch::error::Result<RecordBatch>;
1050
1051 fn size_hint(&self) -> (usize, Option<usize>) {
1052 self.stream.size_hint()
1053 }
1054
1055 fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
1056 match Pin::new(&mut self.stream).poll_next(cx) {
1057 Poll::Ready(None) => {
1058 self.finished = true;
1059 Poll::Ready(None)
1060 }
1061 other => other,
1062 }
1063 }
1064}
1065
1066#[async_trait]
1067impl RegionServerHandler for RegionServer {
1068 async fn handle_region_request(
1069 &self,
1070 request: RegionRequestV1,
1071 ) -> ServerResult<RegionResponseV1> {
1072 #[cfg(feature = "enterprise")]
1073 if request
1074 .header
1075 .as_ref()
1076 .and_then(|header| header.query_context.as_ref())
1077 .is_some_and(|context| context.extensions.contains_key(COMMAND_KEY))
1078 {
1079 return self.handle_flow_write_request(request).await;
1080 }
1081 let accept_partial_result = request
1082 .header
1083 .as_ref()
1084 .and_then(|header| header.query_context.as_ref())
1085 .and_then(|context| context.extensions.get(api::region::ACCEPT_PARTIAL_RESULT))
1086 .is_some_and(|value| value == "true");
1087 let body = request.body.context(servers_error::InvalidQuerySnafu {
1088 reason: "Expecting non-empty region request body.",
1089 })?;
1090 self.handle_inner(body, accept_partial_result).await
1091 }
1092
1093 async fn handle(&self, request: region_request::Body) -> ServerResult<RegionResponseV1> {
1094 self.handle_inner(request, false).await
1095 }
1096}
1097
1098impl RegionServer {
1099 async fn handle_inner(
1100 &self,
1101 request: region_request::Body,
1102 accept_partial_result: bool,
1103 ) -> ServerResult<RegionResponseV1> {
1104 let request_count = match &request {
1105 region_request::Body::Inserts(requests) => Some(requests.requests.len()),
1106 region_request::Body::Deletes(requests) => Some(requests.requests.len()),
1107 _ => None,
1108 };
1109 if let Some(request_count) = request_count {
1110 return self
1111 .handle_write_request(request, request_count, accept_partial_result)
1112 .await;
1113 }
1114 let failed_requests_cnt = crate::metrics::REGION_SERVER_REQUEST_FAILURE_COUNT
1115 .with_label_values(&[request.as_ref()]);
1116 let response = match &request {
1117 region_request::Body::Creates(_)
1118 | region_request::Body::Drops(_)
1119 | region_request::Body::Alters(_) => self.handle_batch_ddl_requests(request).await,
1120 region_request::Body::Sync(sync_request) => {
1121 self.handle_sync_region_request(sync_request).await
1122 }
1123 region_request::Body::ListMetadata(list_metadata_request) => {
1124 self.handle_list_metadata_request(list_metadata_request)
1125 .await
1126 }
1127 region_request::Body::RemoteDynFilter(remote_dyn_filter_request) => {
1128 self.handle_remote_dyn_filter_request(remote_dyn_filter_request)
1129 .await
1130 }
1131 _ => self.handle_requests_in_serial(request).await,
1132 }
1133 .map_err(BoxedError::new)
1134 .inspect_err(|_| failed_requests_cnt.inc())
1135 .context(ExecuteGrpcRequestSnafu)?;
1136 Ok(RegionResponseV1 {
1137 header: Some(ResponseHeader {
1138 status: Some(Status {
1139 status_code: StatusCode::Success as _,
1140 ..Default::default()
1141 }),
1142 }),
1143 affected_rows: response.affected_rows as _,
1144 extensions: response.extensions,
1145 metadata: response.metadata,
1146 failures: Vec::new(),
1147 })
1148 }
1149
1150 async fn handle_write_request(
1151 &self,
1152 request: region_request::Body,
1153 entry_count: usize,
1154 accept_partial_result: bool,
1155 ) -> ServerResult<RegionResponseV1> {
1156 let failed_requests_cnt = crate::metrics::REGION_SERVER_REQUEST_FAILURE_COUNT
1157 .with_label_values(&[request.as_ref()]);
1158 let mut result = self
1159 .handle_requests_in_parallel(request)
1160 .await
1161 .map_err(BoxedError::new)
1162 .inspect_err(|_| failed_requests_cnt.inc())
1163 .context(ExecuteGrpcRequestSnafu)?;
1164 if !result.failures.is_empty() {
1165 failed_requests_cnt.inc();
1166 if (!accept_partial_result || entry_count == 1)
1168 && let Some((_, error)) = result.failures.drain(..).min_by_key(|(index, _)| *index)
1169 {
1170 return Err(BoxedError::new(error)).context(ExecuteGrpcRequestSnafu);
1171 }
1172 }
1173 Ok(encode_batch_write_response(result))
1174 }
1175}
1176
1177#[async_trait]
1178impl FlightCraft for RegionServer {
1179 async fn do_get(
1180 &self,
1181 request: Request<Ticket>,
1182 ) -> TonicResult<Response<TonicStream<FlightData>>> {
1183 ensure!(!self.is_suspended(), SuspendedSnafu);
1184
1185 let ticket = request.into_inner().ticket;
1186 let request = api::v1::region::QueryRequest::decode(ticket.as_ref())
1187 .context(servers_error::InvalidFlightTicketSnafu)?;
1188 let tracing_context = request
1189 .header
1190 .as_ref()
1191 .map(|h| TracingContext::from_w3c(&h.tracing_context))
1192 .unwrap_or_default();
1193 let query_ctx = request
1194 .header
1195 .as_ref()
1196 .map(QueryContext::try_from)
1197 .transpose()
1198 .map_err(|err| {
1199 servers_error::InvalidParameterSnafu {
1200 reason: err.to_string(),
1201 }
1202 .build()
1203 })?
1204 .map(Arc::new)
1205 .unwrap_or(QueryContext::arc());
1206
1207 let region_server = self.clone();
1208 let initializer_query_ctx = query_ctx.clone();
1209 let initializer_tracing_context = tracing_context.clone();
1210 let initializer = async move {
1211 region_server
1212 .handle_remote_read(request, initializer_query_ctx)
1213 .trace(initializer_tracing_context.attach(info_span!("RegionServer::handle_read")))
1214 .await
1215 .map_err(Into::into)
1216 };
1217
1218 let stream = Box::pin(FlightRecordBatchStream::new(
1219 FlightRecordBatchStreamInput::initializer(async move {
1220 initializer
1221 .await
1222 .map(FlightRecordBatchSource::RecordBatches)
1223 }),
1224 tracing_context,
1225 self.flight_compression,
1226 query_ctx,
1227 ));
1228 Ok(Response::new(stream))
1229 }
1230}
1231
1232struct IndexedRegionRequest {
1234 index: usize,
1235 region_id: RegionId,
1236 request: RegionRequest,
1237}
1238
1239fn prepare_batch_write_requests(body: region_request::Body) -> Vec<IndexedRegionRequest> {
1240 match body {
1241 region_request::Body::Inserts(inserts) => inserts
1242 .requests
1243 .into_iter()
1244 .enumerate()
1245 .filter_map(|(index, request)| {
1246 store_region_request::make_region_put(request).map(|(region_id, request)| {
1247 IndexedRegionRequest {
1248 index,
1249 region_id,
1250 request: RegionRequest::Put(request),
1251 }
1252 })
1253 })
1254 .collect(),
1255 region_request::Body::Deletes(deletes) => deletes
1256 .requests
1257 .into_iter()
1258 .enumerate()
1259 .filter_map(|(index, request)| {
1260 store_region_request::make_region_delete(request).map(|(region_id, request)| {
1261 IndexedRegionRequest {
1262 index,
1263 region_id,
1264 request: RegionRequest::Delete(request),
1265 }
1266 })
1267 })
1268 .collect(),
1269 _ => unreachable!("Only inserts/deletes are prepared as batch writes"),
1270 }
1271}
1272
1273fn encode_batch_write_response(result: BatchRegionResponse<error::Error>) -> RegionResponseV1 {
1274 RegionResponseV1 {
1275 header: Some(ResponseHeader {
1276 status: Some(Status {
1277 status_code: if result.failures.is_empty() {
1278 StatusCode::Success
1279 } else {
1280 StatusCode::PartialFailure
1281 } as u32,
1282 err_msg: if let Some((index, error)) = result.first_failure() {
1283 format!(
1284 "{} region requests failed; entry {index}: {}",
1285 result.failures.len(),
1286 error.output_msg()
1287 )
1288 } else {
1289 String::new()
1290 },
1291 ..Default::default()
1292 }),
1293 }),
1294 metadata: Vec::new(),
1295 affected_rows: result.affected_rows as _,
1296 extensions: result.extensions,
1297 failures: result
1298 .failures
1299 .into_iter()
1300 .map(|(index, error)| RegionRequestFailure {
1301 request_index: index as u32,
1302 status: Some(Status {
1303 status_code: error.status_code() as u32,
1304 err_msg: error.output_msg(),
1305 retry_hint: error.retry_hint().as_str().to_string(),
1306 }),
1307 })
1308 .collect(),
1309 }
1310}
1311
1312fn log_batch_write_error(error: &error::Error, index: usize) {
1313 if error.status_code().should_log_error() {
1314 error!(error; "Failed to handle batch entry, request_index: {}", index);
1315 } else {
1316 debug!(
1317 "Failed to handle batch entry, request_index: {}, err: {}",
1318 index, error
1319 );
1320 }
1321}
1322
1323#[derive(Clone)]
1324enum RegionEngineWithStatus {
1325 Registering(RegionEngineRef),
1327 Deregistering(RegionEngineRef),
1329 Ready(RegionEngineRef),
1331}
1332
1333impl RegionEngineWithStatus {
1334 pub fn into_engine(self) -> RegionEngineRef {
1336 match self {
1337 RegionEngineWithStatus::Registering(engine) => engine,
1338 RegionEngineWithStatus::Deregistering(engine) => engine,
1339 RegionEngineWithStatus::Ready(engine) => engine,
1340 }
1341 }
1342}
1343
1344impl Deref for RegionEngineWithStatus {
1345 type Target = RegionEngineRef;
1346
1347 fn deref(&self) -> &Self::Target {
1348 match self {
1349 RegionEngineWithStatus::Registering(engine) => engine,
1350 RegionEngineWithStatus::Deregistering(engine) => engine,
1351 RegionEngineWithStatus::Ready(engine) => engine,
1352 }
1353 }
1354}
1355
1356struct RegionServerInner {
1357 engines: RwLock<HashMap<String, RegionEngineRef>>,
1358 region_map: DashMap<RegionId, RegionEngineWithStatus>,
1359 query_engine: QueryEngineRef,
1360 runtime: Runtime,
1361 event_listener: RegionServerEventListenerRef,
1362 table_provider_factory: TableProviderFactoryRef,
1363 parallelism: Option<RegionServerParallelism>,
1366 topic_stats_reporter: RwLock<Option<Box<dyn TopicStatsReporter>>>,
1368 mito_engine: RwLock<Option<MitoEngine>>,
1372 initial_remote_dyn_filter_registrations: RemoteDynFilterRegistry,
1375}
1376
1377struct RegionServerParallelism {
1378 semaphore: Arc<Semaphore>,
1379 timeout: Duration,
1380}
1381
1382impl RegionServerParallelism {
1383 pub fn from_opts(
1384 max_concurrent_queries: usize,
1385 concurrent_query_limiter_timeout: Duration,
1386 ) -> Option<Self> {
1387 if max_concurrent_queries == 0 {
1388 return None;
1389 }
1390 Some(RegionServerParallelism {
1391 semaphore: Arc::new(Semaphore::new(max_concurrent_queries)),
1392 timeout: concurrent_query_limiter_timeout,
1393 })
1394 }
1395
1396 pub async fn acquire(&self) -> Result<OwnedSemaphorePermit> {
1397 timeout(self.timeout, self.semaphore.clone().acquire_owned())
1398 .await
1399 .context(ConcurrentQueryLimiterTimeoutSnafu)?
1400 .context(ConcurrentQueryLimiterClosedSnafu)
1401 }
1402}
1403
1404struct PermitGuardedStream {
1408 inner: SendableRecordBatchStream,
1409 _permit: OwnedSemaphorePermit,
1410}
1411
1412impl RecordBatchStream for PermitGuardedStream {
1413 fn name(&self) -> &str {
1414 self.inner.name()
1415 }
1416
1417 fn schema(&self) -> SchemaRef {
1418 self.inner.schema()
1419 }
1420
1421 fn output_ordering(&self) -> Option<&[OrderOption]> {
1422 self.inner.output_ordering()
1423 }
1424
1425 fn metrics(&self) -> Option<RecordBatchMetrics> {
1426 self.inner.metrics()
1427 }
1428}
1429
1430impl Stream for PermitGuardedStream {
1431 type Item = common_recordbatch::error::Result<RecordBatch>;
1432
1433 fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
1434 self.inner.as_mut().poll_next(cx)
1435 }
1436}
1437
1438fn maybe_guard_stream(
1441 stream: SendableRecordBatchStream,
1442 permit: Option<OwnedSemaphorePermit>,
1443) -> SendableRecordBatchStream {
1444 match permit {
1445 Some(permit) => Box::pin(PermitGuardedStream {
1446 inner: stream,
1447 _permit: permit,
1448 }),
1449 None => stream,
1450 }
1451}
1452
1453enum CurrentEngine {
1454 Engine(RegionEngineRef),
1455 EarlyReturn(AffectedRows),
1456}
1457
1458impl Debug for CurrentEngine {
1459 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1460 match self {
1461 CurrentEngine::Engine(engine) => f
1462 .debug_struct("CurrentEngine")
1463 .field("engine", &engine.name())
1464 .finish(),
1465 CurrentEngine::EarlyReturn(rows) => f
1466 .debug_struct("CurrentEngine")
1467 .field("return", rows)
1468 .finish(),
1469 }
1470 }
1471}
1472
1473impl RegionServerInner {
1474 pub fn new(
1475 query_engine: QueryEngineRef,
1476 runtime: Runtime,
1477 event_listener: RegionServerEventListenerRef,
1478 table_provider_factory: TableProviderFactoryRef,
1479 parallelism: Option<RegionServerParallelism>,
1480 ) -> Self {
1481 Self {
1482 engines: RwLock::new(HashMap::new()),
1483 region_map: DashMap::new(),
1484 query_engine,
1485 runtime,
1486 event_listener,
1487 table_provider_factory,
1488 parallelism,
1489 topic_stats_reporter: RwLock::new(None),
1490 mito_engine: RwLock::new(None),
1491 initial_remote_dyn_filter_registrations: RemoteDynFilterRegistry::new(),
1492 }
1493 }
1494
1495 pub fn register_engine(&self, engine: RegionEngineRef) {
1496 let engine_name = engine.name();
1497 if engine_name == MITO_ENGINE_NAME
1498 && let Some(mito_engine) = engine.as_any().downcast_ref::<MitoEngine>()
1499 {
1500 *self.mito_engine.write().unwrap() = Some(mito_engine.clone());
1501 }
1502
1503 info!("Region Engine {engine_name} is registered");
1504 self.engines
1505 .write()
1506 .unwrap()
1507 .insert(engine_name.to_string(), engine);
1508 }
1509
1510 pub fn set_topic_stats_reporter(&self, topic_stats_reporter: Box<dyn TopicStatsReporter>) {
1511 info!("Set topic stats reporter");
1512 *self.topic_stats_reporter.write().unwrap() = Some(topic_stats_reporter);
1513 }
1514
1515 fn is_ingest_request(request: &RegionRequest) -> bool {
1516 matches!(
1517 request,
1518 RegionRequest::Put(_) | RegionRequest::Delete(_) | RegionRequest::BulkInserts(_)
1519 )
1520 }
1521
1522 fn get_engine(
1523 &self,
1524 region_id: RegionId,
1525 region_change: &RegionChange,
1526 ) -> Result<CurrentEngine> {
1527 let current_region_status = self.region_map.get(®ion_id);
1528
1529 let engine = match region_change {
1530 RegionChange::Register(attribute) => match current_region_status {
1531 Some(status) => match status.clone() {
1532 RegionEngineWithStatus::Registering(engine) => engine,
1533 RegionEngineWithStatus::Deregistering(_) => {
1534 return error::RegionBusySnafu { region_id }.fail();
1535 }
1536 RegionEngineWithStatus::Ready(_) => status.clone().into_engine(),
1537 },
1538 _ => self
1539 .engines
1540 .read()
1541 .unwrap()
1542 .get(attribute.engine())
1543 .with_context(|| RegionEngineNotFoundSnafu {
1544 name: attribute.engine(),
1545 })?
1546 .clone(),
1547 },
1548 RegionChange::OfflineCleanup(attribute) => match current_region_status {
1549 Some(status) => match status.clone() {
1550 RegionEngineWithStatus::Registering(_)
1551 | RegionEngineWithStatus::Deregistering(_)
1552 | RegionEngineWithStatus::Ready(_) => {
1553 return error::RegionBusySnafu { region_id }.fail();
1554 }
1555 },
1556 None => self
1557 .engines
1558 .read()
1559 .unwrap()
1560 .get(attribute.engine())
1561 .with_context(|| RegionEngineNotFoundSnafu {
1562 name: attribute.engine(),
1563 })?
1564 .clone(),
1565 },
1566 RegionChange::Deregisters => match current_region_status {
1567 Some(status) => match status.clone() {
1568 RegionEngineWithStatus::Registering(_) => {
1569 return error::RegionBusySnafu { region_id }.fail();
1570 }
1571 RegionEngineWithStatus::Deregistering(_) => {
1572 return Ok(CurrentEngine::EarlyReturn(0));
1573 }
1574 RegionEngineWithStatus::Ready(_) => status.clone().into_engine(),
1575 },
1576 None => return Ok(CurrentEngine::EarlyReturn(0)),
1577 },
1578 RegionChange::None | RegionChange::Catchup | RegionChange::Ingest => {
1579 match current_region_status {
1580 Some(status) => match status.clone() {
1581 RegionEngineWithStatus::Registering(_) => {
1582 return error::RegionNotReadySnafu { region_id }.fail();
1583 }
1584 RegionEngineWithStatus::Deregistering(_) => {
1585 return error::RegionNotFoundSnafu { region_id }.fail();
1586 }
1587 RegionEngineWithStatus::Ready(engine) => engine,
1588 },
1589 None => return error::RegionNotFoundSnafu { region_id }.fail(),
1590 }
1591 }
1592 };
1593
1594 Ok(CurrentEngine::Engine(engine))
1595 }
1596
1597 async fn handle_batch_open_requests_inner(
1598 &self,
1599 engine: RegionEngineRef,
1600 parallelism: usize,
1601 requests: Vec<(RegionId, RegionOpenRequest)>,
1602 ignore_nonexistent_region: bool,
1603 ) -> Result<Vec<RegionId>> {
1604 let request_count = requests.len();
1605 let region_changes = requests
1606 .iter()
1607 .map(|(region_id, open)| {
1608 let attribute = parse_region_attribute(&open.engine, &open.options)?;
1609 Ok((*region_id, RegionChange::Register(attribute)))
1610 })
1611 .collect::<Result<HashMap<_, _>>>()
1612 .inspect_err(|err| {
1613 record_region_open_failures(engine.name(), err.status_code(), request_count);
1614 })?;
1615
1616 for (®ion_id, region_change) in ®ion_changes {
1617 self.set_region_status_not_ready(region_id, &engine, region_change)
1618 }
1619
1620 let mut open_regions = Vec::with_capacity(requests.len());
1621 let mut errors = vec![];
1622 match engine
1623 .handle_batch_open_requests(parallelism, requests)
1624 .await
1625 .with_context(|_| HandleBatchOpenRequestSnafu)
1626 {
1627 Ok(results) => {
1628 for (region_id, result) in results {
1629 let region_change = ®ion_changes[®ion_id];
1630 match result {
1631 Ok(_) => {
1632 if let Err(e) = self
1633 .set_region_status_ready(region_id, engine.clone(), *region_change)
1634 .await
1635 {
1636 error!(e; "Failed to set region to ready: {}", region_id);
1637 record_region_open_failures(engine.name(), e.status_code(), 1);
1638 errors.push(BoxedError::new(e));
1639 } else {
1640 open_regions.push(region_id)
1641 }
1642 }
1643 Err(e) => {
1644 self.unset_region_status(region_id, &engine, *region_change);
1645 if e.status_code() == StatusCode::RegionNotFound
1646 && ignore_nonexistent_region
1647 {
1648 warn!("Region {} not found, ignore it, source: {:?}", region_id, e);
1649 } else {
1650 error!(e; "Failed to open region: {}", region_id);
1651 record_region_open_failures(engine.name(), e.status_code(), 1);
1652 errors.push(e);
1653 }
1654 }
1655 }
1656 }
1657 }
1658 Err(e) => {
1659 for (®ion_id, region_change) in ®ion_changes {
1660 self.unset_region_status(region_id, &engine, *region_change);
1661 }
1662 error!(e; "Failed to open batch regions");
1663 record_region_open_failures(engine.name(), e.status_code(), request_count);
1664 errors.push(BoxedError::new(e));
1665 }
1666 }
1667
1668 if !errors.is_empty() {
1669 return Err(errors.swap_remove(0)).context(HandleBatchOpenRequestSnafu);
1671 }
1672
1673 Ok(open_regions)
1674 }
1675
1676 pub async fn handle_batch_open_requests(
1677 &self,
1678 parallelism: usize,
1679 requests: Vec<(RegionId, RegionOpenRequest)>,
1680 ignore_nonexistent_region: bool,
1681 ) -> Result<Vec<RegionId>> {
1682 let mut engine_grouped_requests: HashMap<String, Vec<_>> =
1683 HashMap::with_capacity(requests.len());
1684 for (region_id, request) in requests {
1685 if let Some(requests) = engine_grouped_requests.get_mut(&request.engine) {
1686 requests.push((region_id, request));
1687 } else {
1688 engine_grouped_requests.insert(request.engine.clone(), vec![(region_id, request)]);
1689 }
1690 }
1691
1692 let mut results = Vec::with_capacity(engine_grouped_requests.keys().len());
1693 for (engine, requests) in engine_grouped_requests {
1694 let engine = self
1695 .engines
1696 .read()
1697 .unwrap()
1698 .get(&engine)
1699 .with_context(|| RegionEngineNotFoundSnafu { name: &engine })
1700 .inspect_err(|err| {
1701 record_region_open_failures(&engine, err.status_code(), requests.len());
1702 })?
1703 .clone();
1704 results.push(
1705 self.handle_batch_open_requests_inner(
1706 engine,
1707 parallelism,
1708 requests,
1709 ignore_nonexistent_region,
1710 )
1711 .await,
1712 )
1713 }
1714
1715 Ok(results
1716 .into_iter()
1717 .collect::<Result<Vec<_>>>()?
1718 .into_iter()
1719 .flatten()
1720 .collect::<Vec<_>>())
1721 }
1722
1723 pub async fn handle_batch_catchup_requests_inner(
1724 &self,
1725 engine: RegionEngineRef,
1726 parallelism: usize,
1727 requests: Vec<(RegionId, RegionCatchupRequest)>,
1728 ) -> Result<Vec<(RegionId, std::result::Result<(), BoxedError>)>> {
1729 for (region_id, _) in &requests {
1730 self.set_region_status_not_ready(*region_id, &engine, &RegionChange::Catchup);
1731 }
1732 let region_ids = requests
1733 .iter()
1734 .map(|(region_id, _)| *region_id)
1735 .collect::<Vec<_>>();
1736 let mut responses = Vec::with_capacity(requests.len());
1737 match engine
1738 .handle_batch_catchup_requests(parallelism, requests)
1739 .await
1740 {
1741 Ok(results) => {
1742 for (region_id, result) in results {
1743 match result {
1744 Ok(_) => {
1745 if let Err(e) = self
1746 .set_region_status_ready(
1747 region_id,
1748 engine.clone(),
1749 RegionChange::Catchup,
1750 )
1751 .await
1752 {
1753 error!(e; "Failed to set region to ready: {}", region_id);
1754 responses.push((region_id, Err(BoxedError::new(e))));
1755 } else {
1756 responses.push((region_id, Ok(())));
1757 }
1758 }
1759 Err(e) => {
1760 self.unset_region_status(region_id, &engine, RegionChange::Catchup);
1761 error!(e; "Failed to catchup region: {}", region_id);
1762 responses.push((region_id, Err(e)));
1763 }
1764 }
1765 }
1766 }
1767 Err(e) => {
1768 for region_id in region_ids {
1769 self.unset_region_status(region_id, &engine, RegionChange::Catchup);
1770 }
1771 error!(e; "Failed to catchup batch regions");
1772 return error::UnexpectedSnafu {
1773 violated: format!("Failed to catchup batch regions: {:?}", e),
1774 }
1775 .fail();
1776 }
1777 }
1778
1779 Ok(responses)
1780 }
1781
1782 pub async fn handle_batch_catchup_requests(
1783 &self,
1784 parallelism: usize,
1785 requests: Vec<(RegionId, RegionCatchupRequest)>,
1786 ) -> Result<Vec<(RegionId, std::result::Result<(), BoxedError>)>> {
1787 let mut engine_grouped_requests: HashMap<String, Vec<_>> = HashMap::new();
1788
1789 let mut responses = Vec::with_capacity(requests.len());
1790 for (region_id, request) in requests {
1791 if let Ok(engine) = self.get_engine(region_id, &RegionChange::Catchup) {
1792 match engine {
1793 CurrentEngine::Engine(engine) => {
1794 engine_grouped_requests
1795 .entry(engine.name().to_string())
1796 .or_default()
1797 .push((region_id, request));
1798 }
1799 CurrentEngine::EarlyReturn(_) => {
1800 return error::UnexpectedSnafu {
1801 violated: format!("Unexpected engine type for region {}", region_id),
1802 }
1803 .fail();
1804 }
1805 }
1806 } else {
1807 responses.push((
1808 region_id,
1809 Err(BoxedError::new(
1810 error::RegionNotFoundSnafu { region_id }.build(),
1811 )),
1812 ));
1813 }
1814 }
1815
1816 for (engine, requests) in engine_grouped_requests {
1817 let engine = self
1818 .engines
1819 .read()
1820 .unwrap()
1821 .get(&engine)
1822 .with_context(|| RegionEngineNotFoundSnafu { name: &engine })?
1823 .clone();
1824 responses.extend(
1825 self.handle_batch_catchup_requests_inner(engine, parallelism, requests)
1826 .await?,
1827 );
1828 }
1829
1830 Ok(responses)
1831 }
1832
1833 pub async fn handle_batch_ddl_requests(
1837 &self,
1838 batch_request: BatchRegionDdlRequest,
1839 ) -> Result<RegionResponse> {
1840 let region_changes = match &batch_request {
1841 BatchRegionDdlRequest::Create(requests) => requests
1842 .iter()
1843 .map(|(region_id, create)| {
1844 let attribute = parse_region_attribute(&create.engine, &create.options)?;
1845 Ok((*region_id, RegionChange::Register(attribute)))
1846 })
1847 .collect::<Result<Vec<_>>>()?,
1848 BatchRegionDdlRequest::Drop(requests) => requests
1849 .iter()
1850 .map(|(region_id, _)| (*region_id, RegionChange::Deregisters))
1851 .collect::<Vec<_>>(),
1852 BatchRegionDdlRequest::Alter(requests) => requests
1853 .iter()
1854 .map(|(region_id, _)| (*region_id, RegionChange::None))
1855 .collect::<Vec<_>>(),
1856 };
1857
1858 let (first_region_id, first_region_change) = region_changes.first().unwrap();
1861 let engine = match self.get_engine(*first_region_id, first_region_change)? {
1862 CurrentEngine::Engine(engine) => engine,
1863 CurrentEngine::EarlyReturn(rows) => return Ok(RegionResponse::new(rows)),
1864 };
1865
1866 for (region_id, region_change) in region_changes.iter() {
1867 self.set_region_status_not_ready(*region_id, &engine, region_change);
1868 }
1869
1870 let ddl_type = batch_request.request_type();
1871 let result = engine
1872 .handle_batch_ddl_requests(batch_request)
1873 .await
1874 .context(HandleBatchDdlRequestSnafu { ddl_type });
1875
1876 match result {
1877 Ok(result) => {
1878 for (region_id, region_change) in ®ion_changes {
1879 self.set_region_status_ready(*region_id, engine.clone(), *region_change)
1880 .await?;
1881 }
1882
1883 Ok(RegionResponse {
1884 affected_rows: result.affected_rows,
1885 extensions: result.extensions,
1886 metadata: Vec::new(),
1887 })
1888 }
1889 Err(err) => {
1890 for (region_id, region_change) in region_changes {
1891 self.unset_region_status(region_id, &engine, region_change);
1892 }
1893
1894 Err(err)
1895 }
1896 }
1897 }
1898
1899 pub async fn handle_request(
1900 &self,
1901 region_id: RegionId,
1902 request: RegionRequest,
1903 ) -> Result<RegionResponse> {
1904 let open_engine = match &request {
1905 RegionRequest::Open(open) => Some(open.engine.clone()),
1906 _ => None,
1907 };
1908 self.handle_request_inner(region_id, request)
1909 .await
1910 .inspect_err(|err| {
1911 if let Some(engine) = open_engine {
1912 record_region_open_failures(&engine, err.status_code(), 1);
1913 }
1914 })
1915 }
1916
1917 async fn handle_request_inner(
1918 &self,
1919 region_id: RegionId,
1920 request: RegionRequest,
1921 ) -> Result<RegionResponse> {
1922 let request_type = request.request_type();
1923 let _timer = crate::metrics::HANDLE_REGION_REQUEST_ELAPSED
1924 .with_label_values(&[request_type])
1925 .start_timer();
1926
1927 let region_change = match &request {
1928 RegionRequest::Create(create) => {
1929 let attribute = parse_region_attribute(&create.engine, &create.options)?;
1930 RegionChange::Register(attribute)
1931 }
1932 RegionRequest::Open(open) => {
1933 let attribute = parse_region_attribute(&open.engine, &open.options)?;
1934 RegionChange::Register(attribute)
1935 }
1936 RegionRequest::CleanUp(clean_up) => {
1937 let attribute = parse_region_attribute(&clean_up.engine, &clean_up.options)?;
1938 RegionChange::OfflineCleanup(attribute)
1939 }
1940 RegionRequest::Close(_) | RegionRequest::Drop(_) => RegionChange::Deregisters,
1941 RegionRequest::Put(_) | RegionRequest::Delete(_) | RegionRequest::BulkInserts(_) => {
1942 RegionChange::Ingest
1943 }
1944 RegionRequest::Alter(_)
1945 | RegionRequest::Flush(_)
1946 | RegionRequest::Compact(_)
1947 | RegionRequest::Truncate(_)
1948 | RegionRequest::BuildIndex(_)
1949 | RegionRequest::EnterStaging(_)
1950 | RegionRequest::ApplyStagingManifest(_) => RegionChange::None,
1951 RegionRequest::Catchup(_) => RegionChange::Catchup,
1952 };
1953
1954 let engine = match self.get_engine(region_id, ®ion_change)? {
1955 CurrentEngine::Engine(engine) => engine,
1956 CurrentEngine::EarlyReturn(rows) => return Ok(RegionResponse::new(rows)),
1957 };
1958
1959 self.set_region_status_not_ready(region_id, &engine, ®ion_change);
1961
1962 let result = engine
1963 .handle_request(region_id, request)
1964 .await
1965 .with_context(|_| HandleRegionRequestSnafu { region_id });
1966 if matches!(region_change, RegionChange::Ingest) {
1967 record_ingest_result(request_type, 1, &result);
1968 }
1969 match result {
1970 Ok(result) => {
1971 self.set_region_status_ready(region_id, engine.clone(), region_change)
1973 .await?;
1974
1975 Ok(RegionResponse {
1976 affected_rows: result.affected_rows,
1977 extensions: result.extensions,
1978 metadata: Vec::new(),
1979 })
1980 }
1981 Err(err) => {
1982 self.unset_region_status(region_id, &engine, region_change);
1984 Err(err)
1985 }
1986 }
1987 }
1988
1989 pub async fn handle_sync_region(
1991 &self,
1992 engine: &RegionEngineRef,
1993 region_id: RegionId,
1994 request: SyncRegionFromRequest,
1995 ) -> Result<()> {
1996 let Some(new_opened_regions) = engine
1997 .sync_region(region_id, request)
1998 .await
1999 .with_context(|_| HandleRegionRequestSnafu { region_id })?
2000 .new_opened_logical_region_ids()
2001 else {
2002 return Ok(());
2003 };
2004
2005 for region in &new_opened_regions {
2006 self.region_map
2007 .insert(*region, RegionEngineWithStatus::Ready(engine.clone()));
2008 }
2009 if !new_opened_regions.is_empty() {
2010 info!(
2011 region_id = %region_id,
2012 logical_region_count = new_opened_regions.len(),
2013 logical_regions = ?new_opened_regions,
2014 "Logical regions are registered"
2015 );
2016 }
2017
2018 Ok(())
2019 }
2020
2021 fn set_region_status_not_ready(
2022 &self,
2023 region_id: RegionId,
2024 engine: &RegionEngineRef,
2025 region_change: &RegionChange,
2026 ) {
2027 match region_change {
2028 RegionChange::Register(_) => {
2029 self.region_map.insert(
2030 region_id,
2031 RegionEngineWithStatus::Registering(engine.clone()),
2032 );
2033 }
2034 RegionChange::Deregisters => {
2035 self.region_map.insert(
2036 region_id,
2037 RegionEngineWithStatus::Deregistering(engine.clone()),
2038 );
2039 }
2040 _ => {}
2041 }
2042 }
2043
2044 fn unset_region_status(
2045 &self,
2046 region_id: RegionId,
2047 engine: &RegionEngineRef,
2048 region_change: RegionChange,
2049 ) {
2050 match region_change {
2051 RegionChange::None | RegionChange::Ingest | RegionChange::OfflineCleanup(_) => {}
2052 RegionChange::Register(_) => {
2053 self.region_map.remove(®ion_id);
2054 }
2055 RegionChange::Deregisters => {
2056 self.region_map
2057 .insert(region_id, RegionEngineWithStatus::Ready(engine.clone()));
2058 }
2059 RegionChange::Catchup => {}
2060 }
2061 }
2062
2063 async fn set_region_status_ready(
2064 &self,
2065 region_id: RegionId,
2066 engine: RegionEngineRef,
2067 region_change: RegionChange,
2068 ) -> Result<()> {
2069 let engine_type = engine.name();
2070 match region_change {
2071 RegionChange::None | RegionChange::Ingest | RegionChange::OfflineCleanup(_) => {}
2072 RegionChange::Register(attribute) => {
2073 info!(
2074 "Region {region_id} is registered to engine {}",
2075 attribute.engine()
2076 );
2077 self.region_map
2078 .insert(region_id, RegionEngineWithStatus::Ready(engine.clone()));
2079
2080 match attribute {
2081 RegionAttribute::Metric { physical } => {
2082 if physical {
2083 self.register_logical_regions(&engine, region_id).await?;
2085 self.event_listener.on_region_registered(region_id);
2087 }
2088 }
2089 RegionAttribute::Mito => self.event_listener.on_region_registered(region_id),
2090 RegionAttribute::File => {
2091 }
2093 }
2094 }
2095 RegionChange::Deregisters => {
2096 info!("Region {region_id} is deregistered from engine {engine_type}");
2097 self.region_map
2098 .remove(®ion_id)
2099 .map(|(id, engine)| engine.set_region_role(id, RegionRole::Follower));
2100 self.event_listener.on_region_deregistered(region_id);
2101 }
2102 RegionChange::Catchup => {
2103 if is_metric_engine(engine.name()) {
2104 self.register_logical_regions(&engine, region_id).await?;
2106 }
2107 }
2108 }
2109 Ok(())
2110 }
2111
2112 async fn register_logical_regions(
2113 &self,
2114 engine: &RegionEngineRef,
2115 physical_region_id: RegionId,
2116 ) -> Result<()> {
2117 let metric_engine =
2118 engine
2119 .as_any()
2120 .downcast_ref::<MetricEngine>()
2121 .context(UnexpectedSnafu {
2122 violated: format!(
2123 "expecting engine type '{}', actual '{}'",
2124 METRIC_ENGINE_NAME,
2125 engine.name(),
2126 ),
2127 })?;
2128
2129 let logical_regions = metric_engine
2130 .logical_regions(physical_region_id)
2131 .await
2132 .context(FindLogicalRegionsSnafu { physical_region_id })?;
2133
2134 for region in &logical_regions {
2135 self.region_map
2136 .insert(*region, RegionEngineWithStatus::Ready(engine.clone()));
2137 }
2138 if !logical_regions.is_empty() {
2139 info!(
2140 physical_region_id = %physical_region_id,
2141 logical_region_count = logical_regions.len(),
2142 logical_regions = ?logical_regions,
2143 "Logical regions are registered"
2144 );
2145 }
2146 Ok(())
2147 }
2148
2149 pub async fn handle_read(
2150 self: &Arc<Self>,
2151 request: QueryRequest,
2152 query_ctx: QueryContextRef,
2153 ) -> Result<SendableRecordBatchStream> {
2154 let live_analyze_metrics =
2155 query_ctx.explain_verbose() && query_ctx.live_analyze_metrics_enabled();
2156 let inner = self.clone();
2157 let mut stream = common_runtime::spawn_query(async move {
2158 inner.handle_read_inner(request, query_ctx).await
2159 })
2160 .await
2161 .context(RuntimeJoinSnafu {
2162 request_type: "read",
2163 })??;
2164 let schema = stream.schema();
2165 let output_ordering = stream.output_ordering().map(|ordering| ordering.to_vec());
2166
2167 let (sender, receiver) = mpsc::channel(QUERY_RUNTIME_STREAM_BUFFER_SIZE);
2168 let metrics = QueryRuntimeStream::metrics_store();
2169 let producer_metrics = metrics.clone();
2170
2171 let producer_handle = common_runtime::spawn_query(async move {
2172 if live_analyze_metrics {
2173 loop {
2174 match time::timeout(FLIGHT_METRICS_HEARTBEAT_INTERVAL, stream.next()).await {
2175 Ok(Some(batch)) => {
2176 *producer_metrics.write().unwrap() = stream.metrics();
2177 if sender.send(batch).await.is_err() {
2178 return;
2179 }
2180 }
2181 Ok(None) => break,
2182 Err(_) => {
2183 *producer_metrics.write().unwrap() = stream.metrics();
2184 }
2185 }
2186 }
2187 } else {
2188 while let Some(batch) = stream.next().await {
2189 *producer_metrics.write().unwrap() = stream.metrics();
2190 if sender.send(batch).await.is_err() {
2191 break;
2192 }
2193 }
2194 }
2195 *producer_metrics.write().unwrap() = stream.metrics();
2196 });
2197
2198 Ok(Box::pin(
2199 QueryRuntimeStream::new(schema, receiver)
2200 .with_output_ordering(output_ordering)
2201 .with_metrics_store(metrics)
2202 .with_producer_handle(producer_handle),
2203 ))
2204 }
2205
2206 async fn handle_read_inner(
2207 &self,
2208 request: QueryRequest,
2209 query_ctx: QueryContextRef,
2210 ) -> Result<SendableRecordBatchStream> {
2211 let result = self
2214 .query_engine
2215 .execute(request.plan, query_ctx)
2216 .await
2217 .context(ExecuteLogicalPlanSnafu)?;
2218
2219 match result.data {
2220 OutputData::AffectedRows(_) | OutputData::RecordBatches(_) => {
2221 UnsupportedOutputSnafu { expected: "stream" }.fail()
2222 }
2223 OutputData::Stream(stream) => Ok(stream),
2224 }
2225 }
2226
2227 async fn stop(&self) -> Result<()> {
2228 let regions = self
2236 .region_map
2237 .iter()
2238 .map(|x| (*x.key(), x.value().clone()))
2239 .collect::<Vec<_>>();
2240 let num_regions = regions.len();
2241
2242 let mut first_error = None;
2243 for (region_id, engine) in regions {
2244 let closed = engine
2245 .handle_request(
2246 region_id,
2247 RegionRequest::Close(RegionCloseRequest::default()),
2248 )
2249 .await;
2250 match closed {
2251 Ok(_) => debug!("Region {region_id} is closed"),
2252 Err(e) => {
2253 error!(e; "Failed to close region {region_id}; writes that skipped the WAL may be lost");
2256 record_shutdown_error(
2257 &mut first_error,
2258 Err(e).context(HandleRegionRequestSnafu { region_id }),
2259 );
2260 }
2261 }
2262 }
2263 self.region_map.clear();
2264 info!("closed {num_regions} regions");
2265
2266 drop(self.mito_engine.write().unwrap().take());
2267 let engines = self.engines.write().unwrap().drain().collect::<Vec<_>>();
2268 for (engine_name, engine) in engines {
2269 let result = engine
2270 .stop()
2271 .await
2272 .context(StopRegionEngineSnafu { name: &engine_name });
2273 if result.is_ok() {
2274 info!("Region engine {engine_name} is stopped");
2275 }
2276 record_shutdown_error(&mut first_error, result);
2277 }
2278
2279 first_error.map_or(Ok(()), Err)
2280 }
2281}
2282
2283#[derive(Debug, Clone, Copy)]
2284enum RegionChange {
2285 None,
2286 Register(RegionAttribute),
2287 OfflineCleanup(RegionAttribute),
2288 Deregisters,
2289 Catchup,
2290 Ingest,
2291}
2292
2293fn is_metric_engine(engine: &str) -> bool {
2294 engine == METRIC_ENGINE_NAME
2295}
2296
2297fn record_region_open_failures(engine: &str, status_code: StatusCode, count: usize) {
2299 let engine = match engine {
2300 MITO_ENGINE_NAME | METRIC_ENGINE_NAME | FILE_ENGINE_NAME => engine,
2301 _ => "unknown",
2302 };
2303 crate::metrics::REGION_OPEN_FAILURES_TOTAL
2304 .with_label_values(&[engine, status_code.as_ref()])
2305 .inc_by(count as u64);
2306}
2307
2308async fn join_ingest_task<T>(task: JoinHandle<T>, request_type: &'static str) -> Result<T> {
2310 task.await
2311 .inspect_err(|error| {
2312 let reason = if error.is_panic() {
2313 "panic"
2314 } else {
2315 "cancelled"
2316 };
2317 crate::metrics::INGEST_TASK_FAILURES_TOTAL
2318 .with_label_values(&[request_type, reason])
2319 .inc();
2320 })
2321 .context(RuntimeJoinSnafu { request_type })
2322}
2323
2324fn record_ingest_result(request_type: &str, count: usize, result: &Result<RegionResponse>) {
2327 match result {
2328 Ok(response) => crate::metrics::REGION_CHANGED_ROW_COUNT
2329 .with_label_values(&[request_type])
2330 .inc_by(response.affected_rows as u64),
2331 Err(err) => crate::metrics::REGION_SERVER_INSERT_FAIL_COUNT
2332 .with_label_values(&[request_type, err.status_code().as_ref()])
2333 .inc_by(count as u64),
2334 }
2335}
2336
2337fn parse_region_attribute(
2338 engine: &str,
2339 options: &HashMap<String, String>,
2340) -> Result<RegionAttribute> {
2341 match engine {
2342 MITO_ENGINE_NAME => Ok(RegionAttribute::Mito),
2343 METRIC_ENGINE_NAME => {
2344 let physical = !options.contains_key(LOGICAL_TABLE_METADATA_KEY);
2345
2346 Ok(RegionAttribute::Metric { physical })
2347 }
2348 FILE_ENGINE_NAME => Ok(RegionAttribute::File),
2349 _ => error::UnexpectedSnafu {
2350 violated: format!("Unknown engine: {}", engine),
2351 }
2352 .fail(),
2353 }
2354}
2355
2356#[derive(Debug, Clone, Copy)]
2357enum RegionAttribute {
2358 Mito,
2359 Metric { physical: bool },
2360 File,
2361}
2362
2363impl RegionAttribute {
2364 fn engine(&self) -> &'static str {
2365 match self {
2366 RegionAttribute::Mito => MITO_ENGINE_NAME,
2367 RegionAttribute::Metric { .. } => METRIC_ENGINE_NAME,
2368 RegionAttribute::File => FILE_ENGINE_NAME,
2369 }
2370 }
2371}
2372
2373#[cfg(test)]
2374mod tests {
2375 use std::assert_matches;
2376 use std::collections::HashMap;
2377 use std::sync::Arc;
2378
2379 use api::v1::region::{InsertRequest, InsertRequests};
2380 use api::v1::{PartitionExprVersion, Rows, SemanticType};
2381 use common_error::ext::{ErrorExt, RetryHint};
2382 use common_recordbatch::RecordBatches;
2383 use common_recordbatch::adapter::{RecordBatchMetrics, RegionWatermarkEntry};
2384 use common_wal::config::object_store::{ObjectStoreWalConfig, STANDALONE_GENERATION};
2385 use common_wal::options::{ObjectStoreWalOptions, WAL_OPTIONS_KEY, WalOptions};
2386 use datatypes::prelude::{ConcreteDataType, VectorRef};
2387 use datatypes::schema::{ColumnSchema, Schema};
2388 use datatypes::vectors::Int32Vector;
2389 use futures_util::StreamExt;
2390 use log_store::ObjectStoreLogStore;
2391 use mito2::config::MitoConfig;
2392 use mito2::test_util::{CreateRequestBuilder, TestEnv, build_rows, rows_schema};
2393 use object_store::ObjectStore;
2394 use object_store::services::Memory;
2395 use query::options::FLOW_RETURN_REGION_SEQ;
2396 use store_api::metadata::{ColumnMetadata, RegionMetadata, RegionMetadataBuilder};
2397 use store_api::region_engine::RegionEngine;
2398 use store_api::region_request::{
2399 PathType, RegionCleanUpRequest, RegionCompactRequest, RegionDeleteRequest,
2400 RegionDropRequest, RegionOpenRequest, RegionPutRequest, RegionTruncateRequest,
2401 };
2402 use store_api::storage::RegionId;
2403
2404 use super::*;
2405 use crate::tests::{MockRegionEngine, mock_region_server};
2406
2407 #[test]
2408 fn test_is_ingest_request() {
2409 let rows = || Rows {
2410 schema: Vec::new(),
2411 rows: Vec::new(),
2412 };
2413
2414 assert!(RegionServerInner::is_ingest_request(&RegionRequest::Put(
2415 RegionPutRequest {
2416 skip_wal: false,
2417 rows: rows(),
2418 hint: None,
2419 partition_expr_version: None,
2420 },
2421 )));
2422 assert!(RegionServerInner::is_ingest_request(
2423 &RegionRequest::Delete(RegionDeleteRequest {
2424 rows: rows(),
2425 hint: None,
2426 partition_expr_version: None,
2427 },)
2428 ));
2429 assert!(!RegionServerInner::is_ingest_request(
2430 &RegionRequest::Compact(RegionCompactRequest::default()),
2431 ));
2432 }
2433
2434 fn single_value_stream() -> SendableRecordBatchStream {
2435 let schema = Arc::new(Schema::new(vec![ColumnSchema::new(
2436 "v",
2437 ConcreteDataType::int32_datatype(),
2438 false,
2439 )]));
2440 let values: VectorRef = Arc::new(Int32Vector::from_slice([1]));
2441 let batch = RecordBatch::new(schema.clone(), vec![values]).unwrap();
2442 RecordBatches::try_new(schema, vec![batch])
2443 .unwrap()
2444 .as_stream()
2445 }
2446
2447 #[tokio::test]
2448 async fn test_region_watermark_stream_only_sets_terminal_metrics() {
2449 let schema = Arc::new(Schema::new(vec![ColumnSchema::new(
2450 "v",
2451 ConcreteDataType::int32_datatype(),
2452 false,
2453 )]));
2454 let values: VectorRef = Arc::new(Int32Vector::from_slice([1, 2]));
2455 let batch = RecordBatch::new(schema.clone(), vec![values]).unwrap();
2456 let stream = RecordBatches::try_new(schema, vec![batch])
2457 .unwrap()
2458 .as_stream();
2459
2460 let region_id = RegionId::new(42, 7);
2461 let wrapped = RegionWatermarkStream::new(stream, region_id, 99);
2462 let mut pinned = Box::pin(wrapped);
2463
2464 assert!(pinned.as_ref().get_ref().metrics().is_none());
2465 while pinned.next().await.is_some() {}
2466
2467 let metrics = pinned.as_ref().get_ref().metrics().unwrap();
2468 assert_eq!(
2469 metrics.region_watermarks,
2470 vec![RegionWatermarkEntry {
2471 region_id: region_id.as_u64(),
2472 watermark: Some(99),
2473 }]
2474 );
2475 }
2476
2477 #[test]
2478 fn test_region_watermark_stream_preserves_unproved_watermark() {
2479 let schema = Arc::new(Schema::new(vec![ColumnSchema::new(
2480 "v",
2481 ConcreteDataType::int32_datatype(),
2482 false,
2483 )]));
2484 let values: VectorRef = Arc::new(Int32Vector::from_slice([1]));
2485 let batch = RecordBatch::new(schema.clone(), vec![values]).unwrap();
2486 let stream = RecordBatches::try_new(schema, vec![batch])
2487 .unwrap()
2488 .as_stream();
2489
2490 let region_id = RegionId::new(42, 7);
2491 let wrapped = RegionWatermarkStream::new(stream, region_id, 99);
2492 let metrics = RecordBatchMetrics {
2493 region_watermarks: vec![RegionWatermarkEntry {
2494 region_id: region_id.as_u64(),
2495 watermark: None,
2496 }],
2497 ..Default::default()
2498 };
2499
2500 let merged = wrapped.merged_metrics(metrics);
2501 assert_eq!(
2502 merged.region_watermarks,
2503 vec![RegionWatermarkEntry {
2504 region_id: region_id.as_u64(),
2505 watermark: None,
2506 }]
2507 );
2508 }
2509
2510 #[tokio::test]
2511 async fn test_wrap_flow_region_watermark_stream_adds_terminal_metrics() {
2512 let region_id = RegionId::new(42, 7);
2513 let query_ctx = Arc::new(
2514 QueryContextBuilder::default()
2515 .extensions(HashMap::from([(
2516 FLOW_RETURN_REGION_SEQ.to_string(),
2517 "true".to_string(),
2518 )]))
2519 .build(),
2520 );
2521 query_ctx.set_snapshot(region_id.as_u64(), 99);
2522
2523 let wrapped =
2524 wrap_flow_region_watermark_stream(single_value_stream(), region_id, &query_ctx);
2525 let mut pinned = Box::pin(wrapped);
2526 while pinned.next().await.is_some() {}
2527
2528 let metrics = pinned.as_ref().get_ref().metrics().unwrap();
2529 assert_eq!(
2530 metrics.region_watermarks,
2531 vec![RegionWatermarkEntry {
2532 region_id: region_id.as_u64(),
2533 watermark: Some(99),
2534 }]
2535 );
2536 }
2537
2538 #[tokio::test]
2539 async fn test_wrap_flow_region_watermark_stream_skips_without_extension() {
2540 let region_id = RegionId::new(42, 7);
2541 let query_ctx = Arc::new(QueryContextBuilder::default().build());
2542 query_ctx.set_snapshot(region_id.as_u64(), 99);
2543
2544 let wrapped =
2545 wrap_flow_region_watermark_stream(single_value_stream(), region_id, &query_ctx);
2546 let mut pinned = Box::pin(wrapped);
2547 while pinned.next().await.is_some() {}
2548
2549 assert!(pinned.as_ref().get_ref().metrics().is_none());
2550 }
2551
2552 #[tokio::test]
2553 async fn test_wrap_flow_region_watermark_stream_skips_without_snapshot() {
2554 let region_id = RegionId::new(42, 7);
2555 let query_ctx = Arc::new(
2556 QueryContextBuilder::default()
2557 .extensions(HashMap::from([(
2558 FLOW_RETURN_REGION_SEQ.to_string(),
2559 "true".to_string(),
2560 )]))
2561 .build(),
2562 );
2563
2564 let wrapped =
2565 wrap_flow_region_watermark_stream(single_value_stream(), region_id, &query_ctx);
2566 let mut pinned = Box::pin(wrapped);
2567 while pinned.next().await.is_some() {}
2568
2569 assert!(pinned.as_ref().get_ref().metrics().is_none());
2570 }
2571
2572 #[tokio::test]
2573 async fn test_offline_cleanup_does_not_register_region() {
2574 let mut mock_region_server = mock_region_server();
2575 let (engine, mut receiver) = MockRegionEngine::new(MITO_ENGINE_NAME);
2576 mock_region_server.register_engine(engine);
2577
2578 let region_id = RegionId::new(1, 1);
2579 let response = mock_region_server
2580 .handle_request(
2581 region_id,
2582 RegionRequest::CleanUp(RegionCleanUpRequest {
2583 engine: MITO_ENGINE_NAME.to_string(),
2584 table_dir: String::new(),
2585 path_type: PathType::Bare,
2586 options: HashMap::new(),
2587 }),
2588 )
2589 .await
2590 .unwrap();
2591
2592 assert_eq!(response.affected_rows, 0);
2593 let (handled_region_id, handled_request) = receiver.try_recv().unwrap();
2594 assert_eq!(handled_region_id, region_id);
2595 assert_matches!(handled_request, RegionRequest::CleanUp(_));
2596 assert!(
2597 mock_region_server
2598 .inner
2599 .region_map
2600 .get(®ion_id)
2601 .is_none()
2602 );
2603 }
2604
2605 #[tokio::test]
2606 async fn test_offline_cleanup_rejects_registered_region() {
2607 let mut mock_region_server = mock_region_server();
2608 let (engine, mut receiver) = MockRegionEngine::new(MITO_ENGINE_NAME);
2609 mock_region_server.register_engine(engine.clone());
2610
2611 let region_id = RegionId::new(1, 1);
2612 mock_region_server
2613 .inner
2614 .region_map
2615 .insert(region_id, RegionEngineWithStatus::Ready(engine));
2616
2617 let err = mock_region_server
2618 .handle_request(
2619 region_id,
2620 RegionRequest::CleanUp(RegionCleanUpRequest {
2621 engine: MITO_ENGINE_NAME.to_string(),
2622 table_dir: String::new(),
2623 path_type: PathType::Bare,
2624 options: HashMap::new(),
2625 }),
2626 )
2627 .await
2628 .unwrap_err();
2629
2630 assert_eq!(err.status_code(), StatusCode::RegionBusy);
2631 assert!(receiver.try_recv().is_err());
2632 assert!(matches!(
2633 mock_region_server
2634 .inner
2635 .region_map
2636 .get(®ion_id)
2637 .unwrap()
2638 .clone(),
2639 RegionEngineWithStatus::Ready(_)
2640 ));
2641 }
2642
2643 #[tokio::test]
2644 async fn test_region_registering() {
2645 common_telemetry::init_default_ut_logging();
2646
2647 let mut mock_region_server = mock_region_server();
2648 let (engine, _receiver) = MockRegionEngine::new(MITO_ENGINE_NAME);
2649 let engine_name = engine.name();
2650 mock_region_server.register_engine(engine.clone());
2651 let region_id = RegionId::new(1, 1);
2652 let builder = CreateRequestBuilder::new();
2653 let create_req = builder.build();
2654 mock_region_server.inner.region_map.insert(
2656 region_id,
2657 RegionEngineWithStatus::Registering(engine.clone()),
2658 );
2659 let response = mock_region_server
2660 .handle_request(region_id, RegionRequest::Create(create_req))
2661 .await
2662 .unwrap();
2663 assert_eq!(response.affected_rows, 0);
2664 let status = mock_region_server
2665 .inner
2666 .region_map
2667 .get(®ion_id)
2668 .unwrap()
2669 .clone();
2670 assert!(matches!(status, RegionEngineWithStatus::Ready(_)));
2671
2672 mock_region_server.inner.region_map.insert(
2673 region_id,
2674 RegionEngineWithStatus::Registering(engine.clone()),
2675 );
2676 let response = mock_region_server
2677 .handle_request(
2678 region_id,
2679 RegionRequest::Open(RegionOpenRequest {
2680 engine: engine_name.to_string(),
2681 table_dir: String::new(),
2682 path_type: PathType::Bare,
2683 options: Default::default(),
2684 skip_wal_replay: false,
2685 checkpoint: None,
2686 requirements: Default::default(),
2687 }),
2688 )
2689 .await
2690 .unwrap();
2691 assert_eq!(response.affected_rows, 0);
2692 let status = mock_region_server
2693 .inner
2694 .region_map
2695 .get(®ion_id)
2696 .unwrap()
2697 .clone();
2698 assert!(matches!(status, RegionEngineWithStatus::Ready(_)));
2699 }
2700
2701 #[tokio::test]
2702 async fn test_region_deregistering() {
2703 common_telemetry::init_default_ut_logging();
2704
2705 let mut mock_region_server = mock_region_server();
2706 let (engine, _receiver) = MockRegionEngine::new(MITO_ENGINE_NAME);
2707
2708 mock_region_server.register_engine(engine.clone());
2709
2710 let region_id = RegionId::new(1, 1);
2711
2712 mock_region_server.inner.region_map.insert(
2714 region_id,
2715 RegionEngineWithStatus::Deregistering(engine.clone()),
2716 );
2717
2718 let response = mock_region_server
2719 .handle_request(
2720 region_id,
2721 RegionRequest::Drop(RegionDropRequest {
2722 fast_path: false,
2723 force: false,
2724 partial_drop: false,
2725 }),
2726 )
2727 .await
2728 .unwrap();
2729 assert_eq!(response.affected_rows, 0);
2730
2731 let status = mock_region_server
2732 .inner
2733 .region_map
2734 .get(®ion_id)
2735 .unwrap()
2736 .clone();
2737 assert!(matches!(status, RegionEngineWithStatus::Deregistering(_)));
2738
2739 mock_region_server.inner.region_map.insert(
2740 region_id,
2741 RegionEngineWithStatus::Deregistering(engine.clone()),
2742 );
2743
2744 let response = mock_region_server
2745 .handle_request(
2746 region_id,
2747 RegionRequest::Close(RegionCloseRequest::default()),
2748 )
2749 .await
2750 .unwrap();
2751 assert_eq!(response.affected_rows, 0);
2752
2753 let status = mock_region_server
2754 .inner
2755 .region_map
2756 .get(®ion_id)
2757 .unwrap()
2758 .clone();
2759 assert!(matches!(status, RegionEngineWithStatus::Deregistering(_)));
2760 }
2761
2762 #[tokio::test]
2763 async fn test_region_not_ready() {
2764 common_telemetry::init_default_ut_logging();
2765
2766 let mut mock_region_server = mock_region_server();
2767 let (engine, _receiver) = MockRegionEngine::new(MITO_ENGINE_NAME);
2768
2769 mock_region_server.register_engine(engine.clone());
2770
2771 let region_id = RegionId::new(1, 1);
2772
2773 mock_region_server.inner.region_map.insert(
2775 region_id,
2776 RegionEngineWithStatus::Registering(engine.clone()),
2777 );
2778
2779 let err = mock_region_server
2780 .handle_request(
2781 region_id,
2782 RegionRequest::Truncate(RegionTruncateRequest::All),
2783 )
2784 .await
2785 .unwrap_err();
2786
2787 assert_eq!(err.status_code(), StatusCode::RegionNotReady);
2788 }
2789
2790 #[test]
2791 fn test_batch_write_summary_uses_first_input_failure() {
2792 let first = error::RegionNotFoundSnafu {
2793 region_id: RegionId::new(1, 0),
2794 }
2795 .build();
2796 let expected = format!("2 region requests failed; entry 0: {}", first.output_msg());
2797 let later = error::RegionBusySnafu {
2798 region_id: RegionId::new(1, 1),
2799 }
2800 .build();
2801 let response = encode_batch_write_response(BatchRegionResponse {
2802 affected_rows: 0,
2803 extensions: HashMap::new(),
2804 failures: vec![(1, later), (0, first)],
2805 });
2806 let status = response.header.unwrap().status.unwrap();
2807 assert_eq!(status.status_code, StatusCode::PartialFailure as u32);
2808 assert_eq!(status.err_msg, expected);
2809 assert_eq!(response.failures[0].request_index, 1);
2810 assert_eq!(response.failures[1].request_index, 0);
2811 }
2812
2813 #[tokio::test]
2814 async fn test_write_failure_response_compatibility() {
2815 let server = mock_region_server();
2816 let missing = RegionId::new(1, 1);
2817 let registering = RegionId::new(1, 2);
2818 let (engine, _receiver) = MockRegionEngine::new(MITO_ENGINE_NAME);
2819 server
2820 .inner
2821 .region_map
2822 .insert(registering, RegionEngineWithStatus::Registering(engine));
2823 for (region_id, code) in [
2824 (missing, StatusCode::RegionNotFound),
2825 (registering, StatusCode::RegionNotReady),
2826 ] {
2827 for delete in [false, true] {
2828 for accept_partial in [false, true] {
2829 for with_empty_entry in [false, true] {
2831 let rows = Some(Rows {
2832 schema: Vec::new(),
2833 rows: Vec::new(),
2834 });
2835 let body = if delete {
2836 let request = api::v1::region::DeleteRequest {
2837 region_id: region_id.as_u64(),
2838 rows,
2839 ..Default::default()
2840 };
2841 region_request::Body::Deletes(api::v1::region::DeleteRequests {
2842 requests: if with_empty_entry {
2843 vec![Default::default(), request]
2844 } else {
2845 vec![request]
2846 },
2847 })
2848 } else {
2849 let request = InsertRequest {
2850 region_id: region_id.as_u64(),
2851 rows,
2852 ..Default::default()
2853 };
2854 region_request::Body::Inserts(InsertRequests {
2855 requests: if with_empty_entry {
2856 vec![Default::default(), request]
2857 } else {
2858 vec![request]
2859 },
2860 })
2861 };
2862 let mut header = api::v1::region::RegionRequestHeader::default();
2863 if accept_partial {
2864 header
2865 .query_context
2866 .get_or_insert_with(Default::default)
2867 .extensions
2868 .insert(
2869 api::region::ACCEPT_PARTIAL_RESULT.to_string(),
2870 "true".to_string(),
2871 );
2872 }
2873 let result = server
2874 .handle_region_request(RegionRequestV1 {
2875 header: Some(header),
2876 body: Some(body),
2877 })
2878 .await;
2879 if accept_partial && with_empty_entry {
2880 let response = result.unwrap();
2881 assert_eq!(
2882 StatusCode::PartialFailure as u32,
2883 response.header.unwrap().status.unwrap().status_code
2884 );
2885 assert_eq!(1, response.failures.len());
2886 assert_eq!(1, response.failures[0].request_index);
2887 let status = response.failures[0].status.as_ref().unwrap();
2888 assert_eq!(code as u32, status.status_code);
2889 assert_eq!(
2890 RetryHint::RetryAfterRouteRefresh.as_str(),
2891 status.retry_hint
2892 );
2893 } else {
2894 let status: tonic::Status = result.unwrap_err().into();
2895 assert_eq!(
2896 common_error::status_code::status_to_tonic_code(code),
2897 status.code()
2898 );
2899 assert_eq!(
2900 (code as u32).to_string(),
2901 status
2902 .metadata()
2903 .get(common_error::GREPTIME_DB_HEADER_ERROR_CODE)
2904 .unwrap()
2905 .to_str()
2906 .unwrap()
2907 );
2908 assert_eq!(
2909 RetryHint::RetryAfterRouteRefresh.as_str(),
2910 status
2911 .metadata()
2912 .get(common_error::GREPTIME_DB_HEADER_ERROR_RETRY_HINT)
2913 .unwrap()
2914 .to_str()
2915 .unwrap()
2916 );
2917 }
2918 }
2919 }
2920 }
2921 }
2922 }
2923
2924 #[tokio::test]
2925 async fn test_legacy_batch_returns_first_input_error() {
2926 let server = mock_region_server();
2927 let first = RegionId::new(1, 1);
2928 let second = RegionId::new(1, 2);
2929 let (engine, _receiver) = MockRegionEngine::with_custom_apply_fn("mock", |engine| {
2930 engine.handle_request_delay = Some(Duration::from_millis(10));
2931 engine.handle_request_mock_fn = Some(Box::new(|region_id, _| {
2932 error::RegionBusySnafu { region_id }.fail()
2933 }));
2934 });
2935 server.register_test_region(first, engine);
2936 let requests = [first, second]
2937 .map(|region_id| InsertRequest {
2938 region_id: region_id.as_u64(),
2939 rows: Some(Rows::default()),
2940 ..Default::default()
2941 })
2942 .to_vec();
2943 let error = server
2944 .handle_region_request(RegionRequestV1 {
2945 header: Some(Default::default()),
2946 body: Some(region_request::Body::Inserts(InsertRequests { requests })),
2947 })
2948 .await
2949 .unwrap_err();
2950 assert_eq!(StatusCode::RegionBusy, error.status_code());
2951 assert_eq!(RetryHint::Retryable, error.retry_hint());
2952 }
2953
2954 #[tokio::test]
2955 async fn test_region_request_failed() {
2956 common_telemetry::init_default_ut_logging();
2957
2958 let mut mock_region_server = mock_region_server();
2959 let (engine, _receiver) = MockRegionEngine::with_mock_fn(
2960 MITO_ENGINE_NAME,
2961 Box::new(|_region_id, _request| {
2962 error::UnexpectedSnafu {
2963 violated: "test".to_string(),
2964 }
2965 .fail()
2966 }),
2967 );
2968
2969 mock_region_server.register_engine(engine.clone());
2970
2971 let region_id = RegionId::new(1, 1);
2972 let builder = CreateRequestBuilder::new();
2973 let create_req = builder.build();
2974 mock_region_server
2975 .handle_request(region_id, RegionRequest::Create(create_req))
2976 .await
2977 .unwrap_err();
2978
2979 assert!(!mock_region_server.inner.region_map.contains_key(®ion_id));
2980
2981 mock_region_server
2982 .inner
2983 .region_map
2984 .insert(region_id, RegionEngineWithStatus::Ready(engine.clone()));
2985
2986 mock_region_server
2987 .handle_request(
2988 region_id,
2989 RegionRequest::Drop(RegionDropRequest {
2990 fast_path: false,
2991 force: false,
2992 partial_drop: false,
2993 }),
2994 )
2995 .await
2996 .unwrap_err();
2997
2998 assert!(mock_region_server.inner.region_map.contains_key(®ion_id));
2999
3000 let insert = api::v1::region::InsertRequest {
3001 region_id: region_id.as_u64(),
3002 rows: Some(Rows {
3003 schema: Vec::new(),
3004 rows: Vec::new(),
3005 }),
3006 ..Default::default()
3007 };
3008 let success_region = RegionId::new(1, 2);
3009 let (success_engine, _receiver) =
3010 MockRegionEngine::with_custom_apply_fn("mock", |engine| {
3011 engine.handle_request_delay = Some(std::time::Duration::from_millis(10));
3012 engine.handle_request_mock_fn = Some(Box::new(|_, _| Ok(5)));
3013 });
3014 mock_region_server.register_test_region(success_region, success_engine);
3015 let successful_insert = api::v1::region::InsertRequest {
3016 region_id: success_region.as_u64(),
3017 ..insert.clone()
3018 };
3019 let response = mock_region_server
3020 .handle_region_request(RegionRequestV1 {
3021 header: Some(api::v1::region::RegionRequestHeader {
3022 query_context: Some(api::v1::QueryContext {
3023 extensions: HashMap::from([(
3024 api::region::ACCEPT_PARTIAL_RESULT.to_string(),
3025 "true".to_string(),
3026 )]),
3027 ..Default::default()
3028 }),
3029 ..Default::default()
3030 }),
3031 body: Some(region_request::Body::Inserts(InsertRequests {
3032 requests: vec![
3033 Default::default(),
3034 insert.clone(),
3035 successful_insert,
3036 insert,
3037 ],
3038 })),
3039 })
3040 .await
3041 .unwrap();
3042 assert_eq!(response.affected_rows, 5);
3043 assert_eq!(
3044 response
3045 .header
3046 .as_ref()
3047 .unwrap()
3048 .status
3049 .as_ref()
3050 .unwrap()
3051 .status_code,
3052 StatusCode::PartialFailure as u32
3053 );
3054 assert_eq!(
3055 response
3056 .failures
3057 .iter()
3058 .map(|error| error.request_index)
3059 .collect::<Vec<_>>(),
3060 vec![1, 3]
3061 );
3062 assert!(
3063 response
3064 .failures
3065 .iter()
3066 .all(|error| error.status.as_ref().is_some_and(|status| {
3067 status.status_code == StatusCode::Unexpected as u32
3068 && status.retry_hint == RetryHint::NonRetryable.as_str()
3069 }))
3070 );
3071 }
3072
3073 #[tokio::test]
3076 async fn test_stop_reports_a_region_that_failed_to_close() {
3077 common_telemetry::init_default_ut_logging();
3078
3079 let mut mock_region_server = mock_region_server();
3080 let failing = RegionId::new(1, 1);
3081 let closing = RegionId::new(1, 2);
3082 let closed = Arc::new(std::sync::Mutex::new(Vec::new()));
3083 let (engine, _receiver) = MockRegionEngine::with_mock_fn(MITO_ENGINE_NAME, {
3084 let closed = closed.clone();
3085 Box::new(move |region_id, request| {
3086 if matches!(request, RegionRequest::Close(_)) {
3087 closed.lock().unwrap().push(region_id);
3088 if region_id == failing {
3089 return error::UnexpectedSnafu {
3090 violated: "close failed".to_string(),
3091 }
3092 .fail();
3093 }
3094 }
3095 Ok(0)
3096 })
3097 });
3098 mock_region_server.register_engine(engine.clone());
3099 for region_id in [failing, closing] {
3100 mock_region_server
3101 .inner
3102 .region_map
3103 .insert(region_id, RegionEngineWithStatus::Ready(engine.clone()));
3104 }
3105
3106 let err = mock_region_server.stop().await.unwrap_err();
3107
3108 assert_matches!(err, error::Error::HandleRegionRequest { region_id, .. } if region_id == failing);
3109 let mut closed = closed.lock().unwrap().clone();
3110 closed.sort();
3111 assert_eq!(vec![failing, closing], closed);
3112 }
3113
3114 #[tokio::test]
3115 async fn test_batch_open_region_ignore_nonexistent_regions() {
3116 common_telemetry::init_default_ut_logging();
3117 let mut mock_region_server = mock_region_server();
3118 let (engine, _receiver) = MockRegionEngine::with_mock_fn(
3119 MITO_ENGINE_NAME,
3120 Box::new(|region_id, _request| {
3121 if region_id == RegionId::new(1, 1) {
3122 error::RegionNotFoundSnafu { region_id }.fail()
3123 } else {
3124 Ok(0)
3125 }
3126 }),
3127 );
3128 mock_region_server.register_engine(engine.clone());
3129
3130 let region_ids = mock_region_server
3131 .handle_batch_open_requests(
3132 8,
3133 vec![
3134 (
3135 RegionId::new(1, 1),
3136 RegionOpenRequest {
3137 engine: MITO_ENGINE_NAME.to_string(),
3138 table_dir: String::new(),
3139 path_type: PathType::Bare,
3140 options: Default::default(),
3141 skip_wal_replay: false,
3142 checkpoint: None,
3143 requirements: Default::default(),
3144 },
3145 ),
3146 (
3147 RegionId::new(1, 2),
3148 RegionOpenRequest {
3149 engine: MITO_ENGINE_NAME.to_string(),
3150 table_dir: String::new(),
3151 path_type: PathType::Bare,
3152 options: Default::default(),
3153 skip_wal_replay: false,
3154 checkpoint: None,
3155 requirements: Default::default(),
3156 },
3157 ),
3158 ],
3159 true,
3160 )
3161 .await
3162 .unwrap();
3163 assert_eq!(region_ids, vec![RegionId::new(1, 2)]);
3164
3165 let err = mock_region_server
3166 .handle_batch_open_requests(
3167 8,
3168 vec![
3169 (
3170 RegionId::new(1, 1),
3171 RegionOpenRequest {
3172 engine: MITO_ENGINE_NAME.to_string(),
3173 table_dir: String::new(),
3174 path_type: PathType::Bare,
3175 options: Default::default(),
3176 skip_wal_replay: false,
3177 checkpoint: None,
3178 requirements: Default::default(),
3179 },
3180 ),
3181 (
3182 RegionId::new(1, 2),
3183 RegionOpenRequest {
3184 engine: MITO_ENGINE_NAME.to_string(),
3185 table_dir: String::new(),
3186 path_type: PathType::Bare,
3187 options: Default::default(),
3188 skip_wal_replay: false,
3189 checkpoint: None,
3190 requirements: Default::default(),
3191 },
3192 ),
3193 ],
3194 false,
3195 )
3196 .await
3197 .unwrap_err();
3198 assert_matches!(&err, error::Error::HandleBatchOpenRequest { .. });
3199 assert_eq!(err.status_code(), StatusCode::RegionNotFound);
3200 assert_eq!(err.retry_hint(), RetryHint::RetryAfterRouteRefresh);
3201 }
3202
3203 struct CurrentEngineTest {
3204 region_id: RegionId,
3205 current_region_status: Option<RegionEngineWithStatus>,
3206 region_change: RegionChange,
3207 assert: Box<dyn FnOnce(Result<CurrentEngine>)>,
3208 }
3209
3210 #[tokio::test]
3211 async fn test_current_engine() {
3212 common_telemetry::init_default_ut_logging();
3213
3214 let mut mock_region_server = mock_region_server();
3215 let (engine, _) = MockRegionEngine::new(MITO_ENGINE_NAME);
3216 mock_region_server.register_engine(engine.clone());
3217
3218 let region_id = RegionId::new(1024, 1);
3219 let tests = vec![
3220 CurrentEngineTest {
3222 region_id,
3223 current_region_status: None,
3224 region_change: RegionChange::None,
3225 assert: Box::new(|result| {
3226 let err = result.unwrap_err();
3227 assert_eq!(err.status_code(), StatusCode::RegionNotFound);
3228 }),
3229 },
3230 CurrentEngineTest {
3231 region_id,
3232 current_region_status: Some(RegionEngineWithStatus::Ready(engine.clone())),
3233 region_change: RegionChange::None,
3234 assert: Box::new(|result| {
3235 let current_engine = result.unwrap();
3236 assert_matches!(current_engine, CurrentEngine::Engine(_));
3237 }),
3238 },
3239 CurrentEngineTest {
3240 region_id,
3241 current_region_status: Some(RegionEngineWithStatus::Registering(engine.clone())),
3242 region_change: RegionChange::None,
3243 assert: Box::new(|result| {
3244 let err = result.unwrap_err();
3245 assert_eq!(err.status_code(), StatusCode::RegionNotReady);
3246 }),
3247 },
3248 CurrentEngineTest {
3249 region_id,
3250 current_region_status: Some(RegionEngineWithStatus::Deregistering(engine.clone())),
3251 region_change: RegionChange::None,
3252 assert: Box::new(|result| {
3253 let err = result.unwrap_err();
3254 assert_eq!(err.status_code(), StatusCode::RegionNotFound);
3255 }),
3256 },
3257 CurrentEngineTest {
3259 region_id,
3260 current_region_status: None,
3261 region_change: RegionChange::Register(RegionAttribute::Mito),
3262 assert: Box::new(|result| {
3263 let current_engine = result.unwrap();
3264 assert_matches!(current_engine, CurrentEngine::Engine(_));
3265 }),
3266 },
3267 CurrentEngineTest {
3268 region_id,
3269 current_region_status: Some(RegionEngineWithStatus::Registering(engine.clone())),
3270 region_change: RegionChange::Register(RegionAttribute::Mito),
3271 assert: Box::new(|result| {
3272 let current_engine = result.unwrap();
3273 assert_matches!(current_engine, CurrentEngine::Engine(_));
3274 }),
3275 },
3276 CurrentEngineTest {
3277 region_id,
3278 current_region_status: Some(RegionEngineWithStatus::Deregistering(engine.clone())),
3279 region_change: RegionChange::Register(RegionAttribute::Mito),
3280 assert: Box::new(|result| {
3281 let err = result.unwrap_err();
3282 assert_eq!(err.status_code(), StatusCode::RegionBusy);
3283 }),
3284 },
3285 CurrentEngineTest {
3286 region_id,
3287 current_region_status: Some(RegionEngineWithStatus::Ready(engine.clone())),
3288 region_change: RegionChange::Register(RegionAttribute::Mito),
3289 assert: Box::new(|result| {
3290 let current_engine = result.unwrap();
3291 assert_matches!(current_engine, CurrentEngine::Engine(_));
3292 }),
3293 },
3294 CurrentEngineTest {
3296 region_id,
3297 current_region_status: None,
3298 region_change: RegionChange::Deregisters,
3299 assert: Box::new(|result| {
3300 let current_engine = result.unwrap();
3301 assert_matches!(current_engine, CurrentEngine::EarlyReturn(_));
3302 }),
3303 },
3304 CurrentEngineTest {
3305 region_id,
3306 current_region_status: Some(RegionEngineWithStatus::Registering(engine.clone())),
3307 region_change: RegionChange::Deregisters,
3308 assert: Box::new(|result| {
3309 let err = result.unwrap_err();
3310 assert_eq!(err.status_code(), StatusCode::RegionBusy);
3311 }),
3312 },
3313 CurrentEngineTest {
3314 region_id,
3315 current_region_status: Some(RegionEngineWithStatus::Deregistering(engine.clone())),
3316 region_change: RegionChange::Deregisters,
3317 assert: Box::new(|result| {
3318 let current_engine = result.unwrap();
3319 assert_matches!(current_engine, CurrentEngine::EarlyReturn(_));
3320 }),
3321 },
3322 CurrentEngineTest {
3323 region_id,
3324 current_region_status: Some(RegionEngineWithStatus::Ready(engine.clone())),
3325 region_change: RegionChange::Deregisters,
3326 assert: Box::new(|result| {
3327 let current_engine = result.unwrap();
3328 assert_matches!(current_engine, CurrentEngine::Engine(_));
3329 }),
3330 },
3331 CurrentEngineTest {
3333 region_id,
3334 current_region_status: None,
3335 region_change: RegionChange::OfflineCleanup(RegionAttribute::Mito),
3336 assert: Box::new(|result| {
3337 let current_engine = result.unwrap();
3338 assert_matches!(current_engine, CurrentEngine::Engine(_));
3339 }),
3340 },
3341 CurrentEngineTest {
3342 region_id,
3343 current_region_status: Some(RegionEngineWithStatus::Registering(engine.clone())),
3344 region_change: RegionChange::OfflineCleanup(RegionAttribute::Mito),
3345 assert: Box::new(|result| {
3346 let err = result.unwrap_err();
3347 assert_eq!(err.status_code(), StatusCode::RegionBusy);
3348 }),
3349 },
3350 CurrentEngineTest {
3351 region_id,
3352 current_region_status: Some(RegionEngineWithStatus::Deregistering(engine.clone())),
3353 region_change: RegionChange::OfflineCleanup(RegionAttribute::Mito),
3354 assert: Box::new(|result| {
3355 let err = result.unwrap_err();
3356 assert_eq!(err.status_code(), StatusCode::RegionBusy);
3357 }),
3358 },
3359 CurrentEngineTest {
3360 region_id,
3361 current_region_status: Some(RegionEngineWithStatus::Ready(engine.clone())),
3362 region_change: RegionChange::OfflineCleanup(RegionAttribute::Mito),
3363 assert: Box::new(|result| {
3364 let err = result.unwrap_err();
3365 assert_eq!(err.status_code(), StatusCode::RegionBusy);
3366 }),
3367 },
3368 ];
3369
3370 for test in tests {
3371 let CurrentEngineTest {
3372 region_id,
3373 current_region_status,
3374 region_change,
3375 assert,
3376 } = test;
3377
3378 if let Some(status) = current_region_status {
3380 mock_region_server
3381 .inner
3382 .region_map
3383 .insert(region_id, status);
3384 } else {
3385 mock_region_server.inner.region_map.remove(®ion_id);
3386 }
3387
3388 let result = mock_region_server
3389 .inner
3390 .get_engine(region_id, ®ion_change);
3391
3392 assert(result);
3393 }
3394 }
3395
3396 #[tokio::test]
3397 async fn test_region_server_parallelism() {
3398 let p = RegionServerParallelism::from_opts(2, Duration::from_millis(1)).unwrap();
3399 let first_query = p.acquire().await;
3400 assert!(first_query.is_ok());
3401 let second_query = p.acquire().await;
3402 assert!(second_query.is_ok());
3403 let third_query = p.acquire().await;
3404 assert!(third_query.is_err());
3405 let err = third_query.unwrap_err();
3406 assert_eq!(
3407 err.output_msg(),
3408 "Failed to acquire permit under timeouts: deadline has elapsed".to_string()
3409 );
3410 drop(first_query);
3411 let forth_query = p.acquire().await;
3412 assert!(forth_query.is_ok());
3413 }
3414
3415 fn mock_region_metadata(region_id: RegionId) -> RegionMetadata {
3416 let mut metadata_builder = RegionMetadataBuilder::new(region_id);
3417 metadata_builder.push_column_metadata(ColumnMetadata {
3418 column_schema: datatypes::schema::ColumnSchema::new(
3419 "timestamp",
3420 ConcreteDataType::timestamp_nanosecond_datatype(),
3421 false,
3422 ),
3423 semantic_type: SemanticType::Timestamp,
3424 column_id: 0,
3425 });
3426 metadata_builder.push_column_metadata(ColumnMetadata {
3427 column_schema: datatypes::schema::ColumnSchema::new(
3428 "file",
3429 ConcreteDataType::string_datatype(),
3430 true,
3431 ),
3432 semantic_type: SemanticType::Tag,
3433 column_id: 1,
3434 });
3435 metadata_builder.push_column_metadata(ColumnMetadata {
3436 column_schema: datatypes::schema::ColumnSchema::new(
3437 "message",
3438 ConcreteDataType::string_datatype(),
3439 true,
3440 ),
3441 semantic_type: SemanticType::Field,
3442 column_id: 2,
3443 });
3444 metadata_builder.primary_key(vec![1]);
3445 metadata_builder.build().unwrap()
3446 }
3447
3448 #[tokio::test]
3449 async fn test_handle_list_metadata_request() {
3450 common_telemetry::init_default_ut_logging();
3451
3452 let mut mock_region_server = mock_region_server();
3453 let region_id_1 = RegionId::new(1, 0);
3454 let region_id_2 = RegionId::new(2, 0);
3455
3456 let metadata_1 = mock_region_metadata(region_id_1);
3457 let metadata_2 = mock_region_metadata(region_id_2);
3458 let metadatas = vec![Some(metadata_1.clone()), Some(metadata_2.clone())];
3459
3460 let metadata_1 = Arc::new(metadata_1);
3461 let metadata_2 = Arc::new(metadata_2);
3462 let (engine, _) = MockRegionEngine::with_metadata_mock_fn(
3463 MITO_ENGINE_NAME,
3464 Box::new(move |region_id| {
3465 if region_id == region_id_1 {
3466 Ok(metadata_1.clone())
3467 } else if region_id == region_id_2 {
3468 Ok(metadata_2.clone())
3469 } else {
3470 error::RegionNotFoundSnafu { region_id }.fail()
3471 }
3472 }),
3473 );
3474
3475 mock_region_server.register_engine(engine.clone());
3476 mock_region_server
3477 .inner
3478 .region_map
3479 .insert(region_id_1, RegionEngineWithStatus::Ready(engine.clone()));
3480 mock_region_server
3481 .inner
3482 .region_map
3483 .insert(region_id_2, RegionEngineWithStatus::Ready(engine.clone()));
3484
3485 let list_metadata_request = ListMetadataRequest {
3487 region_ids: vec![region_id_1.as_u64(), region_id_2.as_u64()],
3488 };
3489 let response = mock_region_server
3490 .handle_list_metadata_request(&list_metadata_request)
3491 .await
3492 .unwrap();
3493 let decoded_metadata: Vec<Option<RegionMetadata>> =
3494 serde_json::from_slice(&response.metadata).unwrap();
3495 assert_eq!(metadatas, decoded_metadata);
3496 }
3497
3498 #[tokio::test]
3499 async fn test_handle_list_metadata_not_found() {
3500 common_telemetry::init_default_ut_logging();
3501
3502 let mut mock_region_server = mock_region_server();
3503 let region_id_1 = RegionId::new(1, 0);
3504 let region_id_2 = RegionId::new(2, 0);
3505
3506 let metadata_1 = mock_region_metadata(region_id_1);
3507 let metadatas = vec![Some(metadata_1.clone()), None];
3508
3509 let metadata_1 = Arc::new(metadata_1);
3510 let (engine, _) = MockRegionEngine::with_metadata_mock_fn(
3511 MITO_ENGINE_NAME,
3512 Box::new(move |region_id| {
3513 if region_id == region_id_1 {
3514 Ok(metadata_1.clone())
3515 } else {
3516 error::RegionNotFoundSnafu { region_id }.fail()
3517 }
3518 }),
3519 );
3520
3521 mock_region_server.register_engine(engine.clone());
3522 mock_region_server
3523 .inner
3524 .region_map
3525 .insert(region_id_1, RegionEngineWithStatus::Ready(engine.clone()));
3526
3527 let list_metadata_request = ListMetadataRequest {
3529 region_ids: vec![region_id_1.as_u64(), region_id_2.as_u64()],
3530 };
3531 let response = mock_region_server
3532 .handle_list_metadata_request(&list_metadata_request)
3533 .await
3534 .unwrap();
3535 let decoded_metadata: Vec<Option<RegionMetadata>> =
3536 serde_json::from_slice(&response.metadata).unwrap();
3537 assert_eq!(metadatas, decoded_metadata);
3538
3539 mock_region_server
3541 .inner
3542 .region_map
3543 .insert(region_id_2, RegionEngineWithStatus::Ready(engine.clone()));
3544 let response = mock_region_server
3545 .handle_list_metadata_request(&list_metadata_request)
3546 .await
3547 .unwrap();
3548 let decoded_metadata: Vec<Option<RegionMetadata>> =
3549 serde_json::from_slice(&response.metadata).unwrap();
3550 assert_eq!(metadatas, decoded_metadata);
3551 }
3552
3553 #[tokio::test]
3554 async fn test_handle_list_metadata_failed() {
3555 common_telemetry::init_default_ut_logging();
3556
3557 let mut mock_region_server = mock_region_server();
3558 let region_id_1 = RegionId::new(1, 0);
3559
3560 let (engine, _) = MockRegionEngine::with_metadata_mock_fn(
3561 MITO_ENGINE_NAME,
3562 Box::new(move |region_id| {
3563 error::UnexpectedSnafu {
3564 violated: format!("Failed to get region {region_id}"),
3565 }
3566 .fail()
3567 }),
3568 );
3569
3570 mock_region_server.register_engine(engine.clone());
3571 mock_region_server
3572 .inner
3573 .region_map
3574 .insert(region_id_1, RegionEngineWithStatus::Ready(engine.clone()));
3575
3576 let list_metadata_request = ListMetadataRequest {
3578 region_ids: vec![region_id_1.as_u64()],
3579 };
3580 mock_region_server
3581 .handle_list_metadata_request(&list_metadata_request)
3582 .await
3583 .unwrap_err();
3584 }
3585
3586 async fn region_server_on_object_store_wal(
3589 env: &mut TestEnv,
3590 config: MitoConfig,
3591 ) -> (RegionServer, Arc<ObjectStoreLogStore>, String) {
3592 let wal_config = ObjectStoreWalConfig::default();
3593 let log_store = ObjectStoreLogStore::try_new(
3594 ObjectStore::new(Memory::default()).unwrap(),
3595 &wal_config,
3596 0,
3597 STANDALONE_GENERATION,
3598 )
3599 .await
3600 .unwrap();
3601 let engine = env
3602 .create_engine_with_log_store(config, log_store.clone())
3603 .await;
3604 let mut region_server = mock_region_server();
3605 region_server.register_engine(Arc::new(engine));
3606 let wal_options = serde_json::to_string(&WalOptions::ObjectStore(
3607 ObjectStoreWalOptions::new(wal_config.node_prefix(0, STANDALONE_GENERATION)),
3608 ))
3609 .unwrap();
3610 (region_server, log_store, wal_options)
3611 }
3612
3613 async fn create_region(
3615 region_server: &RegionServer,
3616 region_id: RegionId,
3617 wal_options: &str,
3618 ) -> InsertRequest {
3619 let request = CreateRequestBuilder::new()
3620 .insert_option(WAL_OPTIONS_KEY, wal_options)
3621 .build();
3622 let rows = Rows {
3623 schema: rows_schema(&request),
3624 rows: build_rows(0, 3),
3625 };
3626 region_server
3627 .handle_request(region_id, RegionRequest::Create(request))
3628 .await
3629 .unwrap();
3630 InsertRequest {
3631 region_id: region_id.as_u64(),
3632 rows: Some(rows),
3633 partition_expr_version: None,
3634 skip_wal: false,
3635 }
3636 }
3637
3638 #[tokio::test]
3639 async fn test_metric_batch_failure_indices() {
3640 let mut env = TestEnv::with_prefix("region-server-metric-results").await;
3641 let mito = env.create_engine(MitoConfig::default()).await;
3642 let metric = Arc::new(
3643 MetricEngine::try_new(mito, metric_engine::config::EngineConfig::default()).unwrap(),
3644 );
3645 let mut server = mock_region_server();
3646 server.register_engine(metric.clone());
3647 let mut inserts = Vec::new();
3648 for table in [10, 20] {
3649 let physical = RegionId::new(table, 0);
3650 let logical = RegionId::new(table + 1, 0);
3651 let mut create = CreateRequestBuilder::new().build();
3652 create.engine = METRIC_ENGINE_NAME.to_string();
3653 create.column_metadatas = vec![
3654 store_api::metadata::ColumnMetadata {
3655 column_id: 0,
3656 semantic_type: SemanticType::Timestamp,
3657 column_schema: ColumnSchema::new(
3658 "greptime_timestamp",
3659 ConcreteDataType::timestamp_millisecond_datatype(),
3660 false,
3661 ),
3662 },
3663 store_api::metadata::ColumnMetadata {
3664 column_id: 1,
3665 semantic_type: SemanticType::Field,
3666 column_schema: ColumnSchema::new(
3667 "greptime_value",
3668 ConcreteDataType::float64_datatype(),
3669 false,
3670 ),
3671 },
3672 ];
3673 create.primary_key.clear();
3674 create.table_dir = format!("metric-results/{table}/");
3675 create.options = HashMap::from([(
3676 store_api::metric_engine_consts::PHYSICAL_TABLE_METADATA_KEY.to_string(),
3677 String::new(),
3678 )]);
3679 server
3680 .handle_request(physical, RegionRequest::Create(create.clone()))
3681 .await
3682 .unwrap();
3683 create.table_dir = format!("metric-results/{}/", table + 1);
3684 create.options = HashMap::from([(
3685 store_api::metric_engine_consts::LOGICAL_TABLE_METADATA_KEY.to_string(),
3686 physical.as_u64().to_string(),
3687 )]);
3688 server
3689 .handle_request(logical, RegionRequest::Create(create.clone()))
3690 .await
3691 .unwrap();
3692 inserts.push(InsertRequest {
3693 region_id: logical.as_u64(),
3694 rows: Some(Rows {
3695 schema: rows_schema(&create),
3696 rows: vec![api::v1::Row {
3697 values: vec![
3698 api::v1::Value {
3699 value_data: Some(
3700 api::v1::value::ValueData::TimestampMillisecondValue(1),
3701 ),
3702 },
3703 api::v1::Value {
3704 value_data: Some(api::v1::value::ValueData::F64Value(1.0)),
3705 },
3706 ],
3707 }],
3708 }),
3709 ..Default::default()
3710 });
3711 }
3712 metric
3714 .set_region_role(RegionId::new(20, 0), RegionRole::Follower)
3715 .unwrap();
3716 let response = server
3717 .handle_requests_in_parallel(region_request::Body::Inserts(InsertRequests {
3718 requests: vec![
3719 Default::default(),
3720 inserts[0].clone(),
3721 inserts[1].clone(),
3722 inserts[1].clone(),
3723 ],
3724 }))
3725 .await
3726 .unwrap();
3727 assert_eq!(1, response.affected_rows);
3728 let mut failures = response.failures;
3729 failures.sort_unstable_by_key(|(index, _)| *index);
3730 assert_eq!(
3731 vec![2, 3],
3732 failures.iter().map(|(index, _)| *index).collect::<Vec<_>>()
3733 );
3734 assert!(
3735 failures
3736 .iter()
3737 .all(|(_, error)| error.status_code() == StatusCode::RegionNotReady)
3738 );
3739 assert!(
3740 failures
3741 .iter()
3742 .all(|(_, error)| error.retry_hint() == RetryHint::RetryAfterRouteRefresh)
3743 );
3744 }
3745
3746 #[tokio::test]
3747 async fn test_mito_puts_of_one_request_share_a_wal_append() {
3748 let mut env = TestEnv::with_prefix("region-server-mito-batch-puts").await;
3751 let config = MitoConfig {
3752 num_workers: 1,
3753 worker_request_batch_size: 1,
3754 ..Default::default()
3755 };
3756 let (region_server, log_store, wal_options) =
3757 region_server_on_object_store_wal(&mut env, config).await;
3758 let mut requests = Vec::new();
3759 for region_number in 1..=2 {
3760 let region_id = RegionId::new(1, region_number);
3761 requests.push(create_region(®ion_server, region_id, &wal_options).await);
3762 }
3763
3764 let response = region_server
3765 .handle_requests_in_parallel(region_request::Body::Inserts(InsertRequests { requests }))
3766 .await
3767 .unwrap();
3768
3769 assert_eq!(6, response.affected_rows);
3770 log_store.wait_for_admitted_appends(1).await.unwrap();
3772 assert!(
3773 tokio::time::timeout(Duration::ZERO, log_store.wait_for_admitted_appends(2))
3774 .await
3775 .is_err()
3776 );
3777 }
3778
3779 #[tokio::test]
3780 async fn test_mito_puts_to_one_region_share_a_wal_append() {
3781 let mut env = TestEnv::with_prefix("region-server-mito-batch-puts-one-region").await;
3783 let config = MitoConfig {
3784 num_workers: 1,
3785 worker_request_batch_size: 1,
3786 ..Default::default()
3787 };
3788 let (region_server, log_store, wal_options) =
3789 region_server_on_object_store_wal(&mut env, config).await;
3790 let insert = create_region(®ion_server, RegionId::new(1, 1), &wal_options).await;
3791 let requests = vec![insert.clone(), insert];
3792
3793 let response = region_server
3794 .handle_requests_in_parallel(region_request::Body::Inserts(InsertRequests { requests }))
3795 .await
3796 .unwrap();
3797
3798 assert_eq!(6, response.affected_rows);
3799 log_store.wait_for_admitted_appends(1).await.unwrap();
3800 assert!(
3801 tokio::time::timeout(Duration::ZERO, log_store.wait_for_admitted_appends(2))
3802 .await
3803 .is_err()
3804 );
3805 }
3806
3807 #[tokio::test]
3808 async fn test_mito_batch_failure_indices_for_same_region() {
3809 for failed_index in [0, 1] {
3810 let mut env = TestEnv::with_prefix("region-server-mito-same-region-failure").await;
3811 let (server, _log_store, wal_options) =
3812 region_server_on_object_store_wal(&mut env, MitoConfig::default()).await;
3813 let region_id = RegionId::new(1, 1);
3814 let insert = create_region(&server, region_id, &wal_options).await;
3815 let mut requests = vec![insert.clone(), insert];
3816 requests[failed_index].partition_expr_version = Some(PartitionExprVersion { value: 1 });
3817 let response = server
3818 .handle_requests_in_parallel(region_request::Body::Inserts(InsertRequests {
3819 requests,
3820 }))
3821 .await
3822 .unwrap();
3823 assert_eq!(response.affected_rows, 3);
3824 assert_eq!(response.failures.len(), 1);
3825 assert_eq!(response.failures[0].0, failed_index);
3826 assert_eq!(
3827 response.failures[0].1.status_code(),
3828 StatusCode::InvalidArguments
3829 );
3830 let engine = server
3831 .inner
3832 .region_map
3833 .get(®ion_id)
3834 .unwrap()
3835 .clone()
3836 .into_engine();
3837 let mito = engine.as_any().downcast_ref::<MitoEngine>().unwrap();
3838 let stream = mito
3839 .scan_to_stream(region_id, Default::default())
3840 .await
3841 .unwrap();
3842 let batches = RecordBatches::try_collect(stream).await.unwrap();
3843 assert_eq!(
3844 batches.iter().map(|batch| batch.num_rows()).sum::<usize>(),
3845 3
3846 );
3847 }
3848 }
3849
3850 #[tokio::test]
3851 async fn test_mito_batch_puts_retain_successful_regions() {
3852 for accept_partial_result in [false, true] {
3853 let mut env = TestEnv::with_prefix("region-server-mito-batch-puts-error").await;
3854 let config = MitoConfig {
3855 num_workers: 2,
3856 ..Default::default()
3857 };
3858 let (region_server, log_store, wal_options) =
3859 region_server_on_object_store_wal(&mut env, config).await;
3860 let held = RegionId::new(1, 1);
3863 let unknown = RegionId::new(1, 2);
3864 let held_insert = create_region(®ion_server, held, &wal_options).await;
3865 let unknown_insert = InsertRequest {
3866 region_id: unknown.as_u64(),
3867 ..held_insert.clone()
3868 };
3869 let mito = region_server
3870 .inner
3871 .region_map
3872 .get(&held)
3873 .unwrap()
3874 .clone()
3875 .into_engine();
3876 region_server
3877 .inner
3878 .region_map
3879 .insert(unknown, RegionEngineWithStatus::Ready(mito.clone()));
3880 log_store.hold_creates();
3881
3882 let requests = vec![Default::default(), held_insert, unknown_insert];
3883 let mut header = api::v1::region::RegionRequestHeader::default();
3884 if accept_partial_result {
3885 api::region::set_accept_partial_result(&mut header);
3886 }
3887 let write = region_server.handle_region_request(RegionRequestV1 {
3888 header: Some(header),
3889 body: Some(region_request::Body::Inserts(InsertRequests { requests })),
3890 });
3891 tokio::pin!(write);
3892 assert!(
3893 tokio::time::timeout(Duration::from_millis(100), &mut write)
3894 .await
3895 .is_err()
3896 );
3897 log_store.release_creates();
3898 let result = tokio::time::timeout(Duration::from_secs(10), write)
3899 .await
3900 .unwrap();
3901 if accept_partial_result {
3902 let response = result.unwrap();
3903 assert_eq!(3, response.affected_rows);
3904 assert_eq!(
3905 StatusCode::PartialFailure as u32,
3906 response.header.unwrap().status.unwrap().status_code
3907 );
3908 assert_eq!(1, response.failures.len());
3909 assert_eq!(2, response.failures[0].request_index);
3910 assert_eq!(
3911 StatusCode::RegionNotFound as u32,
3912 response.failures[0].status.as_ref().unwrap().status_code
3913 );
3914 } else {
3915 assert_eq!(
3916 StatusCode::RegionNotFound,
3917 result.unwrap_err().status_code()
3918 );
3919 }
3920 let mito = mito.as_any().downcast_ref::<MitoEngine>().unwrap();
3921 let stream = mito.scan_to_stream(held, Default::default()).await.unwrap();
3922 let batches = RecordBatches::try_collect(stream).await.unwrap();
3923 assert_eq!(
3924 batches.iter().map(|batch| batch.num_rows()).sum::<usize>(),
3925 3
3926 );
3927 }
3928 }
3929
3930 #[tokio::test]
3931 async fn test_mito_batch_puts_to_a_stopped_worker_fail_as_a_single_put_does() {
3932 let mut env = TestEnv::with_prefix("region-server-mito-batch-puts-stopped").await;
3933 let (region_server, _log_store, wal_options) =
3934 region_server_on_object_store_wal(&mut env, MitoConfig::default()).await;
3935 let region_id = RegionId::new(1, 1);
3936 let insert = create_region(®ion_server, region_id, &wal_options).await;
3937 let mito = region_server
3938 .inner
3939 .region_map
3940 .get(®ion_id)
3941 .unwrap()
3942 .clone()
3943 .into_engine();
3944 mito.stop().await.unwrap();
3945
3946 for requests in [vec![insert.clone()], vec![insert.clone(), insert]] {
3948 let count = requests.len();
3949 let response = region_server
3950 .handle_requests_in_parallel(region_request::Body::Inserts(InsertRequests {
3951 requests,
3952 }))
3953 .await
3954 .unwrap();
3955 assert_eq!(0, response.affected_rows);
3956 assert_eq!(count, response.failures.len());
3957 for (index, (request_index, error)) in response.failures.iter().enumerate() {
3958 assert_eq!(index, *request_index);
3959 assert!(format!("{error:?}").contains("is stopped"), "{error:?}");
3960 }
3961 }
3962 }
3963
3964 #[tokio::test]
3965 async fn test_default_batch_puts_handle_each_put_as_a_request() {
3966 let failing = RegionId::new(1, 2);
3967 let (engine, _receiver) = MockRegionEngine::with_mock_fn(
3968 "mock",
3969 Box::new(move |region_id, request| {
3970 assert!(matches!(request, RegionRequest::Put(_)));
3971 if region_id == failing {
3972 return error::UnexpectedSnafu {
3973 violated: "test".to_string(),
3974 }
3975 .fail();
3976 }
3977 Ok(region_id.region_number() as usize)
3978 }),
3979 );
3980 let region_ids = [1, 2, 3].map(|n| RegionId::new(1, n));
3981 let puts = region_ids
3982 .iter()
3983 .map(|region_id| {
3984 let request = RegionPutRequest {
3985 rows: Rows {
3986 schema: Vec::new(),
3987 rows: Vec::new(),
3988 },
3989 hint: None,
3990 skip_wal: false,
3991 partition_expr_version: None,
3992 };
3993 (*region_id, request)
3994 })
3995 .collect();
3996
3997 let (answered, responses): (Vec<_>, Vec<_>) =
3998 engine.handle_batch_put_requests(puts).into_iter().unzip();
3999 let responses = futures_util::future::join_all(responses).await;
4000
4001 let answered_regions = region_ids.map(|region_id| vec![region_id]);
4002 assert_eq!(answered_regions.to_vec(), answered);
4003 assert_eq!(1, responses[0].as_ref().unwrap().affected_rows);
4004 assert!(responses[1].is_err());
4005 assert_eq!(3, responses[2].as_ref().unwrap().affected_rows);
4006 }
4007
4008 #[tokio::test]
4009 async fn test_only_several_puts_into_ready_mito_regions_are_batched() {
4010 let mut env = TestEnv::with_prefix("region-server-mito-batch-puts-guard").await;
4011 let mito: RegionEngineRef = Arc::new(env.create_engine(MitoConfig::default()).await);
4012 let (other, _receiver) = MockRegionEngine::new("mock");
4013 let region_server = mock_region_server();
4014 let [mito_a, mito_b, registering, other_region] = [1, 2, 3, 4].map(|n| RegionId::new(1, n));
4015 let region_map = ®ion_server.inner.region_map;
4016 region_map.insert(mito_a, RegionEngineWithStatus::Ready(mito.clone()));
4017 region_map.insert(mito_b, RegionEngineWithStatus::Ready(mito.clone()));
4018 region_map.insert(registering, RegionEngineWithStatus::Registering(mito));
4019 region_map.insert(other_region, RegionEngineWithStatus::Ready(other));
4020 let rows = || Rows {
4021 schema: Vec::new(),
4022 rows: Vec::new(),
4023 };
4024 let put = |region_id| {
4025 let request = RegionPutRequest {
4026 rows: rows(),
4027 hint: None,
4028 skip_wal: false,
4029 partition_expr_version: None,
4030 };
4031 IndexedRegionRequest {
4032 index: 0,
4033 region_id,
4034 request: RegionRequest::Put(request),
4035 }
4036 };
4037 let delete = |region_id| {
4038 let request = RegionDeleteRequest {
4039 rows: rows(),
4040 hint: None,
4041 partition_expr_version: None,
4042 };
4043 IndexedRegionRequest {
4044 index: 0,
4045 region_id,
4046 request: RegionRequest::Delete(request),
4047 }
4048 };
4049
4050 for requests in [
4051 vec![put(mito_a), put(mito_b)],
4052 vec![put(mito_a), put(mito_a)],
4053 ] {
4054 assert!(
4055 region_server
4056 .mito_engine_for_batch_puts(&requests)
4057 .is_some()
4058 );
4059 }
4060 for requests in [
4061 vec![put(mito_a)],
4062 vec![put(mito_a), delete(mito_b)],
4063 vec![put(mito_a), put(registering)],
4064 vec![put(mito_a), put(other_region)],
4065 vec![put(other_region), put(mito_a)],
4066 ] {
4067 assert!(
4068 region_server
4069 .mito_engine_for_batch_puts(&requests)
4070 .is_none()
4071 );
4072 }
4073 }
4074}