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