Skip to main content

datanode/
region_server.rs

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