Skip to main content

datanode/
region_server.rs

1// Copyright 2023 Greptime Team
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15mod 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    /// Registers an engine.
181    pub fn register_engine(&mut self, engine: RegionEngineRef) {
182        self.inner.register_engine(engine);
183    }
184
185    /// Sets the topic stats.
186    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    /// Finds the region's engine by its id. If the region is not ready, returns `None`.
191    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    /// Gets the MitoEngine if it's registered.
201    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    /// Returns a table provider for the region. Will set snapshot sequence if available in the context.
264    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(&region_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    /// Handle reads from remote. They're often query requests received by our Arrow Flight service.
291    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    /// Returns all opened and reportable regions.
417    ///
418    /// Notes: except all metrics regions.
419    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                // Filters out any regions whose role equals None.
426                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    /// Returns the reportable topics.
436    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(&region_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(&region_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    /// Set region role state gracefully.
476    ///
477    /// For [SettableRegionRoleState::Follower]:
478    /// After the call returns, the engine ensures that
479    /// no **further** write or flush operations will succeed in this region.
480    ///
481    /// For [SettableRegionRoleState::DowngradingLeader]:
482    /// After the call returns, the engine ensures that
483    /// no **further** write operations will succeed in this region.
484    pub async fn set_region_role_state_gracefully(
485        &self,
486        region_id: RegionId,
487        state: SettableRegionRoleState,
488    ) -> Result<SetRegionRoleStateResponse> {
489        // Cloning releases the DashMap guard before awaiting the engine operation.
490        let engine = self
491            .inner
492            .region_map
493            .get(&region_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(&region_id) {
511            Some(e) => e.region_statistic(region_id),
512            None => None,
513        }
514    }
515
516    /// Stop the region server.
517    pub async fn stop(&self) -> Result<()> {
518        self.inner.stop().await
519    }
520
521    #[cfg(test)]
522    /// Registers a region for test purpose.
523    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        // Safety: we have already checked the request type in `RegionServer::handle()`.
542        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    /// Attempts metric batch writes, returning the requests when ordinary execution is needed.
632    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        // Upstream batching keeps requests from the same engine together.
647        // Ordinary execution reports engine lookup failures per entry.
648        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    /// Batches puts into ready mito regions and retains every entry's outcome.
725    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 &region_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    /// Returns the mito engine when there are at least two requests, every one a put into a ready
810    /// mito region.
811    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    /// Handles the ListMetadata request and retrieves metadata for specified regions.
868    ///
869    /// Returns the results as a JSON-serialized list in the [RegionResponse]. It serializes
870    /// non-existing regions as `null`.
871    #[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        // Collect metadata for each region
878        for region_id in &request.region_ids {
879            let region_id = RegionId::from_u64(*region_id);
880            // Get the engine.
881            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        // Serialize metadata to JSON
902        let json_result = serde_json::to_vec(&region_metadatas).context(SerializeJsonSnafu)?;
903
904        let response = RegionResponse::from_metadata(json_result);
905
906        Ok(response)
907    }
908
909    /// Sync region manifest and registers new opened logical regions.
910    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    /// Remaps manifests from old regions to new regions.
931    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
975/// Wraps a region read stream so terminal metrics can carry the scan-open watermark.
976struct 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            // Old clients and single-entry writes retain tonic errors and retry metadata.
1154            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
1219/// A decoded write retains its protobuf entry index even when preceding entries are empty.
1220struct 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    // An opening, or creating region.
1313    Registering(RegionEngineRef),
1314    // A closing, or dropping region.
1315    Deregistering(RegionEngineRef),
1316    // A ready region.
1317    Ready(RegionEngineRef),
1318}
1319
1320impl RegionEngineWithStatus {
1321    /// Returns [RegionEngineRef].
1322    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    /// The number of queries allowed to be executed at the same time.
1351    /// Act as last line of defense on datanode to prevent query overloading.
1352    parallelism: Option<RegionServerParallelism>,
1353    /// The topic stats reporter.
1354    topic_stats_reporter: RwLock<Option<Box<dyn TopicStatsReporter>>>,
1355    /// HACK(zhongzc): Direct MitoEngine handle for diagnostics. This couples the
1356    /// server with a concrete engine; acceptable for now to fetch Mito-specific
1357    /// info (e.g., list SSTs). Consider a diagnostics trait later.
1358    mito_engine: RwLock<Option<MitoEngine>>,
1359    /// TODO(remote-dyn-filter): Reap this query-scoped placeholder registry on query finish/cancel
1360    /// and later fold it into the real remote dyn filter runtime state lifecycle.
1361    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
1391/// Wraps a record batch stream and holds a concurrency permit until the stream is
1392/// fully consumed (dropped), so `max_concurrent_queries` bounds the number of
1393/// in-flight read streams, not just query planning.
1394struct 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
1425/// Wraps `stream` so it holds `permit` until fully consumed. Returns `stream`
1426/// unchanged when no permit was acquired (limiter disabled).
1427fn 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(&region_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 (&region_id, region_change) in &region_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 = &region_changes[&region_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 (&region_id, region_change) in &region_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            // Preserve the first region error so callers can honor its status code and retry hint.
1657            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    // Handle requests in batch.
1821    //
1822    // limitation: all create requests must be in the same engine.
1823    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        // The ddl procedure will ensure all requests are in the same engine.
1846        // Therefore, we can get the engine from the first request.
1847        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 &region_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, &region_change)? {
1942            CurrentEngine::Engine(engine) => engine,
1943            CurrentEngine::EarlyReturn(rows) => return Ok(RegionResponse::new(rows)),
1944        };
1945
1946        // Sets corresponding region status to registering/deregistering before the operation.
1947        self.set_region_status_not_ready(region_id, &engine, &region_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                // Sets corresponding region status to ready.
1959                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                // Removes the region status if the operation fails.
1970                self.unset_region_status(region_id, &engine, region_change);
1971                Err(err)
1972            }
1973        }
1974    }
1975
1976    /// Handles the sync region request.
1977    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(&region_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                            // Registers the logical regions belong to the physical region (`region_id`).
2071                            self.register_logical_regions(&engine, region_id).await?;
2072                            // We only send the `on_region_registered` event of the physical region.
2073                            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                        // do nothing
2079                    }
2080                }
2081            }
2082            RegionChange::Deregisters => {
2083                info!("Region {region_id} is deregistered from engine {engine_type}");
2084                self.region_map
2085                    .remove(&region_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                    // Registers the logical regions belong to the physical region (`region_id`).
2092                    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        // TODO(ruihang): add metrics and set trace id
2199
2200        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        // Calling async functions while iterating inside the Dashmap could easily cause the Rust
2216        // complains "higher-ranked lifetime error". Rust can't prove some future is legit.
2217        // Possible related issue: https://github.com/rust-lang/rust/issues/102211
2218        //
2219        // The workaround is to put the async functions in the `common_runtime::spawn_global`. Or like
2220        // it here, collect the values first then use later separately.
2221
2222        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                    // A close that failed may not have flushed writes that
2241                    // skipped the WAL, which nothing replays.
2242                    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
2284/// Records failed open attempts without using arbitrary engine names as metric labels.
2285fn 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
2295/// Records the result of `count` ingest requests answered by one response: the rows they
2296/// changed, or their failures.
2297fn 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(&region_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(&region_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        // Tries to create/open a registering region.
2626        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(&region_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(&region_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        // Tries to drop/close a registering region.
2684        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(&region_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(&region_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        // Tries to drop/close a registering region.
2745        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                    // A preceding empty entry must still count toward the original envelope size.
2801                    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(&region_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(&region_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    /// A region that fails to close is reported in the result of the stop,
3045    /// which still closes the other regions.
3046    #[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            // RegionChange::None
3192            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            // RegionChange::Register
3229            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            // RegionChange::Deregister
3266            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            // RegionChange::OfflineCleanup
3303            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            // Sets up
3350            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(&region_id);
3357            }
3358
3359            let result = mock_region_server
3360                .inner
3361                .get_engine(region_id, &region_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        // All regions exist.
3457        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        // Not in region map.
3499        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        // Not in region engine.
3511        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        // Failed to get.
3548        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    /// Returns a region server with a Mito engine on a durable object store WAL, the WAL, and the
3558    /// WAL options of a region on it.
3559    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    /// Creates a region with `wal_options` and returns an insert of three rows into it.
3585    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        // Both entries of the second physical group fail at write_data.
3684        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        // One worker that takes one message at a time, so that only puts that
3720        // arrive in one message share a WAL append.
3721        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(&region_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        // The worker got both puts at once and appended them together.
3742        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        // As above: only puts that arrive in one message share a WAL append.
3753        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(&region_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(&region_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            // Regions 1 and 2 of table 1 belong to different workers. The put to region 1 waits for a
3832            // WAL object whose create is held; Mito does not know region 2, so its put fails at once.
3833            let held = RegionId::new(1, 1);
3834            let unknown = RegionId::new(1, 2);
3835            let held_insert = create_region(&region_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(&region_server, region_id, &wal_options).await;
3908        let mito = region_server
3909            .inner
3910            .region_map
3911            .get(&region_id)
3912            .unwrap()
3913            .clone()
3914            .into_engine();
3915        mito.stop().await.unwrap();
3916
3917        // One put takes the per-region path and two take the batch path.
3918        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 = &region_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}