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