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 futures::FutureExt;
112use futures::future::{self, 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 BatchPutResponse, BatchResponses, MitoCopyRegionFromRequest, MitoCopyRegionFromResponse,
127 RegionEngine, RegionManifestInfo, RegionRole, RegionScannerRef, RegionStatistic,
128 RemapManifestsRequest, RemapManifestsResponse, SetRegionRoleStateResponse,
129 SettableRegionRoleState, SyncRegionFromRequest, SyncRegionFromResponse,
130};
131use store_api::region_info::RegionInfoEntry;
132use store_api::region_request::{
133 AffectedRows, RegionCatchupRequest, RegionOpenRequest, RegionPutRequest, 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, ScanMemoryMode};
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_tracker = QueryMemoryTracker::builder(scan_memory_limit)
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 region_hook,
265 #[cfg(feature = "enterprise")]
266 extension_range_provider_factory: None,
267 };
268
269 #[cfg(feature = "enterprise")]
270 let inner =
271 inner.with_extension_range_provider_factory(self.extension_range_provider_factory);
272
273 Ok(MitoEngine {
274 inner: Arc::new(inner),
275 })
276 }
277}
278
279#[derive(Clone)]
281pub struct MitoEngine {
282 inner: Arc<EngineInner>,
283}
284
285impl MitoEngine {
286 #[allow(clippy::too_many_arguments)]
288 pub async fn new<S: LogStore>(
289 data_home: &str,
290 config: MitoConfig,
291 log_store: Arc<S>,
292 object_store_manager: ObjectStoreManagerRef,
293 schema_metadata_manager: SchemaMetadataManagerRef,
294 file_ref_manager: FileReferenceManagerRef,
295 partition_expr_fetcher: PartitionExprFetcherRef,
296 plugins: Plugins,
297 ) -> Result<MitoEngine> {
298 let builder = MitoEngineBuilder::new(
299 data_home,
300 config,
301 log_store,
302 object_store_manager,
303 schema_metadata_manager,
304 file_ref_manager,
305 partition_expr_fetcher,
306 plugins,
307 );
308 builder.try_build().await
309 }
310
311 pub fn mito_config(&self) -> &MitoConfig {
312 &self.inner.config
313 }
314
315 pub fn cache_manager(&self) -> CacheManagerRef {
316 self.inner.workers.cache_manager()
317 }
318
319 pub fn file_ref_manager(&self) -> FileReferenceManagerRef {
320 self.inner.workers.file_ref_manager()
321 }
322
323 pub fn gc_limiter(&self) -> GcLimiterRef {
324 self.inner.workers.gc_limiter()
325 }
326
327 pub fn object_store_manager(&self) -> &ObjectStoreManagerRef {
328 self.inner.workers.object_store_manager()
329 }
330
331 pub fn puffin_manager_factory(&self) -> &PuffinManagerFactory {
332 self.inner.workers.puffin_manager_factory()
333 }
334
335 pub fn intermediate_manager(&self) -> &IntermediateManager {
336 self.inner.workers.intermediate_manager()
337 }
338
339 pub fn schema_metadata_manager(&self) -> &SchemaMetadataManagerRef {
340 self.inner.workers.schema_metadata_manager()
341 }
342
343 pub fn region_hook(&self) -> Option<RegionHookRef> {
346 self.inner.region_hook.clone()
347 }
348
349 pub async fn get_snapshot_of_file_refs(
351 &self,
352 file_handle_regions: impl IntoIterator<Item = RegionId>,
353 related_regions: HashMap<RegionId, HashSet<RegionId>>,
354 ) -> Result<FileRefsManifest> {
355 let file_ref_mgr = self.file_ref_manager();
356
357 let file_handle_regions = file_handle_regions.into_iter().collect::<Vec<_>>();
358 let query_regions: Vec<MitoRegionRef> = file_handle_regions
361 .into_iter()
362 .filter_map(|region_id| self.find_region(region_id))
363 .collect();
364
365 let dst_region_to_src_regions: Vec<(MitoRegionRef, HashSet<RegionId>)> = {
366 let dst2src = related_regions
367 .into_iter()
368 .flat_map(|(src, dsts)| dsts.into_iter().map(move |dst| (dst, src)))
369 .fold(
370 HashMap::<RegionId, HashSet<RegionId>>::new(),
371 |mut acc, (k, v)| {
372 let entry = acc.entry(k).or_default();
373 entry.insert(v);
374 acc
375 },
376 );
377 let mut dst_region_to_src_regions = Vec::with_capacity(dst2src.len());
378 for (dst_region, srcs) in dst2src {
379 let Some(region) = self.find_region(dst_region) else {
380 return RegionNotFoundSnafu {
381 region_id: dst_region,
382 }
383 .fail();
384 };
385 dst_region_to_src_regions.push((region, srcs));
386 }
387 dst_region_to_src_regions
388 };
389
390 file_ref_mgr
391 .get_snapshot_of_file_refs(query_regions, dst_region_to_src_regions)
392 .await
393 }
394
395 pub fn is_region_exists(&self, region_id: RegionId) -> bool {
397 self.inner.workers.is_region_exists(region_id)
398 }
399
400 pub fn is_region_opening(&self, region_id: RegionId) -> bool {
402 self.inner.workers.is_region_opening(region_id)
403 }
404
405 pub fn is_region_catching_up(&self, region_id: RegionId) -> bool {
407 self.inner.workers.is_region_catching_up(region_id)
408 }
409
410 pub fn get_region_statistic(&self, region_id: RegionId) -> Option<RegionStatistic> {
412 self.find_region(region_id)
413 .map(|region| region.region_statistic())
414 }
415
416 pub fn get_primary_key_encoding(&self, region_id: RegionId) -> Option<PrimaryKeyEncoding> {
418 self.find_region(region_id)
419 .map(|r| r.primary_key_encoding())
420 }
421
422 #[tracing::instrument(skip_all)]
427 pub async fn scan_to_stream(
428 &self,
429 region_id: RegionId,
430 request: ScanRequest,
431 ) -> Result<SendableRecordBatchStream, BoxedError> {
432 self.scanner(region_id, request)
433 .await
434 .map_err(BoxedError::new)?
435 .scan()
436 .await
437 }
438
439 pub async fn scan_batch(
441 &self,
442 region_id: RegionId,
443 request: ScanRequest,
444 filter_deleted: bool,
445 ) -> Result<ScanBatchStream> {
446 let mut scan_region = self.scan_region(region_id, request)?;
447 scan_region.set_filter_deleted(filter_deleted);
448 scan_region.scanner().await?.scan_batch()
449 }
450
451 pub(crate) async fn scanner(
453 &self,
454 region_id: RegionId,
455 request: ScanRequest,
456 ) -> Result<Scanner> {
457 self.scan_region(region_id, request)?.scanner().await
458 }
459
460 #[tracing::instrument(skip_all, fields(region_id = %region_id))]
462 fn scan_region(&self, region_id: RegionId, request: ScanRequest) -> Result<ScanRegion> {
463 self.inner.scan_region(region_id, request)
464 }
465
466 pub async fn edit_region(&self, region_id: RegionId, edit: RegionEdit) -> Result<()> {
471 let _timer = HANDLE_REQUEST_ELAPSED
472 .with_label_values(&["edit_region"])
473 .start_timer();
474
475 ensure!(
476 is_valid_region_edit(&edit),
477 InvalidRequestSnafu {
478 region_id,
479 reason: "invalid region edit"
480 }
481 );
482
483 let (tx, rx) = oneshot::channel();
484 let request = WorkerRequest::EditRegion(RegionEditRequest::new(region_id, edit, true, tx));
485 self.inner
486 .workers
487 .submit_to_worker(region_id, request)
488 .await?;
489 rx.await.context(RecvSnafu)?
490 }
491
492 pub async fn copy_region_from(
496 &self,
497 region_id: RegionId,
498 request: MitoCopyRegionFromRequest,
499 ) -> Result<MitoCopyRegionFromResponse> {
500 self.inner.copy_region_from(region_id, request).await
501 }
502
503 #[cfg(test)]
504 pub(crate) fn get_region(&self, id: RegionId) -> Option<crate::region::MitoRegionRef> {
505 self.find_region(id)
506 }
507
508 pub fn find_region(&self, region_id: RegionId) -> Option<MitoRegionRef> {
509 self.inner.workers.get_region(region_id)
510 }
511
512 pub fn regions(&self) -> Vec<MitoRegionRef> {
514 self.inner.workers.all_regions().collect()
515 }
516
517 fn encode_manifest_info_to_extensions(
518 region_id: &RegionId,
519 manifest_info: RegionManifestInfo,
520 extensions: &mut HashMap<String, Vec<u8>>,
521 ) -> Result<()> {
522 let region_manifest_info = vec![(*region_id, manifest_info)];
523
524 extensions.insert(
525 MANIFEST_INFO_EXTENSION_KEY.to_string(),
526 RegionManifestInfo::encode_list(®ion_manifest_info).context(SerdeJsonSnafu)?,
527 );
528 info!(
529 "Added manifest info: {:?} to extensions, region_id: {:?}",
530 region_manifest_info, region_id
531 );
532 Ok(())
533 }
534
535 fn encode_column_metadatas_to_extensions(
536 region_id: &RegionId,
537 column_metadatas: Vec<ColumnMetadata>,
538 extensions: &mut HashMap<String, Vec<u8>>,
539 ) -> Result<()> {
540 extensions.insert(
541 TABLE_COLUMN_METADATA_EXTENSION_KEY.to_string(),
542 ColumnMetadata::encode_list(&column_metadatas).context(SerializeColumnMetadataSnafu)?,
543 );
544 info!(
545 "Added column metadatas: {:?} to extensions, region_id: {:?}",
546 column_metadatas, region_id
547 );
548 Ok(())
549 }
550
551 pub fn find_memtable_and_sst_stats(
554 &self,
555 region_id: RegionId,
556 ) -> Result<(Vec<MemtableStats>, Vec<FileMeta>)> {
557 let region = self
558 .find_region(region_id)
559 .context(RegionNotFoundSnafu { region_id })?;
560
561 let version = region.version();
562 let memtable_stats = version
563 .memtables
564 .list_memtables()
565 .iter()
566 .map(|x| x.stats())
567 .collect::<Vec<_>>();
568
569 let sst_stats = version
570 .ssts
571 .levels()
572 .iter()
573 .flat_map(|level| level.files().map(|x| x.meta_ref()))
574 .cloned()
575 .collect::<Vec<_>>();
576 Ok((memtable_stats, sst_stats))
577 }
578
579 pub async fn all_ssts_from_manifest(&self) -> Vec<ManifestSstEntry> {
581 let node_id = self.inner.workers.file_ref_manager().node_id();
582 let regions = self.inner.workers.all_regions();
583
584 let mut results = Vec::new();
585 for region in regions {
586 let mut entries = region.manifest_sst_entries().await;
587 for e in &mut entries {
588 e.node_id = node_id;
589 }
590 results.extend(entries);
591 }
592
593 results
594 }
595
596 pub async fn all_index_metas(&self) -> Vec<PuffinIndexMetaEntry> {
598 let node_id = self.inner.workers.file_ref_manager().node_id();
599 let cache_manager = self.inner.workers.cache_manager();
600 let puffin_metadata_cache = cache_manager.puffin_metadata_cache().cloned();
601 let bloom_filter_cache = cache_manager.bloom_filter_index_cache().cloned();
602 let inverted_index_cache = cache_manager.inverted_index_cache().cloned();
603
604 let mut results = Vec::new();
605
606 for region in self.inner.workers.all_regions() {
607 let manifest_entries = region.manifest_sst_entries().await;
608 let access_layer = region.access_layer.clone();
609 let table_dir = access_layer.table_dir().to_string();
610 let path_type = access_layer.path_type();
611 let object_store = access_layer.object_store().clone();
612 let puffin_factory = access_layer.puffin_manager_factory().clone();
613 let path_factory = RegionFilePathFactory::new(table_dir, path_type);
614
615 let entry_futures = manifest_entries.into_iter().map(|entry| {
616 let object_store = object_store.clone();
617 let path_factory = path_factory.clone();
618 let puffin_factory = puffin_factory.clone();
619 let puffin_metadata_cache = puffin_metadata_cache.clone();
620 let bloom_filter_cache = bloom_filter_cache.clone();
621 let inverted_index_cache = inverted_index_cache.clone();
622
623 async move {
624 let Some(index_file_path) = entry.index_file_path.as_ref() else {
625 return Vec::new();
626 };
627
628 let index_version = entry.index_version;
629 let file_id = match FileId::parse_str(&entry.file_id) {
630 Ok(file_id) => file_id,
631 Err(err) => {
632 warn!(
633 err;
634 "Failed to parse puffin index file id, table_dir: {}, file_id: {}",
635 entry.table_dir,
636 entry.file_id
637 );
638 return Vec::new();
639 }
640 };
641 let region_index_id = RegionIndexId::new(
644 RegionFileId::new(entry.origin_region_id, file_id),
645 index_version,
646 );
647 let context = IndexEntryContext {
648 table_dir: &entry.table_dir,
649 index_file_path: index_file_path.as_str(),
650 region_id: entry.region_id,
651 table_id: entry.table_id,
652 region_number: entry.region_number,
653 region_group: entry.region_group,
654 region_sequence: entry.region_sequence,
655 file_id: &entry.file_id,
656 index_file_size: entry.index_file_size,
657 node_id,
658 };
659
660 let manager = puffin_factory
661 .build(object_store, path_factory)
662 .with_puffin_metadata_cache(puffin_metadata_cache);
663
664 collect_index_entries_from_puffin(
665 manager,
666 region_index_id,
667 context,
668 bloom_filter_cache,
669 inverted_index_cache,
670 )
671 .await
672 }
673 });
674
675 let mut meta_stream = stream::iter(entry_futures).buffer_unordered(8); while let Some(mut metas) = meta_stream.next().await {
677 results.append(&mut metas);
678 }
679 }
680
681 results
682 }
683
684 pub async fn all_region_infos(&self) -> Vec<RegionInfoEntry> {
686 let node_id = self.inner.workers.file_ref_manager().node_id();
687 self.inner
688 .workers
689 .all_regions()
690 .map(|region| region.region_info_entry(node_id))
691 .collect()
692 }
693
694 pub fn all_ssts_from_storage(&self) -> impl Stream<Item = Result<StorageSstEntry>> {
696 let node_id = self.inner.workers.file_ref_manager().node_id();
697 let regions = self.inner.workers.all_regions();
698
699 let mut layers_distinct_table_dirs = HashMap::new();
700 for region in regions {
701 let table_dir = region.access_layer.table_dir();
702 if !layers_distinct_table_dirs.contains_key(table_dir) {
703 layers_distinct_table_dirs
704 .insert(table_dir.to_string(), region.access_layer.clone());
705 }
706 }
707
708 stream::iter(layers_distinct_table_dirs)
709 .map(|(_, access_layer)| access_layer.storage_sst_entries())
710 .flatten()
711 .map(move |entry| {
712 entry.map(move |mut entry| {
713 entry.node_id = node_id;
714 entry
715 })
716 })
717 }
718}
719
720fn is_valid_region_edit(edit: &RegionEdit) -> bool {
724 (!edit.files_to_add.is_empty() || !edit.files_to_remove.is_empty())
725 && matches!(
726 edit,
727 RegionEdit {
728 files_to_add: _,
729 files_to_remove: _,
730 timestamp_ms: _,
731 compaction_time_window: None,
732 flushed_entry_id: None,
733 flushed_sequence: None,
734 ..
735 }
736 )
737}
738
739struct EngineInner {
741 workers: WorkerGroup,
743 config: Arc<MitoConfig>,
745 wal_raw_entry_reader: Arc<dyn RawEntryReader>,
747 scan_memory_tracker: QueryMemoryTracker,
749 region_hook: Option<RegionHookRef>,
752 #[cfg(feature = "enterprise")]
753 extension_range_provider_factory: Option<BoxedExtensionRangeProviderFactory>,
754}
755
756type TopicGroupedRegionOpenRequests = HashMap<String, Vec<(RegionId, RegionOpenRequest)>>;
757
758fn prepare_batch_open_requests(
760 requests: Vec<(RegionId, RegionOpenRequest)>,
761) -> Result<(
762 TopicGroupedRegionOpenRequests,
763 Vec<(RegionId, RegionOpenRequest)>,
764)> {
765 let mut topic_to_regions: HashMap<String, Vec<(RegionId, RegionOpenRequest)>> = HashMap::new();
766 let mut remaining_regions: Vec<(RegionId, RegionOpenRequest)> = Vec::new();
767 for (region_id, request) in requests {
768 match parse_wal_options(&request.options).context(SerdeJsonSnafu)? {
769 WalOptions::Kafka(options) => {
770 topic_to_regions
771 .entry(options.topic)
772 .or_default()
773 .push((region_id, request));
774 }
775 WalOptions::RaftEngine | WalOptions::Noop | WalOptions::ObjectStore(_) => {
776 remaining_regions.push((region_id, request));
777 }
778 }
779 }
780
781 Ok((topic_to_regions, remaining_regions))
782}
783
784impl EngineInner {
785 #[cfg(feature = "enterprise")]
786 #[must_use]
787 fn with_extension_range_provider_factory(
788 self,
789 extension_range_provider_factory: Option<BoxedExtensionRangeProviderFactory>,
790 ) -> Self {
791 Self {
792 extension_range_provider_factory,
793 ..self
794 }
795 }
796
797 async fn stop(&self) -> Result<()> {
799 self.workers.stop().await
800 }
801
802 fn find_region(&self, region_id: RegionId) -> Result<MitoRegionRef> {
803 self.workers
804 .get_region(region_id)
805 .context(RegionNotFoundSnafu { region_id })
806 }
807
808 fn get_metadata(&self, region_id: RegionId) -> Result<RegionMetadataRef> {
812 let region = self.find_region(region_id)?;
814 Ok(region.metadata())
815 }
816
817 async fn open_topic_regions(
818 &self,
819 topic: String,
820 region_requests: Vec<(RegionId, RegionOpenRequest)>,
821 ) -> Result<Vec<(RegionId, Result<AffectedRows>)>> {
822 let now = Instant::now();
823 let region_ids = region_requests
824 .iter()
825 .map(|(region_id, _)| *region_id)
826 .collect::<Vec<_>>();
827 let provider = Provider::kafka_provider(topic);
828 let (distributor, entry_receivers) = build_wal_entry_distributor_and_receivers(
829 provider.clone(),
830 self.wal_raw_entry_reader.clone(),
831 ®ion_ids,
832 DEFAULT_ENTRY_RECEIVER_BUFFER_SIZE,
833 );
834
835 let mut responses = Vec::with_capacity(region_requests.len());
836 for ((region_id, request), entry_receiver) in
837 region_requests.into_iter().zip(entry_receivers)
838 {
839 let (request, receiver) =
840 WorkerRequest::new_open_region_request(region_id, request, Some(entry_receiver));
841 self.workers.submit_to_worker(region_id, request).await?;
842 responses.push(async move { receiver.await.context(RecvSnafu)? });
843 }
844
845 let distribution =
847 common_runtime::spawn_global(async move { distributor.distribute().await });
848 let responses = join_all(responses).await;
850 distribution.await.context(JoinSnafu)??;
851
852 let num_failure = responses.iter().filter(|r| r.is_err()).count();
853 info!(
854 "Opened {} regions for topic '{}', failures: {}, elapsed: {:?}",
855 region_ids.len() - num_failure,
856 provider.as_kafka_provider().unwrap(),
858 num_failure,
859 now.elapsed(),
860 );
861 Ok(region_ids.into_iter().zip(responses).collect())
862 }
863
864 async fn handle_batch_open_requests(
865 &self,
866 parallelism: usize,
867 requests: Vec<(RegionId, RegionOpenRequest)>,
868 ) -> Result<Vec<(RegionId, Result<AffectedRows>)>> {
869 let semaphore = Arc::new(Semaphore::new(parallelism));
870 let (topic_to_region_requests, remaining_region_requests) =
871 prepare_batch_open_requests(requests)?;
872 let mut responses =
873 Vec::with_capacity(topic_to_region_requests.len() + remaining_region_requests.len());
874
875 if !topic_to_region_requests.is_empty() {
876 let mut tasks = Vec::with_capacity(topic_to_region_requests.len());
877 for (topic, region_requests) in topic_to_region_requests {
878 let semaphore_moved = semaphore.clone();
879 tasks.push(async move {
880 let _permit = semaphore_moved.acquire().await.unwrap();
882 self.open_topic_regions(topic, region_requests).await
883 })
884 }
885 let r = try_join_all(tasks).await?;
886 responses.extend(r.into_iter().flatten());
887 }
888
889 if !remaining_region_requests.is_empty() {
890 let mut tasks = Vec::with_capacity(remaining_region_requests.len());
891 let mut region_ids = Vec::with_capacity(remaining_region_requests.len());
892 for (region_id, request) in remaining_region_requests {
893 let semaphore_moved = semaphore.clone();
894 region_ids.push(region_id);
895 tasks.push(async move {
896 let _permit = semaphore_moved.acquire().await.unwrap();
898 let (request, receiver) =
899 WorkerRequest::new_open_region_request(region_id, request, None);
900
901 self.workers.submit_to_worker(region_id, request).await?;
902
903 receiver.await.context(RecvSnafu)?
904 })
905 }
906
907 let results = join_all(tasks).await;
908 responses.extend(region_ids.into_iter().zip(results));
909 }
910
911 Ok(responses)
912 }
913
914 async fn catchup_topic_regions(
915 &self,
916 provider: Provider,
917 region_requests: Vec<(RegionId, RegionCatchupRequest)>,
918 ) -> Result<Vec<(RegionId, Result<AffectedRows>)>> {
919 let now = Instant::now();
920 let region_ids = region_requests
921 .iter()
922 .map(|(region_id, _)| *region_id)
923 .collect::<Vec<_>>();
924 let (distributor, entry_receivers) = build_wal_entry_distributor_and_receivers(
925 provider.clone(),
926 self.wal_raw_entry_reader.clone(),
927 ®ion_ids,
928 DEFAULT_ENTRY_RECEIVER_BUFFER_SIZE,
929 );
930
931 let mut responses = Vec::with_capacity(region_requests.len());
932 for ((region_id, request), entry_receiver) in
933 region_requests.into_iter().zip(entry_receivers)
934 {
935 let (request, receiver) =
936 WorkerRequest::new_catchup_region_request(region_id, request, Some(entry_receiver));
937 self.workers.submit_to_worker(region_id, request).await?;
938 responses.push(async move { receiver.await.context(RecvSnafu)? });
939 }
940
941 let distribution =
943 common_runtime::spawn_global(async move { distributor.distribute().await });
944 let responses = join_all(responses).await;
946 distribution.await.context(JoinSnafu)??;
947
948 let num_failure = responses.iter().filter(|r| r.is_err()).count();
949 info!(
950 "Caught up {} regions for topic '{}', failures: {}, elapsed: {:?}",
951 region_ids.len() - num_failure,
952 provider.as_kafka_provider().unwrap(),
954 num_failure,
955 now.elapsed(),
956 );
957
958 Ok(region_ids.into_iter().zip(responses).collect())
959 }
960
961 async fn handle_batch_catchup_requests(
962 &self,
963 parallelism: usize,
964 requests: Vec<(RegionId, RegionCatchupRequest)>,
965 ) -> Result<Vec<(RegionId, Result<AffectedRows>)>> {
966 let mut responses = Vec::with_capacity(requests.len());
967 let mut topic_regions: HashMap<Arc<KafkaProvider>, Vec<_>> = HashMap::new();
968 let mut remaining_region_requests = vec![];
969
970 for (region_id, request) in requests {
971 match self.workers.get_region(region_id) {
972 Some(region) => match region.provider.as_kafka_provider() {
973 Some(provider) => {
974 topic_regions
975 .entry(provider.clone())
976 .or_default()
977 .push((region_id, request));
978 }
979 None => {
980 remaining_region_requests.push((region_id, request));
981 }
982 },
983 None => responses.push((region_id, RegionNotFoundSnafu { region_id }.fail())),
984 }
985 }
986
987 let semaphore = Arc::new(Semaphore::new(parallelism));
988
989 if !topic_regions.is_empty() {
990 let mut tasks = Vec::with_capacity(topic_regions.len());
991 for (provider, region_requests) in topic_regions {
992 let semaphore_moved = semaphore.clone();
993 tasks.push(async move {
994 let _permit = semaphore_moved.acquire().await.unwrap();
996 self.catchup_topic_regions(Provider::Kafka(provider), region_requests)
997 .await
998 })
999 }
1000
1001 let r = try_join_all(tasks).await?;
1002 responses.extend(r.into_iter().flatten());
1003 }
1004
1005 if !remaining_region_requests.is_empty() {
1006 let mut tasks = Vec::with_capacity(remaining_region_requests.len());
1007 let mut region_ids = Vec::with_capacity(remaining_region_requests.len());
1008 for (region_id, request) in remaining_region_requests {
1009 let semaphore_moved = semaphore.clone();
1010 region_ids.push(region_id);
1011 tasks.push(async move {
1012 let _permit = semaphore_moved.acquire().await.unwrap();
1014 let (request, receiver) =
1015 WorkerRequest::new_catchup_region_request(region_id, request, None);
1016
1017 self.workers.submit_to_worker(region_id, request).await?;
1018
1019 receiver.await.context(RecvSnafu)?
1020 })
1021 }
1022
1023 let results = join_all(tasks).await;
1024 responses.extend(region_ids.into_iter().zip(results));
1025 }
1026
1027 Ok(responses)
1028 }
1029
1030 async fn handle_request(
1032 &self,
1033 region_id: RegionId,
1034 request: RegionRequest,
1035 ) -> Result<AffectedRows> {
1036 let region_metadata = self.get_metadata(region_id).ok();
1037 let (request, receiver) =
1038 WorkerRequest::try_from_region_request(region_id, request, region_metadata)?;
1039 self.workers.submit_to_worker(region_id, request).await?;
1040
1041 receiver.await.context(RecvSnafu)?
1042 }
1043
1044 fn get_committed_sequence(&self, region_id: RegionId) -> Result<SequenceNumber> {
1046 self.find_region(region_id)
1048 .map(|r| r.find_committed_sequence())
1049 }
1050
1051 #[tracing::instrument(skip_all, fields(region_id = %region_id))]
1053 fn scan_region(&self, region_id: RegionId, mut request: ScanRequest) -> Result<ScanRegion> {
1054 let query_start = Instant::now();
1055 let region = self.find_region(region_id)?;
1057 let series_index = region.series_index_store.as_ref().map(|store| {
1061 crate::series_index::SeriesIndexReadContext {
1062 store: store.clone(),
1063 version: region.series_index_version(),
1064 }
1065 });
1066 let version_data = region.version_control.current();
1067 let version = version_data.version;
1068
1069 if request.snapshot_on_scan && request.memtable_max_sequence.is_none() {
1070 request.memtable_max_sequence = Some(version_data.committed_sequence);
1071 }
1072
1073 let exact_selection = if request.exact_sequence_range {
1077 match exact_sequence_range(&request, &version) {
1078 Ok((files, sequence_range)) => Some((files, sequence_range)),
1079 Err(err) => {
1080 debug!(
1081 "Scan region {} exact sequence range denied: min={:?}, max={:?}, denial_reason=foreign_file_missing_barrier",
1082 region_id, request.memtable_min_sequence, request.memtable_max_sequence,
1083 );
1084 return Err(err);
1085 }
1086 }
1087 } else {
1088 None
1089 };
1090 let exact_sequence_range = exact_selection
1091 .as_ref()
1092 .and_then(|(_, sequence_range)| *sequence_range);
1093
1094 #[cfg(feature = "enterprise")]
1101 let extension_provider_blocks_exact = region.is_follower()
1102 && self.extension_range_provider_factory.is_some()
1103 && exact_sequence_range.is_some();
1104 #[cfg(not(feature = "enterprise"))]
1105 let extension_provider_blocks_exact = false;
1106 let exact_sequence_range = if extension_provider_blocks_exact {
1107 None
1108 } else {
1109 exact_sequence_range
1110 };
1111 let exact_selection = exact_selection.map(|(files, _)| (files, exact_sequence_range));
1112 let exact_denial_reason = if !request.exact_sequence_range {
1113 "not_requested"
1114 } else if request.skip_sst_files {
1115 "sst_files_skipped"
1116 } else if request.memtable_min_sequence.is_none() {
1117 "missing_lower_bound"
1118 } else if request.memtable_max_sequence.is_none() {
1119 "missing_upper_bound"
1120 } else if !version.options.preserve_row_sequence {
1121 "preserve_row_sequence_disabled"
1122 } else if extension_provider_blocks_exact {
1123 "extension_provider"
1124 } else if exact_sequence_range.is_none() {
1125 "selected_file_barrier_not_admitted"
1126 } else {
1127 "none"
1128 };
1129
1130 debug!(
1131 "Scan region {} exact sequence range: requested={}, min={:?}, max={:?}, committed={}, flushed={}, available={}, selected_files={}, denial_reason={}",
1132 region_id,
1133 request.exact_sequence_range,
1134 request.memtable_min_sequence,
1135 request.memtable_max_sequence,
1136 version_data.committed_sequence,
1137 version.flushed_sequence,
1138 exact_sequence_range.is_some(),
1139 exact_selection.as_ref().map_or(0, |(files, _)| files.len()),
1140 exact_denial_reason,
1141 );
1142 validate_sequence_fences(
1143 &request,
1144 &version,
1145 region_id,
1146 exact_sequence_range.is_some(),
1147 extension_provider_blocks_exact,
1148 )?;
1149
1150 let cache_manager = self.workers.cache_manager();
1152
1153 let scan_region = ScanRegion::new(
1154 version,
1155 region.access_layer.clone(),
1156 request,
1157 CacheStrategy::EnableAll(cache_manager),
1158 )
1159 .with_series_index(series_index)
1160 .with_ignore_range_index(!self.config.experimental_enable_range_index)
1161 .with_query_stat_counters(region.region_stats.query_stat_counters())
1162 .with_max_concurrent_scan_files(self.config.max_concurrent_scan_files)
1163 .with_scan_memory_tracker(self.scan_memory_tracker.clone())
1164 .with_experimental_series_scan_v2(self.config.experimental_series_scan_v2)
1165 .with_ignore_inverted_index(self.config.inverted_index.apply_on_query.disabled())
1166 .with_ignore_fulltext_index(self.config.fulltext_index.apply_on_query.disabled())
1167 .with_ignore_bloom_filter(self.config.bloom_filter_index.apply_on_query.disabled())
1168 .with_start_time(query_start);
1169 let scan_region = if let Some(selection) = exact_selection {
1170 scan_region.with_exact_selection(selection)
1171 } else {
1172 scan_region
1173 };
1174
1175 #[cfg(feature = "enterprise")]
1176 let scan_region = self.maybe_fill_extension_range_provider(scan_region, region);
1177
1178 Ok(scan_region)
1179 }
1180
1181 #[cfg(feature = "enterprise")]
1182 fn maybe_fill_extension_range_provider(
1183 &self,
1184 mut scan_region: ScanRegion,
1185 region: MitoRegionRef,
1186 ) -> ScanRegion {
1187 if region.is_follower()
1188 && let Some(factory) = self.extension_range_provider_factory.as_ref()
1189 {
1190 scan_region
1191 .set_extension_range_provider(factory.create_extension_range_provider(region));
1192 }
1193 scan_region
1194 }
1195
1196 fn set_region_role(&self, region_id: RegionId, role: RegionRole) -> Result<()> {
1198 let region = self.find_region(region_id)?;
1199 region.set_role(role);
1200 Ok(())
1201 }
1202
1203 async fn set_region_role_state_gracefully(
1205 &self,
1206 region_id: RegionId,
1207 region_role_state: SettableRegionRoleState,
1208 ) -> Result<SetRegionRoleStateResponse> {
1209 let (request, receiver) =
1212 WorkerRequest::new_set_readonly_gracefully(region_id, region_role_state);
1213 self.workers.submit_to_worker(region_id, request).await?;
1214
1215 receiver.await.context(RecvSnafu)
1216 }
1217
1218 async fn sync_region(
1219 &self,
1220 region_id: RegionId,
1221 manifest_info: RegionManifestInfo,
1222 ) -> Result<(ManifestVersion, bool)> {
1223 ensure!(manifest_info.is_mito(), MitoManifestInfoSnafu);
1224 let manifest_version = manifest_info.data_manifest_version();
1225 let (request, receiver) =
1226 WorkerRequest::new_sync_region_request(region_id, manifest_version);
1227 self.workers.submit_to_worker(region_id, request).await?;
1228
1229 receiver.await.context(RecvSnafu)?
1230 }
1231
1232 async fn remap_manifests(
1233 &self,
1234 request: RemapManifestsRequest,
1235 ) -> Result<RemapManifestsResponse> {
1236 let region_id = request.region_id;
1237 let (request, receiver) = WorkerRequest::try_from_remap_manifests_request(request)?;
1238 self.workers.submit_to_worker(region_id, request).await?;
1239 let manifest_paths = receiver.await.context(RecvSnafu)??;
1240 Ok(RemapManifestsResponse { manifest_paths })
1241 }
1242
1243 async fn copy_region_from(
1244 &self,
1245 region_id: RegionId,
1246 request: MitoCopyRegionFromRequest,
1247 ) -> Result<MitoCopyRegionFromResponse> {
1248 let (request, receiver) =
1249 WorkerRequest::try_from_copy_region_from_request(region_id, request)?;
1250 self.workers.submit_to_worker(region_id, request).await?;
1251 let response = receiver.await.context(RecvSnafu)??;
1252 Ok(response)
1253 }
1254
1255 fn role(&self, region_id: RegionId) -> Option<RegionRole> {
1256 self.workers
1257 .get_region(region_id)
1258 .map(|region| region.region_role())
1259 }
1260}
1261
1262fn validate_sequence_fences(
1263 request: &ScanRequest,
1264 version: &crate::region::version::Version,
1265 region_id: RegionId,
1266 exact_sequence_range_available: bool,
1267 extension_provider_blocks_exact: bool,
1268) -> Result<()> {
1269 if request.exact_sequence_range {
1270 if !exact_sequence_range_available {
1276 if let Some(given_seq) = request.memtable_min_sequence {
1277 let min_readable_seq = version.flushed_sequence;
1278 ensure!(
1279 given_seq >= min_readable_seq,
1280 IncrementalQueryStaleSnafu {
1281 region_id,
1282 given_seq,
1283 min_readable_seq,
1284 }
1285 );
1286 }
1287
1288 if let Some(given_seq) = request.memtable_max_sequence {
1289 let min_enforceable_seq = version.flushed_sequence;
1290 ensure!(
1291 given_seq >= min_enforceable_seq,
1292 SnapshotFenceStaleSnafu {
1293 region_id,
1294 given_seq,
1295 min_enforceable_seq,
1296 }
1297 );
1298 }
1299
1300 return SequenceRangeUnsupportedSnafu {
1306 region_id,
1307 min_seq: request.memtable_min_sequence.unwrap_or_default(),
1308 max_seq: request.memtable_max_sequence.unwrap_or_default(),
1309 reason: sequence_range_unsupported_reason(version, extension_provider_blocks_exact),
1310 }
1311 .fail();
1312 }
1313 } else {
1314 if let Some(given_seq) = request.memtable_min_sequence {
1318 let min_readable_seq = version.flushed_sequence;
1319 ensure!(
1320 given_seq >= min_readable_seq,
1321 IncrementalQueryStaleSnafu {
1322 region_id,
1323 given_seq,
1324 min_readable_seq,
1325 }
1326 );
1327 }
1328
1329 if let Some(given_seq) = request.memtable_max_sequence
1330 && !request.skip_sst_files
1331 {
1332 let min_enforceable_seq = version.flushed_sequence;
1339 ensure!(
1340 given_seq >= min_enforceable_seq,
1341 SnapshotFenceStaleSnafu {
1342 region_id,
1343 given_seq,
1344 min_enforceable_seq,
1345 }
1346 );
1347 }
1348 }
1349
1350 Ok(())
1351}
1352
1353fn sequence_range_unsupported_reason(
1354 version: &crate::region::version::Version,
1355 extension_provider_blocks_exact: bool,
1356) -> String {
1357 if extension_provider_blocks_exact {
1358 "region is a follower with an extension range provider, whose streams cannot be filtered by sequence".to_string()
1359 } else if version.options.preserve_row_sequence {
1360 "region has files without preserved per-row sequences".to_string()
1361 } else {
1362 "region does not preserve per-row sequences (preserve_row_sequence is off)".to_string()
1363 }
1364}
1365
1366fn map_batch_responses(responses: Vec<(RegionId, Result<AffectedRows>)>) -> BatchResponses {
1367 responses
1368 .into_iter()
1369 .map(|(region_id, response)| {
1370 (
1371 region_id,
1372 response.map(RegionResponse::new).map_err(BoxedError::new),
1373 )
1374 })
1375 .collect()
1376}
1377
1378#[async_trait]
1379impl RegionEngine for MitoEngine {
1380 fn name(&self) -> &str {
1381 MITO_ENGINE_NAME
1382 }
1383
1384 #[tracing::instrument(skip_all)]
1385 async fn handle_batch_open_requests(
1386 &self,
1387 parallelism: usize,
1388 requests: Vec<(RegionId, RegionOpenRequest)>,
1389 ) -> Result<BatchResponses, BoxedError> {
1390 self.inner
1392 .handle_batch_open_requests(parallelism, requests)
1393 .await
1394 .map(map_batch_responses)
1395 .map_err(BoxedError::new)
1396 }
1397
1398 #[tracing::instrument(skip_all)]
1399 async fn handle_batch_catchup_requests(
1400 &self,
1401 parallelism: usize,
1402 requests: Vec<(RegionId, RegionCatchupRequest)>,
1403 ) -> Result<BatchResponses, BoxedError> {
1404 self.inner
1405 .handle_batch_catchup_requests(parallelism, requests)
1406 .await
1407 .map(map_batch_responses)
1408 .map_err(BoxedError::new)
1409 }
1410
1411 fn handle_batch_put_requests(
1415 self: Arc<Self>,
1416 requests: Vec<(RegionId, RegionPutRequest)>,
1417 ) -> Vec<BatchPutResponse> {
1418 let mut writes = Vec::with_capacity(requests.len());
1419 let mut responses = Vec::with_capacity(requests.len());
1420 for (region_id, request) in requests {
1421 let request = RegionRequest::Put(request);
1422 let timer = HANDLE_REQUEST_ELAPSED
1423 .with_label_values(&[request.request_type()])
1424 .start_timer();
1425 let region_metadata = self.inner.get_metadata(region_id).ok();
1426 let response =
1427 match WorkerRequest::try_from_region_request(region_id, request, region_metadata) {
1428 Ok((WorkerRequest::Write(write), receiver)) => {
1429 writes.push(write);
1430 async move {
1431 let _timer = timer;
1432 receiver
1433 .await
1434 .context(RecvSnafu)
1435 .flatten()
1436 .map(RegionResponse::new)
1437 .map_err(BoxedError::new)
1438 }
1439 .boxed()
1440 }
1441 Ok(_) => unreachable!("a put converts to a write"),
1442 Err(e) => future::ready(Err(BoxedError::new(e))).boxed(),
1443 };
1444 responses.push((vec![region_id], response));
1445 }
1446 let inner = self.inner.clone();
1447 common_runtime::spawn_ingest(async move { inner.workers.submit_writes(writes).await });
1448 responses
1449 }
1450
1451 #[tracing::instrument(skip_all)]
1452 async fn handle_request(
1453 &self,
1454 region_id: RegionId,
1455 request: RegionRequest,
1456 ) -> Result<RegionResponse, BoxedError> {
1457 let _timer = HANDLE_REQUEST_ELAPSED
1458 .with_label_values(&[request.request_type()])
1459 .start_timer();
1460
1461 let is_alter = matches!(request, RegionRequest::Alter(_));
1462 let is_create = matches!(request, RegionRequest::Create(_));
1463 let mut response = self
1464 .inner
1465 .handle_request(region_id, request)
1466 .await
1467 .map(RegionResponse::new)
1468 .map_err(BoxedError::new)?;
1469
1470 if is_alter {
1471 self.handle_alter_response(region_id, &mut response)
1472 .map_err(BoxedError::new)?;
1473 } else if is_create {
1474 self.handle_create_response(region_id, &mut response)
1475 .map_err(BoxedError::new)?;
1476 }
1477
1478 Ok(response)
1479 }
1480
1481 #[tracing::instrument(skip_all)]
1482 async fn handle_query(
1483 &self,
1484 region_id: RegionId,
1485 request: ScanRequest,
1486 ) -> Result<RegionScannerRef, BoxedError> {
1487 self.scan_region(region_id, request)
1488 .map_err(BoxedError::new)?
1489 .region_scanner()
1490 .await
1491 .map_err(BoxedError::new)
1492 }
1493
1494 fn query_memory_tracker(&self) -> Option<QueryMemoryTracker> {
1495 (self.inner.config.scan_memory_mode == ScanMemoryMode::Extended)
1496 .then(|| self.inner.scan_memory_tracker.clone())
1497 }
1498
1499 async fn get_committed_sequence(
1500 &self,
1501 region_id: RegionId,
1502 ) -> Result<SequenceNumber, BoxedError> {
1503 self.inner
1504 .get_committed_sequence(region_id)
1505 .map_err(BoxedError::new)
1506 }
1507
1508 async fn get_metadata(
1510 &self,
1511 region_id: RegionId,
1512 ) -> std::result::Result<RegionMetadataRef, BoxedError> {
1513 self.inner.get_metadata(region_id).map_err(BoxedError::new)
1514 }
1515
1516 async fn stop(&self) -> std::result::Result<(), BoxedError> {
1522 self.inner.stop().await.map_err(BoxedError::new)
1523 }
1524
1525 fn region_statistic(&self, region_id: RegionId) -> Option<RegionStatistic> {
1526 self.get_region_statistic(region_id)
1527 }
1528
1529 fn set_region_role(&self, region_id: RegionId, role: RegionRole) -> Result<(), BoxedError> {
1530 self.inner
1531 .set_region_role(region_id, role)
1532 .map_err(BoxedError::new)
1533 }
1534
1535 async fn set_region_role_state_gracefully(
1536 &self,
1537 region_id: RegionId,
1538 region_role_state: SettableRegionRoleState,
1539 ) -> Result<SetRegionRoleStateResponse, BoxedError> {
1540 let _timer = HANDLE_REQUEST_ELAPSED
1541 .with_label_values(&["set_region_role_state_gracefully"])
1542 .start_timer();
1543
1544 self.inner
1545 .set_region_role_state_gracefully(region_id, region_role_state)
1546 .await
1547 .map_err(BoxedError::new)
1548 }
1549
1550 async fn sync_region(
1551 &self,
1552 region_id: RegionId,
1553 request: SyncRegionFromRequest,
1554 ) -> Result<SyncRegionFromResponse, BoxedError> {
1555 let manifest_info = request
1556 .into_region_manifest_info()
1557 .context(UnexpectedSnafu {
1558 err_msg: "Expected a manifest info request",
1559 })
1560 .map_err(BoxedError::new)?;
1561 let (_, synced) = self
1562 .inner
1563 .sync_region(region_id, manifest_info)
1564 .await
1565 .map_err(BoxedError::new)?;
1566
1567 Ok(SyncRegionFromResponse::Mito { synced })
1568 }
1569
1570 async fn remap_manifests(
1571 &self,
1572 request: RemapManifestsRequest,
1573 ) -> Result<RemapManifestsResponse, BoxedError> {
1574 self.inner
1575 .remap_manifests(request)
1576 .await
1577 .map_err(BoxedError::new)
1578 }
1579
1580 fn role(&self, region_id: RegionId) -> Option<RegionRole> {
1581 self.inner.role(region_id)
1582 }
1583
1584 fn as_any(&self) -> &dyn Any {
1585 self
1586 }
1587}
1588
1589impl MitoEngine {
1590 fn handle_alter_response(
1591 &self,
1592 region_id: RegionId,
1593 response: &mut RegionResponse,
1594 ) -> Result<()> {
1595 if let Some(statistic) = self.region_statistic(region_id) {
1596 Self::encode_manifest_info_to_extensions(
1597 ®ion_id,
1598 statistic.manifest,
1599 &mut response.extensions,
1600 )?;
1601 }
1602 let column_metadatas = self
1603 .inner
1604 .find_region(region_id)
1605 .ok()
1606 .map(|r| r.metadata().column_metadatas.clone());
1607 if let Some(column_metadatas) = column_metadatas {
1608 Self::encode_column_metadatas_to_extensions(
1609 ®ion_id,
1610 column_metadatas,
1611 &mut response.extensions,
1612 )?;
1613 }
1614 Ok(())
1615 }
1616
1617 fn handle_create_response(
1618 &self,
1619 region_id: RegionId,
1620 response: &mut RegionResponse,
1621 ) -> Result<()> {
1622 let column_metadatas = self
1623 .inner
1624 .find_region(region_id)
1625 .ok()
1626 .map(|r| r.metadata().column_metadatas.clone());
1627 if let Some(column_metadatas) = column_metadatas {
1628 Self::encode_column_metadatas_to_extensions(
1629 ®ion_id,
1630 column_metadatas,
1631 &mut response.extensions,
1632 )?;
1633 }
1634 Ok(())
1635 }
1636}
1637
1638#[cfg(any(test, feature = "test"))]
1640#[allow(clippy::too_many_arguments)]
1641impl MitoEngine {
1642 pub async fn new_for_test<S: LogStore>(
1644 data_home: &str,
1645 mut config: MitoConfig,
1646 log_store: Arc<S>,
1647 object_store_manager: ObjectStoreManagerRef,
1648 write_buffer_manager: Option<crate::flush::WriteBufferManagerRef>,
1649 listener: Option<crate::engine::listener::EventListenerRef>,
1650 time_provider: crate::time_provider::TimeProviderRef,
1651 schema_metadata_manager: SchemaMetadataManagerRef,
1652 file_ref_manager: FileReferenceManagerRef,
1653 partition_expr_fetcher: PartitionExprFetcherRef,
1654 ) -> Result<MitoEngine> {
1655 config.sanitize(data_home)?;
1656
1657 let config = Arc::new(config);
1658 let wal_raw_entry_reader = Arc::new(LogStoreRawEntryReader::new(log_store.clone()));
1659 let total_memory = get_total_memory_bytes().max(0) as u64;
1660 let scan_memory_limit = config.scan_memory_limit.resolve(total_memory) as usize;
1661 let scan_memory_tracker = QueryMemoryTracker::builder(scan_memory_limit)
1662 .on_update(|usage| {
1663 SCAN_MEMORY_USAGE_BYTES.set(usage as i64);
1664 })
1665 .on_exhausted(|| {
1666 SCAN_MEMORY_EXHAUSTED_TOTAL.inc();
1667 })
1668 .on_reject(|| {
1669 SCAN_REQUESTS_REJECTED_TOTAL.inc();
1670 })
1671 .build();
1672 Ok(MitoEngine {
1673 inner: Arc::new(EngineInner {
1674 workers: WorkerGroup::start_for_test(
1675 data_home,
1676 config.clone(),
1677 log_store,
1678 object_store_manager,
1679 write_buffer_manager,
1680 listener,
1681 schema_metadata_manager,
1682 file_ref_manager,
1683 time_provider,
1684 partition_expr_fetcher,
1685 )
1686 .await?,
1687 config,
1688 wal_raw_entry_reader,
1689 scan_memory_tracker,
1690 region_hook: None,
1691 #[cfg(feature = "enterprise")]
1692 extension_range_provider_factory: None,
1693 }),
1694 })
1695 }
1696
1697 pub fn purge_scheduler(&self) -> &crate::schedule::scheduler::SchedulerRef {
1699 self.inner.workers.purge_scheduler()
1700 }
1701}
1702
1703#[cfg(test)]
1704mod tests {
1705 use std::time::Duration;
1706
1707 use super::*;
1708 use crate::sst::file::FileMeta;
1709
1710 #[test]
1711 fn test_is_valid_region_edit() {
1712 let edit = RegionEdit {
1714 files_to_add: vec![FileMeta::default()],
1715 files_to_remove: vec![],
1716 timestamp_ms: None,
1717 compaction_time_window: None,
1718 flushed_entry_id: None,
1719 flushed_sequence: None,
1720 committed_sequence: None,
1721 };
1722 assert!(is_valid_region_edit(&edit));
1723
1724 let edit = RegionEdit {
1726 files_to_add: vec![],
1727 files_to_remove: vec![],
1728 timestamp_ms: None,
1729 compaction_time_window: None,
1730 flushed_entry_id: None,
1731 flushed_sequence: None,
1732 committed_sequence: None,
1733 };
1734 assert!(!is_valid_region_edit(&edit));
1735
1736 let edit = RegionEdit {
1738 files_to_add: vec![],
1739 files_to_remove: vec![FileMeta::default()],
1740 timestamp_ms: None,
1741 compaction_time_window: None,
1742 flushed_entry_id: None,
1743 flushed_sequence: None,
1744 committed_sequence: None,
1745 };
1746 assert!(is_valid_region_edit(&edit));
1747
1748 let edit = RegionEdit {
1750 files_to_add: vec![FileMeta::default()],
1751 files_to_remove: vec![FileMeta::default()],
1752 timestamp_ms: None,
1753 compaction_time_window: None,
1754 flushed_entry_id: None,
1755 flushed_sequence: None,
1756 committed_sequence: None,
1757 };
1758 assert!(is_valid_region_edit(&edit));
1759
1760 let edit = RegionEdit {
1762 files_to_add: vec![FileMeta::default()],
1763 files_to_remove: vec![],
1764 timestamp_ms: None,
1765 compaction_time_window: Some(Duration::from_secs(1)),
1766 flushed_entry_id: None,
1767 flushed_sequence: None,
1768 committed_sequence: None,
1769 };
1770 assert!(!is_valid_region_edit(&edit));
1771 let edit = RegionEdit {
1772 files_to_add: vec![FileMeta::default()],
1773 files_to_remove: vec![],
1774 timestamp_ms: None,
1775 compaction_time_window: None,
1776 flushed_entry_id: Some(1),
1777 flushed_sequence: None,
1778 committed_sequence: None,
1779 };
1780 assert!(!is_valid_region_edit(&edit));
1781 let edit = RegionEdit {
1782 files_to_add: vec![FileMeta::default()],
1783 files_to_remove: vec![],
1784 timestamp_ms: None,
1785 compaction_time_window: None,
1786 flushed_entry_id: None,
1787 flushed_sequence: Some(1),
1788 committed_sequence: None,
1789 };
1790 assert!(!is_valid_region_edit(&edit));
1791 }
1792}