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