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