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