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