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