1#[cfg(test)]
18mod alter_test;
19#[cfg(test)]
20mod append_mode_test;
21#[cfg(test)]
22mod basic_test;
23#[cfg(test)]
24mod batch_catchup_test;
25#[cfg(test)]
26mod batch_open_test;
27#[cfg(test)]
28mod bump_committed_sequence_test;
29#[cfg(test)]
30mod catchup_test;
31#[cfg(test)]
32mod close_test;
33#[cfg(test)]
34pub(crate) mod compaction_test;
35#[cfg(test)]
36mod create_test;
37#[cfg(test)]
38mod drop_test;
39#[cfg(test)]
40mod edit_region_test;
41#[cfg(test)]
42mod file_ref_test;
43#[cfg(test)]
44mod filter_deleted_test;
45#[cfg(test)]
46mod flush_test;
47#[cfg(test)]
48mod index_build_test;
49#[cfg(any(test, feature = "test"))]
50pub mod listener;
51#[cfg(test)]
52mod merge_mode_test;
53#[cfg(test)]
54mod object_store_wal_recovery_test;
55#[cfg(test)]
56mod object_store_wal_test;
57#[cfg(test)]
58mod open_test;
59#[cfg(test)]
60mod parallel_test;
61#[cfg(test)]
62mod projection_test;
63#[cfg(test)]
64mod prune_test;
65pub mod region_hook;
66#[cfg(test)]
67mod row_selector_test;
68#[cfg(test)]
69mod scan_corrupt;
70#[cfg(test)]
71mod scan_test;
72#[cfg(test)]
73mod set_role_state_test;
74#[cfg(test)]
75mod skip_wal_test;
76#[cfg(test)]
77mod staging_test;
78#[cfg(test)]
79mod sync_test;
80#[cfg(test)]
81mod truncate_test;
82
83#[cfg(test)]
84mod copy_region_from_test;
85#[cfg(test)]
86mod remap_manifests_test;
87
88#[cfg(test)]
89mod apply_staging_manifest_test;
90#[cfg(test)]
91mod partition_filter_test;
92mod puffin_index;
93
94use std::any::Any;
95use std::collections::{HashMap, HashSet};
96use std::sync::Arc;
97use std::time::Instant;
98
99use api::region::RegionResponse;
100use async_trait::async_trait;
101use common_base::Plugins;
102use common_error::ext::BoxedError;
103use common_meta::error::UnexpectedSnafu;
104use common_meta::key::SchemaMetadataManagerRef;
105use common_recordbatch::{QueryMemoryTracker, SendableRecordBatchStream};
106use common_stat::get_total_memory_bytes;
107use common_telemetry::{debug, info, tracing, warn};
108use common_wal::options::WalOptions;
109use datafusion::execution::memory_pool::{GreedyMemoryPool, MemoryPool, UnboundedMemoryPool};
110use futures::future::{join_all, try_join_all};
111use futures::stream::{self, Stream, StreamExt};
112use object_store::manager::ObjectStoreManagerRef;
113use region_hook::RegionHookRef;
114use snafu::{OptionExt, ResultExt, ensure};
115use store_api::ManifestVersion;
116use store_api::codec::PrimaryKeyEncoding;
117use store_api::logstore::LogStore;
118use store_api::logstore::provider::{KafkaProvider, Provider};
119use store_api::metadata::{ColumnMetadata, RegionMetadataRef};
120use store_api::metric_engine_consts::{
121 MANIFEST_INFO_EXTENSION_KEY, TABLE_COLUMN_METADATA_EXTENSION_KEY,
122};
123use store_api::region_engine::{
124 BatchResponses, MitoCopyRegionFromRequest, MitoCopyRegionFromResponse, RegionEngine,
125 RegionManifestInfo, RegionRole, RegionScannerRef, RegionStatistic, RemapManifestsRequest,
126 RemapManifestsResponse, SetRegionRoleStateResponse, SettableRegionRoleState,
127 SyncRegionFromRequest, SyncRegionFromResponse,
128};
129use store_api::region_info::RegionInfoEntry;
130use store_api::region_request::{
131 AffectedRows, RegionCatchupRequest, RegionOpenRequest, RegionRequest,
132};
133use store_api::sst_entry::{ManifestSstEntry, PuffinIndexMetaEntry, StorageSstEntry};
134use store_api::storage::{FileId, FileRefsManifest, RegionId, ScanRequest, SequenceNumber};
135use tokio::sync::{Semaphore, oneshot};
136
137use crate::access_layer::RegionFilePathFactory;
138use crate::cache::{CacheManagerRef, CacheStrategy};
139use crate::config::MitoConfig;
140use crate::engine::puffin_index::{IndexEntryContext, collect_index_entries_from_puffin};
141use crate::error::{
142 IncrementalQueryStaleSnafu, InvalidRequestSnafu, JoinSnafu, MitoManifestInfoSnafu, RecvSnafu,
143 RegionNotFoundSnafu, Result, SequenceRangeUnsupportedSnafu, SerdeJsonSnafu,
144 SerializeColumnMetadataSnafu, SnapshotFenceStaleSnafu,
145};
146#[cfg(feature = "enterprise")]
147use crate::extension::BoxedExtensionRangeProviderFactory;
148use crate::gc::GcLimiterRef;
149use crate::manifest::action::RegionEdit;
150use crate::memtable::MemtableStats;
151use crate::metrics::{
152 HANDLE_REQUEST_ELAPSED, SCAN_MEMORY_EXHAUSTED_TOTAL, SCAN_MEMORY_USAGE_BYTES,
153 SCAN_REQUESTS_REJECTED_TOTAL,
154};
155use crate::read::scan_region::{ScanRegion, Scanner, exact_sequence_range};
156use crate::read::stream::ScanBatchStream;
157use crate::region::MitoRegionRef;
158use crate::region::opener::PartitionExprFetcherRef;
159use crate::region::options::parse_wal_options;
160use crate::request::{RegionEditRequest, WorkerRequest};
161use crate::sst::file::{FileMeta, RegionFileId, RegionIndexId};
162use crate::sst::file_ref::FileReferenceManagerRef;
163use crate::sst::index::intermediate::IntermediateManager;
164use crate::sst::index::puffin_manager::PuffinManagerFactory;
165use crate::wal::entry_distributor::{
166 DEFAULT_ENTRY_RECEIVER_BUFFER_SIZE, build_wal_entry_distributor_and_receivers,
167};
168use crate::wal::raw_entry_reader::{LogStoreRawEntryReader, RawEntryReader};
169use crate::worker::WorkerGroup;
170
171pub const MITO_ENGINE_NAME: &str = "mito";
172
173pub struct MitoEngineBuilder<'a, S: LogStore> {
174 data_home: &'a str,
175 config: MitoConfig,
176 log_store: Arc<S>,
177 object_store_manager: ObjectStoreManagerRef,
178 schema_metadata_manager: SchemaMetadataManagerRef,
179 file_ref_manager: FileReferenceManagerRef,
180 partition_expr_fetcher: PartitionExprFetcherRef,
181 plugins: Plugins,
182 #[cfg(feature = "enterprise")]
183 extension_range_provider_factory: Option<BoxedExtensionRangeProviderFactory>,
184}
185
186impl<'a, S: LogStore> MitoEngineBuilder<'a, S> {
187 #[allow(clippy::too_many_arguments)]
188 pub fn new(
189 data_home: &'a str,
190 config: MitoConfig,
191 log_store: Arc<S>,
192 object_store_manager: ObjectStoreManagerRef,
193 schema_metadata_manager: SchemaMetadataManagerRef,
194 file_ref_manager: FileReferenceManagerRef,
195 partition_expr_fetcher: PartitionExprFetcherRef,
196 plugins: Plugins,
197 ) -> Self {
198 Self {
199 data_home,
200 config,
201 log_store,
202 object_store_manager,
203 schema_metadata_manager,
204 file_ref_manager,
205 plugins,
206 partition_expr_fetcher,
207 #[cfg(feature = "enterprise")]
208 extension_range_provider_factory: None,
209 }
210 }
211
212 #[cfg(feature = "enterprise")]
213 #[must_use]
214 pub fn with_extension_range_provider_factory(
215 self,
216 extension_range_provider_factory: Option<BoxedExtensionRangeProviderFactory>,
217 ) -> Self {
218 Self {
219 extension_range_provider_factory,
220 ..self
221 }
222 }
223
224 pub async fn try_build(mut self) -> Result<MitoEngine> {
225 self.config.sanitize(self.data_home)?;
226
227 let config = Arc::new(self.config);
228 let region_hook = self.plugins.get::<RegionHookRef>();
231 let workers = WorkerGroup::start(
232 self.data_home,
233 config.clone(),
234 self.log_store.clone(),
235 self.object_store_manager,
236 self.schema_metadata_manager,
237 self.file_ref_manager,
238 self.partition_expr_fetcher.clone(),
239 self.plugins,
240 )
241 .await?;
242 let wal_raw_entry_reader = Arc::new(LogStoreRawEntryReader::new(self.log_store));
243 let total_memory = get_total_memory_bytes().max(0) as u64;
244 let scan_memory_limit = config.scan_memory_limit.resolve(total_memory) as usize;
245 let scan_memory_pool = new_scan_memory_pool(scan_memory_limit);
246 let scan_memory_tracker =
247 QueryMemoryTracker::builder(scan_memory_limit, config.scan_memory_on_exhausted)
248 .on_update(|usage| {
249 SCAN_MEMORY_USAGE_BYTES.set(usage as i64);
250 })
251 .on_exhausted(|| {
252 SCAN_MEMORY_EXHAUSTED_TOTAL.inc();
253 })
254 .on_reject(|| {
255 SCAN_REQUESTS_REJECTED_TOTAL.inc();
256 })
257 .build();
258
259 let inner = EngineInner {
260 workers,
261 config,
262 wal_raw_entry_reader,
263 scan_memory_tracker,
264 scan_memory_pool,
265 region_hook,
266 #[cfg(feature = "enterprise")]
267 extension_range_provider_factory: None,
268 };
269
270 #[cfg(feature = "enterprise")]
271 let inner =
272 inner.with_extension_range_provider_factory(self.extension_range_provider_factory);
273
274 Ok(MitoEngine {
275 inner: Arc::new(inner),
276 })
277 }
278}
279
280#[derive(Clone)]
282pub struct MitoEngine {
283 inner: Arc<EngineInner>,
284}
285
286impl MitoEngine {
287 #[allow(clippy::too_many_arguments)]
289 pub async fn new<S: LogStore>(
290 data_home: &str,
291 config: MitoConfig,
292 log_store: Arc<S>,
293 object_store_manager: ObjectStoreManagerRef,
294 schema_metadata_manager: SchemaMetadataManagerRef,
295 file_ref_manager: FileReferenceManagerRef,
296 partition_expr_fetcher: PartitionExprFetcherRef,
297 plugins: Plugins,
298 ) -> Result<MitoEngine> {
299 let builder = MitoEngineBuilder::new(
300 data_home,
301 config,
302 log_store,
303 object_store_manager,
304 schema_metadata_manager,
305 file_ref_manager,
306 partition_expr_fetcher,
307 plugins,
308 );
309 builder.try_build().await
310 }
311
312 pub fn mito_config(&self) -> &MitoConfig {
313 &self.inner.config
314 }
315
316 pub fn cache_manager(&self) -> CacheManagerRef {
317 self.inner.workers.cache_manager()
318 }
319
320 pub fn file_ref_manager(&self) -> FileReferenceManagerRef {
321 self.inner.workers.file_ref_manager()
322 }
323
324 pub fn gc_limiter(&self) -> GcLimiterRef {
325 self.inner.workers.gc_limiter()
326 }
327
328 pub fn object_store_manager(&self) -> &ObjectStoreManagerRef {
329 self.inner.workers.object_store_manager()
330 }
331
332 pub fn puffin_manager_factory(&self) -> &PuffinManagerFactory {
333 self.inner.workers.puffin_manager_factory()
334 }
335
336 pub fn intermediate_manager(&self) -> &IntermediateManager {
337 self.inner.workers.intermediate_manager()
338 }
339
340 pub fn schema_metadata_manager(&self) -> &SchemaMetadataManagerRef {
341 self.inner.workers.schema_metadata_manager()
342 }
343
344 pub fn region_hook(&self) -> Option<RegionHookRef> {
347 self.inner.region_hook.clone()
348 }
349
350 pub async fn get_snapshot_of_file_refs(
352 &self,
353 file_handle_regions: impl IntoIterator<Item = RegionId>,
354 related_regions: HashMap<RegionId, HashSet<RegionId>>,
355 ) -> Result<FileRefsManifest> {
356 let file_ref_mgr = self.file_ref_manager();
357
358 let file_handle_regions = file_handle_regions.into_iter().collect::<Vec<_>>();
359 let query_regions: Vec<MitoRegionRef> = file_handle_regions
362 .into_iter()
363 .filter_map(|region_id| self.find_region(region_id))
364 .collect();
365
366 let dst_region_to_src_regions: Vec<(MitoRegionRef, HashSet<RegionId>)> = {
367 let dst2src = related_regions
368 .into_iter()
369 .flat_map(|(src, dsts)| dsts.into_iter().map(move |dst| (dst, src)))
370 .fold(
371 HashMap::<RegionId, HashSet<RegionId>>::new(),
372 |mut acc, (k, v)| {
373 let entry = acc.entry(k).or_default();
374 entry.insert(v);
375 acc
376 },
377 );
378 let mut dst_region_to_src_regions = Vec::with_capacity(dst2src.len());
379 for (dst_region, srcs) in dst2src {
380 let Some(region) = self.find_region(dst_region) else {
381 return RegionNotFoundSnafu {
382 region_id: dst_region,
383 }
384 .fail();
385 };
386 dst_region_to_src_regions.push((region, srcs));
387 }
388 dst_region_to_src_regions
389 };
390
391 file_ref_mgr
392 .get_snapshot_of_file_refs(query_regions, dst_region_to_src_regions)
393 .await
394 }
395
396 pub fn is_region_exists(&self, region_id: RegionId) -> bool {
398 self.inner.workers.is_region_exists(region_id)
399 }
400
401 pub fn is_region_opening(&self, region_id: RegionId) -> bool {
403 self.inner.workers.is_region_opening(region_id)
404 }
405
406 pub fn is_region_catching_up(&self, region_id: RegionId) -> bool {
408 self.inner.workers.is_region_catching_up(region_id)
409 }
410
411 pub fn get_region_statistic(&self, region_id: RegionId) -> Option<RegionStatistic> {
413 self.find_region(region_id)
414 .map(|region| region.region_statistic())
415 }
416
417 pub fn get_primary_key_encoding(&self, region_id: RegionId) -> Option<PrimaryKeyEncoding> {
419 self.find_region(region_id)
420 .map(|r| r.primary_key_encoding())
421 }
422
423 #[tracing::instrument(skip_all)]
428 pub async fn scan_to_stream(
429 &self,
430 region_id: RegionId,
431 request: ScanRequest,
432 ) -> Result<SendableRecordBatchStream, BoxedError> {
433 self.scanner(region_id, request)
434 .await
435 .map_err(BoxedError::new)?
436 .scan()
437 .await
438 }
439
440 pub async fn scan_batch(
442 &self,
443 region_id: RegionId,
444 request: ScanRequest,
445 filter_deleted: bool,
446 ) -> Result<ScanBatchStream> {
447 let mut scan_region = self.scan_region(region_id, request)?;
448 scan_region.set_filter_deleted(filter_deleted);
449 scan_region.scanner().await?.scan_batch()
450 }
451
452 pub(crate) async fn scanner(
454 &self,
455 region_id: RegionId,
456 request: ScanRequest,
457 ) -> Result<Scanner> {
458 self.scan_region(region_id, request)?.scanner().await
459 }
460
461 #[tracing::instrument(skip_all, fields(region_id = %region_id))]
463 fn scan_region(&self, region_id: RegionId, request: ScanRequest) -> Result<ScanRegion> {
464 self.inner.scan_region(region_id, request)
465 }
466
467 pub async fn edit_region(&self, region_id: RegionId, edit: RegionEdit) -> Result<()> {
472 let _timer = HANDLE_REQUEST_ELAPSED
473 .with_label_values(&["edit_region"])
474 .start_timer();
475
476 ensure!(
477 is_valid_region_edit(&edit),
478 InvalidRequestSnafu {
479 region_id,
480 reason: "invalid region edit"
481 }
482 );
483
484 let (tx, rx) = oneshot::channel();
485 let request = WorkerRequest::EditRegion(RegionEditRequest::new(region_id, edit, true, tx));
486 self.inner
487 .workers
488 .submit_to_worker(region_id, request)
489 .await?;
490 rx.await.context(RecvSnafu)?
491 }
492
493 pub async fn copy_region_from(
497 &self,
498 region_id: RegionId,
499 request: MitoCopyRegionFromRequest,
500 ) -> Result<MitoCopyRegionFromResponse> {
501 self.inner.copy_region_from(region_id, request).await
502 }
503
504 #[cfg(test)]
505 pub(crate) fn get_region(&self, id: RegionId) -> Option<crate::region::MitoRegionRef> {
506 self.find_region(id)
507 }
508
509 pub fn find_region(&self, region_id: RegionId) -> Option<MitoRegionRef> {
510 self.inner.workers.get_region(region_id)
511 }
512
513 pub fn regions(&self) -> Vec<MitoRegionRef> {
515 self.inner.workers.all_regions().collect()
516 }
517
518 fn encode_manifest_info_to_extensions(
519 region_id: &RegionId,
520 manifest_info: RegionManifestInfo,
521 extensions: &mut HashMap<String, Vec<u8>>,
522 ) -> Result<()> {
523 let region_manifest_info = vec![(*region_id, manifest_info)];
524
525 extensions.insert(
526 MANIFEST_INFO_EXTENSION_KEY.to_string(),
527 RegionManifestInfo::encode_list(®ion_manifest_info).context(SerdeJsonSnafu)?,
528 );
529 info!(
530 "Added manifest info: {:?} to extensions, region_id: {:?}",
531 region_manifest_info, region_id
532 );
533 Ok(())
534 }
535
536 fn encode_column_metadatas_to_extensions(
537 region_id: &RegionId,
538 column_metadatas: Vec<ColumnMetadata>,
539 extensions: &mut HashMap<String, Vec<u8>>,
540 ) -> Result<()> {
541 extensions.insert(
542 TABLE_COLUMN_METADATA_EXTENSION_KEY.to_string(),
543 ColumnMetadata::encode_list(&column_metadatas).context(SerializeColumnMetadataSnafu)?,
544 );
545 info!(
546 "Added column metadatas: {:?} to extensions, region_id: {:?}",
547 column_metadatas, region_id
548 );
549 Ok(())
550 }
551
552 pub fn find_memtable_and_sst_stats(
555 &self,
556 region_id: RegionId,
557 ) -> Result<(Vec<MemtableStats>, Vec<FileMeta>)> {
558 let region = self
559 .find_region(region_id)
560 .context(RegionNotFoundSnafu { region_id })?;
561
562 let version = region.version();
563 let memtable_stats = version
564 .memtables
565 .list_memtables()
566 .iter()
567 .map(|x| x.stats())
568 .collect::<Vec<_>>();
569
570 let sst_stats = version
571 .ssts
572 .levels()
573 .iter()
574 .flat_map(|level| level.files().map(|x| x.meta_ref()))
575 .cloned()
576 .collect::<Vec<_>>();
577 Ok((memtable_stats, sst_stats))
578 }
579
580 pub async fn all_ssts_from_manifest(&self) -> Vec<ManifestSstEntry> {
582 let node_id = self.inner.workers.file_ref_manager().node_id();
583 let regions = self.inner.workers.all_regions();
584
585 let mut results = Vec::new();
586 for region in regions {
587 let mut entries = region.manifest_sst_entries().await;
588 for e in &mut entries {
589 e.node_id = node_id;
590 }
591 results.extend(entries);
592 }
593
594 results
595 }
596
597 pub async fn all_index_metas(&self) -> Vec<PuffinIndexMetaEntry> {
599 let node_id = self.inner.workers.file_ref_manager().node_id();
600 let cache_manager = self.inner.workers.cache_manager();
601 let puffin_metadata_cache = cache_manager.puffin_metadata_cache().cloned();
602 let bloom_filter_cache = cache_manager.bloom_filter_index_cache().cloned();
603 let inverted_index_cache = cache_manager.inverted_index_cache().cloned();
604
605 let mut results = Vec::new();
606
607 for region in self.inner.workers.all_regions() {
608 let manifest_entries = region.manifest_sst_entries().await;
609 let access_layer = region.access_layer.clone();
610 let table_dir = access_layer.table_dir().to_string();
611 let path_type = access_layer.path_type();
612 let object_store = access_layer.object_store().clone();
613 let puffin_factory = access_layer.puffin_manager_factory().clone();
614 let path_factory = RegionFilePathFactory::new(table_dir, path_type);
615
616 let entry_futures = manifest_entries.into_iter().map(|entry| {
617 let object_store = object_store.clone();
618 let path_factory = path_factory.clone();
619 let puffin_factory = puffin_factory.clone();
620 let puffin_metadata_cache = puffin_metadata_cache.clone();
621 let bloom_filter_cache = bloom_filter_cache.clone();
622 let inverted_index_cache = inverted_index_cache.clone();
623
624 async move {
625 let Some(index_file_path) = entry.index_file_path.as_ref() else {
626 return Vec::new();
627 };
628
629 let index_version = entry.index_version;
630 let file_id = match FileId::parse_str(&entry.file_id) {
631 Ok(file_id) => file_id,
632 Err(err) => {
633 warn!(
634 err;
635 "Failed to parse puffin index file id, table_dir: {}, file_id: {}",
636 entry.table_dir,
637 entry.file_id
638 );
639 return Vec::new();
640 }
641 };
642 let region_index_id = RegionIndexId::new(
645 RegionFileId::new(entry.origin_region_id, file_id),
646 index_version,
647 );
648 let context = IndexEntryContext {
649 table_dir: &entry.table_dir,
650 index_file_path: index_file_path.as_str(),
651 region_id: entry.region_id,
652 table_id: entry.table_id,
653 region_number: entry.region_number,
654 region_group: entry.region_group,
655 region_sequence: entry.region_sequence,
656 file_id: &entry.file_id,
657 index_file_size: entry.index_file_size,
658 node_id,
659 };
660
661 let manager = puffin_factory
662 .build(object_store, path_factory)
663 .with_puffin_metadata_cache(puffin_metadata_cache);
664
665 collect_index_entries_from_puffin(
666 manager,
667 region_index_id,
668 context,
669 bloom_filter_cache,
670 inverted_index_cache,
671 )
672 .await
673 }
674 });
675
676 let mut meta_stream = stream::iter(entry_futures).buffer_unordered(8); while let Some(mut metas) = meta_stream.next().await {
678 results.append(&mut metas);
679 }
680 }
681
682 results
683 }
684
685 pub async fn all_region_infos(&self) -> Vec<RegionInfoEntry> {
687 let node_id = self.inner.workers.file_ref_manager().node_id();
688 self.inner
689 .workers
690 .all_regions()
691 .map(|region| region.region_info_entry(node_id))
692 .collect()
693 }
694
695 pub fn all_ssts_from_storage(&self) -> impl Stream<Item = Result<StorageSstEntry>> {
697 let node_id = self.inner.workers.file_ref_manager().node_id();
698 let regions = self.inner.workers.all_regions();
699
700 let mut layers_distinct_table_dirs = HashMap::new();
701 for region in regions {
702 let table_dir = region.access_layer.table_dir();
703 if !layers_distinct_table_dirs.contains_key(table_dir) {
704 layers_distinct_table_dirs
705 .insert(table_dir.to_string(), region.access_layer.clone());
706 }
707 }
708
709 stream::iter(layers_distinct_table_dirs)
710 .map(|(_, access_layer)| access_layer.storage_sst_entries())
711 .flatten()
712 .map(move |entry| {
713 entry.map(move |mut entry| {
714 entry.node_id = node_id;
715 entry
716 })
717 })
718 }
719}
720
721fn is_valid_region_edit(edit: &RegionEdit) -> bool {
725 (!edit.files_to_add.is_empty() || !edit.files_to_remove.is_empty())
726 && matches!(
727 edit,
728 RegionEdit {
729 files_to_add: _,
730 files_to_remove: _,
731 timestamp_ms: _,
732 compaction_time_window: None,
733 flushed_entry_id: None,
734 flushed_sequence: None,
735 ..
736 }
737 )
738}
739
740struct EngineInner {
742 workers: WorkerGroup,
744 config: Arc<MitoConfig>,
746 wal_raw_entry_reader: Arc<dyn RawEntryReader>,
748 scan_memory_tracker: QueryMemoryTracker,
750 scan_memory_pool: Arc<dyn MemoryPool>,
752 region_hook: Option<RegionHookRef>,
755 #[cfg(feature = "enterprise")]
756 extension_range_provider_factory: Option<BoxedExtensionRangeProviderFactory>,
757}
758
759type TopicGroupedRegionOpenRequests = HashMap<String, Vec<(RegionId, RegionOpenRequest)>>;
760
761fn prepare_batch_open_requests(
763 requests: Vec<(RegionId, RegionOpenRequest)>,
764) -> Result<(
765 TopicGroupedRegionOpenRequests,
766 Vec<(RegionId, RegionOpenRequest)>,
767)> {
768 let mut topic_to_regions: HashMap<String, Vec<(RegionId, RegionOpenRequest)>> = HashMap::new();
769 let mut remaining_regions: Vec<(RegionId, RegionOpenRequest)> = Vec::new();
770 for (region_id, request) in requests {
771 match parse_wal_options(&request.options).context(SerdeJsonSnafu)? {
772 WalOptions::Kafka(options) => {
773 topic_to_regions
774 .entry(options.topic)
775 .or_default()
776 .push((region_id, request));
777 }
778 WalOptions::RaftEngine | WalOptions::Noop | WalOptions::ObjectStore(_) => {
779 remaining_regions.push((region_id, request));
780 }
781 }
782 }
783
784 Ok((topic_to_regions, remaining_regions))
785}
786
787impl EngineInner {
788 #[cfg(feature = "enterprise")]
789 #[must_use]
790 fn with_extension_range_provider_factory(
791 self,
792 extension_range_provider_factory: Option<BoxedExtensionRangeProviderFactory>,
793 ) -> Self {
794 Self {
795 extension_range_provider_factory,
796 ..self
797 }
798 }
799
800 async fn stop(&self) -> Result<()> {
802 self.workers.stop().await
803 }
804
805 fn find_region(&self, region_id: RegionId) -> Result<MitoRegionRef> {
806 self.workers
807 .get_region(region_id)
808 .context(RegionNotFoundSnafu { region_id })
809 }
810
811 fn get_metadata(&self, region_id: RegionId) -> Result<RegionMetadataRef> {
815 let region = self.find_region(region_id)?;
817 Ok(region.metadata())
818 }
819
820 async fn open_topic_regions(
821 &self,
822 topic: String,
823 region_requests: Vec<(RegionId, RegionOpenRequest)>,
824 ) -> Result<Vec<(RegionId, Result<AffectedRows>)>> {
825 let now = Instant::now();
826 let region_ids = region_requests
827 .iter()
828 .map(|(region_id, _)| *region_id)
829 .collect::<Vec<_>>();
830 let provider = Provider::kafka_provider(topic);
831 let (distributor, entry_receivers) = build_wal_entry_distributor_and_receivers(
832 provider.clone(),
833 self.wal_raw_entry_reader.clone(),
834 ®ion_ids,
835 DEFAULT_ENTRY_RECEIVER_BUFFER_SIZE,
836 );
837
838 let mut responses = Vec::with_capacity(region_requests.len());
839 for ((region_id, request), entry_receiver) in
840 region_requests.into_iter().zip(entry_receivers)
841 {
842 let (request, receiver) =
843 WorkerRequest::new_open_region_request(region_id, request, Some(entry_receiver));
844 self.workers.submit_to_worker(region_id, request).await?;
845 responses.push(async move { receiver.await.context(RecvSnafu)? });
846 }
847
848 let distribution =
850 common_runtime::spawn_global(async move { distributor.distribute().await });
851 let responses = join_all(responses).await;
853 distribution.await.context(JoinSnafu)??;
854
855 let num_failure = responses.iter().filter(|r| r.is_err()).count();
856 info!(
857 "Opened {} regions for topic '{}', failures: {}, elapsed: {:?}",
858 region_ids.len() - num_failure,
859 provider.as_kafka_provider().unwrap(),
861 num_failure,
862 now.elapsed(),
863 );
864 Ok(region_ids.into_iter().zip(responses).collect())
865 }
866
867 async fn handle_batch_open_requests(
868 &self,
869 parallelism: usize,
870 requests: Vec<(RegionId, RegionOpenRequest)>,
871 ) -> Result<Vec<(RegionId, Result<AffectedRows>)>> {
872 let semaphore = Arc::new(Semaphore::new(parallelism));
873 let (topic_to_region_requests, remaining_region_requests) =
874 prepare_batch_open_requests(requests)?;
875 let mut responses =
876 Vec::with_capacity(topic_to_region_requests.len() + remaining_region_requests.len());
877
878 if !topic_to_region_requests.is_empty() {
879 let mut tasks = Vec::with_capacity(topic_to_region_requests.len());
880 for (topic, region_requests) in topic_to_region_requests {
881 let semaphore_moved = semaphore.clone();
882 tasks.push(async move {
883 let _permit = semaphore_moved.acquire().await.unwrap();
885 self.open_topic_regions(topic, region_requests).await
886 })
887 }
888 let r = try_join_all(tasks).await?;
889 responses.extend(r.into_iter().flatten());
890 }
891
892 if !remaining_region_requests.is_empty() {
893 let mut tasks = Vec::with_capacity(remaining_region_requests.len());
894 let mut region_ids = Vec::with_capacity(remaining_region_requests.len());
895 for (region_id, request) in remaining_region_requests {
896 let semaphore_moved = semaphore.clone();
897 region_ids.push(region_id);
898 tasks.push(async move {
899 let _permit = semaphore_moved.acquire().await.unwrap();
901 let (request, receiver) =
902 WorkerRequest::new_open_region_request(region_id, request, None);
903
904 self.workers.submit_to_worker(region_id, request).await?;
905
906 receiver.await.context(RecvSnafu)?
907 })
908 }
909
910 let results = join_all(tasks).await;
911 responses.extend(region_ids.into_iter().zip(results));
912 }
913
914 Ok(responses)
915 }
916
917 async fn catchup_topic_regions(
918 &self,
919 provider: Provider,
920 region_requests: Vec<(RegionId, RegionCatchupRequest)>,
921 ) -> Result<Vec<(RegionId, Result<AffectedRows>)>> {
922 let now = Instant::now();
923 let region_ids = region_requests
924 .iter()
925 .map(|(region_id, _)| *region_id)
926 .collect::<Vec<_>>();
927 let (distributor, entry_receivers) = build_wal_entry_distributor_and_receivers(
928 provider.clone(),
929 self.wal_raw_entry_reader.clone(),
930 ®ion_ids,
931 DEFAULT_ENTRY_RECEIVER_BUFFER_SIZE,
932 );
933
934 let mut responses = Vec::with_capacity(region_requests.len());
935 for ((region_id, request), entry_receiver) in
936 region_requests.into_iter().zip(entry_receivers)
937 {
938 let (request, receiver) =
939 WorkerRequest::new_catchup_region_request(region_id, request, Some(entry_receiver));
940 self.workers.submit_to_worker(region_id, request).await?;
941 responses.push(async move { receiver.await.context(RecvSnafu)? });
942 }
943
944 let distribution =
946 common_runtime::spawn_global(async move { distributor.distribute().await });
947 let responses = join_all(responses).await;
949 distribution.await.context(JoinSnafu)??;
950
951 let num_failure = responses.iter().filter(|r| r.is_err()).count();
952 info!(
953 "Caught up {} regions for topic '{}', failures: {}, elapsed: {:?}",
954 region_ids.len() - num_failure,
955 provider.as_kafka_provider().unwrap(),
957 num_failure,
958 now.elapsed(),
959 );
960
961 Ok(region_ids.into_iter().zip(responses).collect())
962 }
963
964 async fn handle_batch_catchup_requests(
965 &self,
966 parallelism: usize,
967 requests: Vec<(RegionId, RegionCatchupRequest)>,
968 ) -> Result<Vec<(RegionId, Result<AffectedRows>)>> {
969 let mut responses = Vec::with_capacity(requests.len());
970 let mut topic_regions: HashMap<Arc<KafkaProvider>, Vec<_>> = HashMap::new();
971 let mut remaining_region_requests = vec![];
972
973 for (region_id, request) in requests {
974 match self.workers.get_region(region_id) {
975 Some(region) => match region.provider.as_kafka_provider() {
976 Some(provider) => {
977 topic_regions
978 .entry(provider.clone())
979 .or_default()
980 .push((region_id, request));
981 }
982 None => {
983 remaining_region_requests.push((region_id, request));
984 }
985 },
986 None => responses.push((region_id, RegionNotFoundSnafu { region_id }.fail())),
987 }
988 }
989
990 let semaphore = Arc::new(Semaphore::new(parallelism));
991
992 if !topic_regions.is_empty() {
993 let mut tasks = Vec::with_capacity(topic_regions.len());
994 for (provider, region_requests) in topic_regions {
995 let semaphore_moved = semaphore.clone();
996 tasks.push(async move {
997 let _permit = semaphore_moved.acquire().await.unwrap();
999 self.catchup_topic_regions(Provider::Kafka(provider), region_requests)
1000 .await
1001 })
1002 }
1003
1004 let r = try_join_all(tasks).await?;
1005 responses.extend(r.into_iter().flatten());
1006 }
1007
1008 if !remaining_region_requests.is_empty() {
1009 let mut tasks = Vec::with_capacity(remaining_region_requests.len());
1010 let mut region_ids = Vec::with_capacity(remaining_region_requests.len());
1011 for (region_id, request) in remaining_region_requests {
1012 let semaphore_moved = semaphore.clone();
1013 region_ids.push(region_id);
1014 tasks.push(async move {
1015 let _permit = semaphore_moved.acquire().await.unwrap();
1017 let (request, receiver) =
1018 WorkerRequest::new_catchup_region_request(region_id, request, None);
1019
1020 self.workers.submit_to_worker(region_id, request).await?;
1021
1022 receiver.await.context(RecvSnafu)?
1023 })
1024 }
1025
1026 let results = join_all(tasks).await;
1027 responses.extend(region_ids.into_iter().zip(results));
1028 }
1029
1030 Ok(responses)
1031 }
1032
1033 async fn handle_request(
1035 &self,
1036 region_id: RegionId,
1037 request: RegionRequest,
1038 ) -> Result<AffectedRows> {
1039 let region_metadata = self.get_metadata(region_id).ok();
1040 let (request, receiver) =
1041 WorkerRequest::try_from_region_request(region_id, request, region_metadata)?;
1042 self.workers.submit_to_worker(region_id, request).await?;
1043
1044 receiver.await.context(RecvSnafu)?
1045 }
1046
1047 fn get_committed_sequence(&self, region_id: RegionId) -> Result<SequenceNumber> {
1049 self.find_region(region_id)
1051 .map(|r| r.find_committed_sequence())
1052 }
1053
1054 #[tracing::instrument(skip_all, fields(region_id = %region_id))]
1056 fn scan_region(&self, region_id: RegionId, mut request: ScanRequest) -> Result<ScanRegion> {
1057 let query_start = Instant::now();
1058 let region = self.find_region(region_id)?;
1060 let series_index = region.series_index_store.as_ref().map(|store| {
1064 crate::series_index::SeriesIndexReadContext {
1065 store: store.clone(),
1066 version: region.series_index_version(),
1067 }
1068 });
1069 let version_data = region.version_control.current();
1070 let version = version_data.version;
1071
1072 if request.snapshot_on_scan && request.memtable_max_sequence.is_none() {
1073 request.memtable_max_sequence = Some(version_data.committed_sequence);
1074 }
1075
1076 let exact_selection = if request.exact_sequence_range {
1080 match exact_sequence_range(&request, &version) {
1081 Ok((files, sequence_range)) => Some((files, sequence_range)),
1082 Err(err) => {
1083 debug!(
1084 "Scan region {} exact sequence range denied: min={:?}, max={:?}, denial_reason=foreign_file_missing_barrier",
1085 region_id, request.memtable_min_sequence, request.memtable_max_sequence,
1086 );
1087 return Err(err);
1088 }
1089 }
1090 } else {
1091 None
1092 };
1093 let exact_sequence_range = exact_selection
1094 .as_ref()
1095 .and_then(|(_, sequence_range)| *sequence_range);
1096
1097 #[cfg(feature = "enterprise")]
1104 let extension_provider_blocks_exact = region.is_follower()
1105 && self.extension_range_provider_factory.is_some()
1106 && exact_sequence_range.is_some();
1107 #[cfg(not(feature = "enterprise"))]
1108 let extension_provider_blocks_exact = false;
1109 let exact_sequence_range = if extension_provider_blocks_exact {
1110 None
1111 } else {
1112 exact_sequence_range
1113 };
1114 let exact_selection = exact_selection.map(|(files, _)| (files, exact_sequence_range));
1115 let exact_denial_reason = if !request.exact_sequence_range {
1116 "not_requested"
1117 } else if request.skip_sst_files {
1118 "sst_files_skipped"
1119 } else if request.memtable_min_sequence.is_none() {
1120 "missing_lower_bound"
1121 } else if request.memtable_max_sequence.is_none() {
1122 "missing_upper_bound"
1123 } else if !version.options.preserve_row_sequence {
1124 "preserve_row_sequence_disabled"
1125 } else if extension_provider_blocks_exact {
1126 "extension_provider"
1127 } else if exact_sequence_range.is_none() {
1128 "selected_file_barrier_not_admitted"
1129 } else {
1130 "none"
1131 };
1132
1133 debug!(
1134 "Scan region {} exact sequence range: requested={}, min={:?}, max={:?}, committed={}, flushed={}, available={}, selected_files={}, denial_reason={}",
1135 region_id,
1136 request.exact_sequence_range,
1137 request.memtable_min_sequence,
1138 request.memtable_max_sequence,
1139 version_data.committed_sequence,
1140 version.flushed_sequence,
1141 exact_sequence_range.is_some(),
1142 exact_selection.as_ref().map_or(0, |(files, _)| files.len()),
1143 exact_denial_reason,
1144 );
1145 validate_sequence_fences(
1146 &request,
1147 &version,
1148 region_id,
1149 exact_sequence_range.is_some(),
1150 extension_provider_blocks_exact,
1151 )?;
1152
1153 let cache_manager = self.workers.cache_manager();
1155
1156 let scan_region = ScanRegion::new(
1157 version,
1158 region.access_layer.clone(),
1159 request,
1160 CacheStrategy::EnableAll(cache_manager),
1161 )
1162 .with_series_index(series_index)
1163 .with_ignore_range_index(!self.config.experimental_enable_range_index)
1164 .with_query_stat_counters(region.region_stats.query_stat_counters())
1165 .with_max_concurrent_scan_files(self.config.max_concurrent_scan_files)
1166 .with_scan_memory_pool(self.scan_memory_pool.clone())
1167 .with_experimental_series_scan_v2(self.config.experimental_series_scan_v2)
1168 .with_ignore_inverted_index(self.config.inverted_index.apply_on_query.disabled())
1169 .with_ignore_fulltext_index(self.config.fulltext_index.apply_on_query.disabled())
1170 .with_ignore_bloom_filter(self.config.bloom_filter_index.apply_on_query.disabled())
1171 .with_start_time(query_start);
1172 let scan_region = if let Some(selection) = exact_selection {
1173 scan_region.with_exact_selection(selection)
1174 } else {
1175 scan_region
1176 };
1177
1178 #[cfg(feature = "enterprise")]
1179 let scan_region = self.maybe_fill_extension_range_provider(scan_region, region);
1180
1181 Ok(scan_region)
1182 }
1183
1184 #[cfg(feature = "enterprise")]
1185 fn maybe_fill_extension_range_provider(
1186 &self,
1187 mut scan_region: ScanRegion,
1188 region: MitoRegionRef,
1189 ) -> ScanRegion {
1190 if region.is_follower()
1191 && let Some(factory) = self.extension_range_provider_factory.as_ref()
1192 {
1193 scan_region
1194 .set_extension_range_provider(factory.create_extension_range_provider(region));
1195 }
1196 scan_region
1197 }
1198
1199 fn set_region_role(&self, region_id: RegionId, role: RegionRole) -> Result<()> {
1201 let region = self.find_region(region_id)?;
1202 region.set_role(role);
1203 Ok(())
1204 }
1205
1206 async fn set_region_role_state_gracefully(
1208 &self,
1209 region_id: RegionId,
1210 region_role_state: SettableRegionRoleState,
1211 ) -> Result<SetRegionRoleStateResponse> {
1212 let (request, receiver) =
1215 WorkerRequest::new_set_readonly_gracefully(region_id, region_role_state);
1216 self.workers.submit_to_worker(region_id, request).await?;
1217
1218 receiver.await.context(RecvSnafu)
1219 }
1220
1221 async fn sync_region(
1222 &self,
1223 region_id: RegionId,
1224 manifest_info: RegionManifestInfo,
1225 ) -> Result<(ManifestVersion, bool)> {
1226 ensure!(manifest_info.is_mito(), MitoManifestInfoSnafu);
1227 let manifest_version = manifest_info.data_manifest_version();
1228 let (request, receiver) =
1229 WorkerRequest::new_sync_region_request(region_id, manifest_version);
1230 self.workers.submit_to_worker(region_id, request).await?;
1231
1232 receiver.await.context(RecvSnafu)?
1233 }
1234
1235 async fn remap_manifests(
1236 &self,
1237 request: RemapManifestsRequest,
1238 ) -> Result<RemapManifestsResponse> {
1239 let region_id = request.region_id;
1240 let (request, receiver) = WorkerRequest::try_from_remap_manifests_request(request)?;
1241 self.workers.submit_to_worker(region_id, request).await?;
1242 let manifest_paths = receiver.await.context(RecvSnafu)??;
1243 Ok(RemapManifestsResponse { manifest_paths })
1244 }
1245
1246 async fn copy_region_from(
1247 &self,
1248 region_id: RegionId,
1249 request: MitoCopyRegionFromRequest,
1250 ) -> Result<MitoCopyRegionFromResponse> {
1251 let (request, receiver) =
1252 WorkerRequest::try_from_copy_region_from_request(region_id, request)?;
1253 self.workers.submit_to_worker(region_id, request).await?;
1254 let response = receiver.await.context(RecvSnafu)??;
1255 Ok(response)
1256 }
1257
1258 fn role(&self, region_id: RegionId) -> Option<RegionRole> {
1259 self.workers
1260 .get_region(region_id)
1261 .map(|region| region.region_role())
1262 }
1263}
1264
1265fn validate_sequence_fences(
1266 request: &ScanRequest,
1267 version: &crate::region::version::Version,
1268 region_id: RegionId,
1269 exact_sequence_range_available: bool,
1270 extension_provider_blocks_exact: bool,
1271) -> Result<()> {
1272 if request.exact_sequence_range {
1273 if !exact_sequence_range_available {
1279 if let Some(given_seq) = request.memtable_min_sequence {
1280 let min_readable_seq = version.flushed_sequence;
1281 ensure!(
1282 given_seq >= min_readable_seq,
1283 IncrementalQueryStaleSnafu {
1284 region_id,
1285 given_seq,
1286 min_readable_seq,
1287 }
1288 );
1289 }
1290
1291 if let Some(given_seq) = request.memtable_max_sequence {
1292 let min_enforceable_seq = version.flushed_sequence;
1293 ensure!(
1294 given_seq >= min_enforceable_seq,
1295 SnapshotFenceStaleSnafu {
1296 region_id,
1297 given_seq,
1298 min_enforceable_seq,
1299 }
1300 );
1301 }
1302
1303 return SequenceRangeUnsupportedSnafu {
1309 region_id,
1310 min_seq: request.memtable_min_sequence.unwrap_or_default(),
1311 max_seq: request.memtable_max_sequence.unwrap_or_default(),
1312 reason: sequence_range_unsupported_reason(version, extension_provider_blocks_exact),
1313 }
1314 .fail();
1315 }
1316 } else {
1317 if let Some(given_seq) = request.memtable_min_sequence {
1321 let min_readable_seq = version.flushed_sequence;
1322 ensure!(
1323 given_seq >= min_readable_seq,
1324 IncrementalQueryStaleSnafu {
1325 region_id,
1326 given_seq,
1327 min_readable_seq,
1328 }
1329 );
1330 }
1331
1332 if let Some(given_seq) = request.memtable_max_sequence
1333 && !request.skip_sst_files
1334 {
1335 let min_enforceable_seq = version.flushed_sequence;
1342 ensure!(
1343 given_seq >= min_enforceable_seq,
1344 SnapshotFenceStaleSnafu {
1345 region_id,
1346 given_seq,
1347 min_enforceable_seq,
1348 }
1349 );
1350 }
1351 }
1352
1353 Ok(())
1354}
1355
1356fn sequence_range_unsupported_reason(
1357 version: &crate::region::version::Version,
1358 extension_provider_blocks_exact: bool,
1359) -> String {
1360 if extension_provider_blocks_exact {
1361 "region is a follower with an extension range provider, whose streams cannot be filtered by sequence".to_string()
1362 } else if version.options.preserve_row_sequence {
1363 "region has files without preserved per-row sequences".to_string()
1364 } else {
1365 "region does not preserve per-row sequences (preserve_row_sequence is off)".to_string()
1366 }
1367}
1368
1369fn map_batch_responses(responses: Vec<(RegionId, Result<AffectedRows>)>) -> BatchResponses {
1370 responses
1371 .into_iter()
1372 .map(|(region_id, response)| {
1373 (
1374 region_id,
1375 response.map(RegionResponse::new).map_err(BoxedError::new),
1376 )
1377 })
1378 .collect()
1379}
1380
1381#[async_trait]
1382impl RegionEngine for MitoEngine {
1383 fn name(&self) -> &str {
1384 MITO_ENGINE_NAME
1385 }
1386
1387 #[tracing::instrument(skip_all)]
1388 async fn handle_batch_open_requests(
1389 &self,
1390 parallelism: usize,
1391 requests: Vec<(RegionId, RegionOpenRequest)>,
1392 ) -> Result<BatchResponses, BoxedError> {
1393 self.inner
1395 .handle_batch_open_requests(parallelism, requests)
1396 .await
1397 .map(map_batch_responses)
1398 .map_err(BoxedError::new)
1399 }
1400
1401 #[tracing::instrument(skip_all)]
1402 async fn handle_batch_catchup_requests(
1403 &self,
1404 parallelism: usize,
1405 requests: Vec<(RegionId, RegionCatchupRequest)>,
1406 ) -> Result<BatchResponses, BoxedError> {
1407 self.inner
1408 .handle_batch_catchup_requests(parallelism, requests)
1409 .await
1410 .map(map_batch_responses)
1411 .map_err(BoxedError::new)
1412 }
1413
1414 #[tracing::instrument(skip_all)]
1415 async fn handle_request(
1416 &self,
1417 region_id: RegionId,
1418 request: RegionRequest,
1419 ) -> Result<RegionResponse, BoxedError> {
1420 let _timer = HANDLE_REQUEST_ELAPSED
1421 .with_label_values(&[request.request_type()])
1422 .start_timer();
1423
1424 let is_alter = matches!(request, RegionRequest::Alter(_));
1425 let is_create = matches!(request, RegionRequest::Create(_));
1426 let mut response = self
1427 .inner
1428 .handle_request(region_id, request)
1429 .await
1430 .map(RegionResponse::new)
1431 .map_err(BoxedError::new)?;
1432
1433 if is_alter {
1434 self.handle_alter_response(region_id, &mut response)
1435 .map_err(BoxedError::new)?;
1436 } else if is_create {
1437 self.handle_create_response(region_id, &mut response)
1438 .map_err(BoxedError::new)?;
1439 }
1440
1441 Ok(response)
1442 }
1443
1444 #[tracing::instrument(skip_all)]
1445 async fn handle_query(
1446 &self,
1447 region_id: RegionId,
1448 request: ScanRequest,
1449 ) -> Result<RegionScannerRef, BoxedError> {
1450 self.scan_region(region_id, request)
1451 .map_err(BoxedError::new)?
1452 .region_scanner()
1453 .await
1454 .map_err(BoxedError::new)
1455 }
1456
1457 fn query_memory_tracker(&self) -> Option<QueryMemoryTracker> {
1458 Some(self.inner.scan_memory_tracker.clone())
1459 }
1460
1461 async fn get_committed_sequence(
1462 &self,
1463 region_id: RegionId,
1464 ) -> Result<SequenceNumber, BoxedError> {
1465 self.inner
1466 .get_committed_sequence(region_id)
1467 .map_err(BoxedError::new)
1468 }
1469
1470 async fn get_metadata(
1472 &self,
1473 region_id: RegionId,
1474 ) -> std::result::Result<RegionMetadataRef, BoxedError> {
1475 self.inner.get_metadata(region_id).map_err(BoxedError::new)
1476 }
1477
1478 async fn stop(&self) -> std::result::Result<(), BoxedError> {
1484 self.inner.stop().await.map_err(BoxedError::new)
1485 }
1486
1487 fn region_statistic(&self, region_id: RegionId) -> Option<RegionStatistic> {
1488 self.get_region_statistic(region_id)
1489 }
1490
1491 fn set_region_role(&self, region_id: RegionId, role: RegionRole) -> Result<(), BoxedError> {
1492 self.inner
1493 .set_region_role(region_id, role)
1494 .map_err(BoxedError::new)
1495 }
1496
1497 async fn set_region_role_state_gracefully(
1498 &self,
1499 region_id: RegionId,
1500 region_role_state: SettableRegionRoleState,
1501 ) -> Result<SetRegionRoleStateResponse, BoxedError> {
1502 let _timer = HANDLE_REQUEST_ELAPSED
1503 .with_label_values(&["set_region_role_state_gracefully"])
1504 .start_timer();
1505
1506 self.inner
1507 .set_region_role_state_gracefully(region_id, region_role_state)
1508 .await
1509 .map_err(BoxedError::new)
1510 }
1511
1512 async fn sync_region(
1513 &self,
1514 region_id: RegionId,
1515 request: SyncRegionFromRequest,
1516 ) -> Result<SyncRegionFromResponse, BoxedError> {
1517 let manifest_info = request
1518 .into_region_manifest_info()
1519 .context(UnexpectedSnafu {
1520 err_msg: "Expected a manifest info request",
1521 })
1522 .map_err(BoxedError::new)?;
1523 let (_, synced) = self
1524 .inner
1525 .sync_region(region_id, manifest_info)
1526 .await
1527 .map_err(BoxedError::new)?;
1528
1529 Ok(SyncRegionFromResponse::Mito { synced })
1530 }
1531
1532 async fn remap_manifests(
1533 &self,
1534 request: RemapManifestsRequest,
1535 ) -> Result<RemapManifestsResponse, BoxedError> {
1536 self.inner
1537 .remap_manifests(request)
1538 .await
1539 .map_err(BoxedError::new)
1540 }
1541
1542 fn role(&self, region_id: RegionId) -> Option<RegionRole> {
1543 self.inner.role(region_id)
1544 }
1545
1546 fn as_any(&self) -> &dyn Any {
1547 self
1548 }
1549}
1550
1551impl MitoEngine {
1552 fn handle_alter_response(
1553 &self,
1554 region_id: RegionId,
1555 response: &mut RegionResponse,
1556 ) -> Result<()> {
1557 if let Some(statistic) = self.region_statistic(region_id) {
1558 Self::encode_manifest_info_to_extensions(
1559 ®ion_id,
1560 statistic.manifest,
1561 &mut response.extensions,
1562 )?;
1563 }
1564 let column_metadatas = self
1565 .inner
1566 .find_region(region_id)
1567 .ok()
1568 .map(|r| r.metadata().column_metadatas.clone());
1569 if let Some(column_metadatas) = column_metadatas {
1570 Self::encode_column_metadatas_to_extensions(
1571 ®ion_id,
1572 column_metadatas,
1573 &mut response.extensions,
1574 )?;
1575 }
1576 Ok(())
1577 }
1578
1579 fn handle_create_response(
1580 &self,
1581 region_id: RegionId,
1582 response: &mut RegionResponse,
1583 ) -> Result<()> {
1584 let column_metadatas = self
1585 .inner
1586 .find_region(region_id)
1587 .ok()
1588 .map(|r| r.metadata().column_metadatas.clone());
1589 if let Some(column_metadatas) = column_metadatas {
1590 Self::encode_column_metadatas_to_extensions(
1591 ®ion_id,
1592 column_metadatas,
1593 &mut response.extensions,
1594 )?;
1595 }
1596 Ok(())
1597 }
1598}
1599
1600#[cfg(any(test, feature = "test"))]
1602#[allow(clippy::too_many_arguments)]
1603impl MitoEngine {
1604 pub async fn new_for_test<S: LogStore>(
1606 data_home: &str,
1607 mut config: MitoConfig,
1608 log_store: Arc<S>,
1609 object_store_manager: ObjectStoreManagerRef,
1610 write_buffer_manager: Option<crate::flush::WriteBufferManagerRef>,
1611 listener: Option<crate::engine::listener::EventListenerRef>,
1612 time_provider: crate::time_provider::TimeProviderRef,
1613 schema_metadata_manager: SchemaMetadataManagerRef,
1614 file_ref_manager: FileReferenceManagerRef,
1615 partition_expr_fetcher: PartitionExprFetcherRef,
1616 ) -> Result<MitoEngine> {
1617 config.sanitize(data_home)?;
1618
1619 let config = Arc::new(config);
1620 let wal_raw_entry_reader = Arc::new(LogStoreRawEntryReader::new(log_store.clone()));
1621 let total_memory = get_total_memory_bytes().max(0) as u64;
1622 let scan_memory_limit = config.scan_memory_limit.resolve(total_memory) as usize;
1623 let scan_memory_pool = new_scan_memory_pool(scan_memory_limit);
1624 let scan_memory_tracker =
1625 QueryMemoryTracker::builder(scan_memory_limit, config.scan_memory_on_exhausted)
1626 .on_update(|usage| {
1627 SCAN_MEMORY_USAGE_BYTES.set(usage as i64);
1628 })
1629 .on_exhausted(|| {
1630 SCAN_MEMORY_EXHAUSTED_TOTAL.inc();
1631 })
1632 .on_reject(|| {
1633 SCAN_REQUESTS_REJECTED_TOTAL.inc();
1634 })
1635 .build();
1636 Ok(MitoEngine {
1637 inner: Arc::new(EngineInner {
1638 workers: WorkerGroup::start_for_test(
1639 data_home,
1640 config.clone(),
1641 log_store,
1642 object_store_manager,
1643 write_buffer_manager,
1644 listener,
1645 schema_metadata_manager,
1646 file_ref_manager,
1647 time_provider,
1648 partition_expr_fetcher,
1649 )
1650 .await?,
1651 config,
1652 wal_raw_entry_reader,
1653 scan_memory_tracker,
1654 scan_memory_pool,
1655 region_hook: None,
1656 #[cfg(feature = "enterprise")]
1657 extension_range_provider_factory: None,
1658 }),
1659 })
1660 }
1661
1662 pub fn purge_scheduler(&self) -> &crate::schedule::scheduler::SchedulerRef {
1664 self.inner.workers.purge_scheduler()
1665 }
1666}
1667
1668fn new_scan_memory_pool(scan_memory_limit: usize) -> Arc<dyn MemoryPool> {
1669 if scan_memory_limit == 0 {
1670 Arc::new(UnboundedMemoryPool::default())
1671 } else {
1672 Arc::new(GreedyMemoryPool::new(scan_memory_limit))
1673 }
1674}
1675
1676#[cfg(test)]
1677mod tests {
1678 use std::time::Duration;
1679
1680 use datafusion::execution::memory_pool::MemoryConsumer;
1681
1682 use super::*;
1683 use crate::sst::file::FileMeta;
1684
1685 #[test]
1686 fn test_is_valid_region_edit() {
1687 let edit = RegionEdit {
1689 files_to_add: vec![FileMeta::default()],
1690 files_to_remove: vec![],
1691 timestamp_ms: None,
1692 compaction_time_window: None,
1693 flushed_entry_id: None,
1694 flushed_sequence: None,
1695 committed_sequence: None,
1696 };
1697 assert!(is_valid_region_edit(&edit));
1698
1699 let edit = RegionEdit {
1701 files_to_add: vec![],
1702 files_to_remove: vec![],
1703 timestamp_ms: None,
1704 compaction_time_window: None,
1705 flushed_entry_id: None,
1706 flushed_sequence: None,
1707 committed_sequence: None,
1708 };
1709 assert!(!is_valid_region_edit(&edit));
1710
1711 let edit = RegionEdit {
1713 files_to_add: vec![],
1714 files_to_remove: vec![FileMeta::default()],
1715 timestamp_ms: None,
1716 compaction_time_window: None,
1717 flushed_entry_id: None,
1718 flushed_sequence: None,
1719 committed_sequence: None,
1720 };
1721 assert!(is_valid_region_edit(&edit));
1722
1723 let edit = RegionEdit {
1725 files_to_add: vec![FileMeta::default()],
1726 files_to_remove: vec![FileMeta::default()],
1727 timestamp_ms: None,
1728 compaction_time_window: None,
1729 flushed_entry_id: None,
1730 flushed_sequence: None,
1731 committed_sequence: None,
1732 };
1733 assert!(is_valid_region_edit(&edit));
1734
1735 let edit = RegionEdit {
1737 files_to_add: vec![FileMeta::default()],
1738 files_to_remove: vec![],
1739 timestamp_ms: None,
1740 compaction_time_window: Some(Duration::from_secs(1)),
1741 flushed_entry_id: None,
1742 flushed_sequence: None,
1743 committed_sequence: None,
1744 };
1745 assert!(!is_valid_region_edit(&edit));
1746 let edit = RegionEdit {
1747 files_to_add: vec![FileMeta::default()],
1748 files_to_remove: vec![],
1749 timestamp_ms: None,
1750 compaction_time_window: None,
1751 flushed_entry_id: Some(1),
1752 flushed_sequence: None,
1753 committed_sequence: None,
1754 };
1755 assert!(!is_valid_region_edit(&edit));
1756 let edit = RegionEdit {
1757 files_to_add: vec![FileMeta::default()],
1758 files_to_remove: vec![],
1759 timestamp_ms: None,
1760 compaction_time_window: None,
1761 flushed_entry_id: None,
1762 flushed_sequence: Some(1),
1763 committed_sequence: None,
1764 };
1765 assert!(!is_valid_region_edit(&edit));
1766 }
1767
1768 #[test]
1769 fn test_scan_memory_pool_is_shared_across_consumers() {
1770 let pool = new_scan_memory_pool(100);
1771 let cloned_pool = pool.clone();
1772 let first = MemoryConsumer::new("first-scan").register(&pool);
1773 let second = MemoryConsumer::new("second-scan").register(&cloned_pool);
1774
1775 first.try_grow(60).unwrap();
1776 assert!(second.try_grow(50).is_err());
1777 second.try_grow(40).unwrap();
1778 assert_eq!(100, pool.reserved());
1779 }
1780}