1pub mod catchup;
18pub mod opener;
19pub mod options;
20pub mod utils;
21pub(crate) mod version;
22
23use std::collections::hash_map::Entry;
24use std::collections::{HashMap, HashSet};
25use std::sync::atomic::{AtomicI64, AtomicU64, Ordering};
26use std::sync::{Arc, Mutex, RwLock};
27
28use common_base::hash::partition_expr_version;
29use common_recordbatch::adapter::RegionQueryStatCounters;
30use common_telemetry::{error, info, warn};
31use crossbeam_utils::atomic::AtomicCell;
32use object_store::ObjectStore;
33use partition::expr::PartitionExpr;
34use snafu::{OptionExt, ResultExt, ensure};
35use store_api::ManifestVersion;
36use store_api::codec::PrimaryKeyEncoding;
37use store_api::logstore::provider::Provider;
38use store_api::metadata::RegionMetadataRef;
39use store_api::metrics::{REGION_QUERY_CPU_TIME, REGION_QUERY_SCANNED_BYTES};
40use store_api::region_engine::{
41 RegionManifestInfo, RegionRole, RegionStatistic, SettableRegionRoleState,
42};
43use store_api::region_info::RegionInfoEntry;
44use store_api::region_request::{PathType, StagingPartitionDirective};
45use store_api::sst_entry::ManifestSstEntry;
46use store_api::storage::{FileId, RegionId, SequenceNumber};
47use tokio::sync::RwLockWriteGuard;
48pub use utils::*;
49
50use crate::access_layer::AccessLayerRef;
51use crate::engine::region_hook::{PendingManifestHook, RegionHookRef};
52use crate::error::{
53 FlushableRegionStateSnafu, InvalidPartitionExprSnafu, RegionNotFoundSnafu, RegionStateSnafu,
54 RegionTruncatedSnafu, Result, UnexpectedSnafu, UpdateManifestSnafu,
55};
56use crate::manifest::action::{
57 RegionChange, RegionManifest, RegionMetaAction, RegionMetaActionList,
58};
59use crate::manifest::manager::RegionManifestManager;
60use crate::region::version::{VersionControlRef, VersionRef};
61use crate::request::{OnFailure, OptionOutputTx};
62use crate::series_index::{SeriesIndexVersion, SeriesIndexVersionControl};
63use crate::sst::file::FileMeta;
64use crate::sst::file_purger::FilePurgerRef;
65use crate::sst::location::{index_file_path, sst_file_path};
66use crate::time_provider::TimeProviderRef;
67
68const ESTIMATED_WAL_FACTOR: f32 = 0.42825;
70
71#[derive(Debug)]
73pub struct RegionUsage {
74 pub region_id: RegionId,
75 pub wal_usage: u64,
76 pub sst_usage: u64,
77 pub manifest_usage: u64,
78}
79
80impl RegionUsage {
81 pub fn disk_usage(&self) -> u64 {
82 self.wal_usage + self.sst_usage + self.manifest_usage
83 }
84}
85
86#[derive(Debug, Clone, Copy, PartialEq, Eq)]
87pub enum RegionLeaderState {
88 Writable,
90 Staging,
92 EnteringStaging,
94 Altering,
96 Dropping,
98 Truncating,
100 Editing,
102 Downgrading,
104}
105
106#[derive(Debug, Clone, Copy, PartialEq, Eq)]
107pub enum RegionRoleState {
108 Leader(RegionLeaderState),
109 Follower,
110}
111
112impl RegionRoleState {
113 pub fn into_leader_state(self) -> Option<RegionLeaderState> {
115 match self {
116 RegionRoleState::Leader(leader_state) => Some(leader_state),
117 RegionRoleState::Follower => None,
118 }
119 }
120
121 pub(crate) fn as_str(&self) -> &'static str {
122 match self {
123 RegionRoleState::Follower => "Follower",
124 RegionRoleState::Leader(RegionLeaderState::Writable) => "Leader(Writable)",
125 RegionRoleState::Leader(RegionLeaderState::Staging) => "Leader(Staging)",
126 RegionRoleState::Leader(RegionLeaderState::EnteringStaging) => {
127 "Leader(EnteringStaging)"
128 }
129 RegionRoleState::Leader(RegionLeaderState::Altering) => "Leader(Altering)",
130 RegionRoleState::Leader(RegionLeaderState::Dropping) => "Leader(Dropping)",
131 RegionRoleState::Leader(RegionLeaderState::Truncating) => "Leader(Truncating)",
132 RegionRoleState::Leader(RegionLeaderState::Editing) => "Leader(Editing)",
133 RegionRoleState::Leader(RegionLeaderState::Downgrading) => "Leader(Downgrading)",
134 }
135 }
136}
137
138#[derive(Debug)]
144pub struct MitoRegion {
145 pub(crate) region_id: RegionId,
150
151 pub(crate) version_control: VersionControlRef,
155 pub(crate) series_index_version_control: SeriesIndexVersionControl,
157 pub(crate) series_index_store: Option<ObjectStore>,
159 pub(crate) access_layer: AccessLayerRef,
161 pub(crate) manifest_ctx: ManifestContextRef,
163 pub(crate) file_purger: FilePurgerRef,
165 pub(crate) provider: Provider,
167 last_flush_millis: AtomicI64,
169 last_schedule_compaction_millis: AtomicI64,
171 time_provider: TimeProviderRef,
173 pub(crate) topic_latest_entry_id: AtomicU64,
183 pub(crate) region_stats: RegionStats,
185 stats: ManifestStats,
187}
188
189#[derive(Debug)]
191pub(crate) struct RegionStats {
192 pub(crate) written_bytes: Arc<AtomicU64>,
194 pub(crate) query_cpu_time: Arc<AtomicU64>,
196 pub(crate) query_scanned_bytes: Arc<AtomicU64>,
198}
199
200impl RegionStats {
201 pub(crate) fn new() -> Self {
202 Self {
203 written_bytes: Arc::new(AtomicU64::new(0)),
204 query_cpu_time: Arc::new(AtomicU64::new(0)),
205 query_scanned_bytes: Arc::new(AtomicU64::new(0)),
206 }
207 }
208
209 pub(crate) fn query_stat_counters(&self) -> RegionQueryStatCounters {
210 RegionQueryStatCounters {
211 query_cpu_time: self.query_cpu_time.clone(),
212 query_scanned_bytes: self.query_scanned_bytes.clone(),
213 }
214 }
215}
216
217pub type MitoRegionRef = Arc<MitoRegion>;
218
219#[derive(Debug, Clone)]
220pub(crate) struct StagingPartitionInfo {
221 pub(crate) partition_directive: StagingPartitionDirective,
222 pub(crate) partition_rule_version: u64,
223}
224
225impl StagingPartitionInfo {
226 pub(crate) fn partition_expr(&self) -> Option<&str> {
228 self.partition_directive.partition_expr()
229 }
230
231 pub(crate) fn from_partition_directive(partition_directive: StagingPartitionDirective) -> Self {
233 let partition_rule_version = match &partition_directive {
234 StagingPartitionDirective::UpdatePartitionExpr(expr) => {
235 partition_expr_version(Some(expr))
236 }
237 StagingPartitionDirective::RejectAllWrites => 0,
238 };
239 Self {
240 partition_directive,
241 partition_rule_version,
242 }
243 }
244}
245
246impl MitoRegion {
247 pub(crate) fn series_index_version(&self) -> Arc<SeriesIndexVersion> {
249 self.series_index_version_control.current()
250 }
251
252 fn remove_region_metrics(&self) {
253 let region_id = self.region_id.as_u64().to_string();
254 let labels = &[region_id.as_str()];
255 let _ = REGION_QUERY_CPU_TIME.remove_label_values(labels);
256 let _ = REGION_QUERY_SCANNED_BYTES.remove_label_values(labels);
257 }
258
259 pub(crate) async fn stop(&self) {
261 self.manifest_ctx
262 .manifest_manager
263 .write()
264 .await
265 .stop()
266 .await;
267
268 info!(
269 "Stopped region manifest manager, region_id: {}",
270 self.region_id
271 );
272 }
273
274 pub fn metadata(&self) -> RegionMetadataRef {
276 let version_data = self.version_control.current();
277 version_data.version.metadata.clone()
278 }
279
280 pub(crate) fn primary_key_encoding(&self) -> PrimaryKeyEncoding {
282 let version_data = self.version_control.current();
283 version_data.version.metadata.primary_key_encoding
284 }
285
286 pub(crate) fn version(&self) -> VersionRef {
288 let version_data = self.version_control.current();
289 version_data.version
290 }
291
292 pub(crate) fn skip_wal(&self) -> bool {
294 self.provider == Provider::Noop || self.version().options.skip_wal
295 }
296
297 pub(crate) fn last_flush_millis(&self) -> i64 {
299 self.last_flush_millis.load(Ordering::Relaxed)
300 }
301
302 pub(crate) fn update_flush_millis(&self) {
304 let now = self.time_provider.current_time_millis();
305 self.last_flush_millis.store(now, Ordering::Relaxed);
306 }
307
308 pub(crate) fn last_schedule_compaction_millis(&self) -> i64 {
310 self.last_schedule_compaction_millis.load(Ordering::Relaxed)
311 }
312
313 pub(crate) fn update_schedule_compaction_millis(&self) {
315 let now = self.time_provider.current_time_millis();
316 self.last_schedule_compaction_millis
317 .store(now, Ordering::Relaxed);
318 }
319
320 pub(crate) fn table_dir(&self) -> &str {
322 self.access_layer.table_dir()
323 }
324
325 pub(crate) fn path_type(&self) -> PathType {
327 self.access_layer.path_type()
328 }
329
330 pub(crate) fn is_writable(&self) -> bool {
332 matches!(
333 self.manifest_ctx.state.load(),
334 RegionRoleState::Leader(RegionLeaderState::Writable)
335 | RegionRoleState::Leader(RegionLeaderState::Staging)
336 )
337 }
338
339 pub(crate) fn is_flushable(&self) -> bool {
341 matches!(
342 self.manifest_ctx.state.load(),
343 RegionRoleState::Leader(RegionLeaderState::Writable)
344 | RegionRoleState::Leader(RegionLeaderState::Staging)
345 | RegionRoleState::Leader(RegionLeaderState::Downgrading)
346 )
347 }
348
349 pub(crate) fn should_abort_index(&self) -> bool {
351 matches!(
352 self.manifest_ctx.state.load(),
353 RegionRoleState::Follower
354 | RegionRoleState::Leader(RegionLeaderState::Dropping)
355 | RegionRoleState::Leader(RegionLeaderState::Truncating)
356 | RegionRoleState::Leader(RegionLeaderState::Downgrading)
357 | RegionRoleState::Leader(RegionLeaderState::Staging)
358 )
359 }
360
361 pub(crate) fn is_downgrading(&self) -> bool {
363 matches!(
364 self.manifest_ctx.state.load(),
365 RegionRoleState::Leader(RegionLeaderState::Downgrading)
366 )
367 }
368
369 pub(crate) fn is_staging(&self) -> bool {
371 self.manifest_ctx.state.load() == RegionRoleState::Leader(RegionLeaderState::Staging)
372 }
373
374 pub(crate) fn is_enter_staging(&self) -> bool {
376 self.manifest_ctx.state.load()
377 == RegionRoleState::Leader(RegionLeaderState::EnteringStaging)
378 }
379
380 pub(crate) fn is_truncating(&self) -> bool {
382 self.manifest_ctx.state.load() == RegionRoleState::Leader(RegionLeaderState::Truncating)
383 }
384
385 pub fn region_id(&self) -> RegionId {
386 self.region_id
387 }
388
389 pub fn find_committed_sequence(&self) -> SequenceNumber {
390 self.version_control.committed_sequence()
391 }
392
393 pub fn flushed_sequence(&self) -> SequenceNumber {
399 self.version_control.current().version.flushed_sequence
400 }
401
402 pub fn is_follower(&self) -> bool {
404 self.manifest_ctx.state.load() == RegionRoleState::Follower
405 }
406
407 pub(crate) fn state(&self) -> RegionRoleState {
409 self.manifest_ctx.state.load()
410 }
411
412 pub(crate) fn set_role(&self, next_role: RegionRole) {
414 self.manifest_ctx.set_role(next_role, self.region_id);
415 }
416
417 pub(crate) fn region_role(&self) -> RegionRole {
418 match self.state() {
419 RegionRoleState::Follower => RegionRole::Follower,
420 RegionRoleState::Leader(RegionLeaderState::Staging) => RegionRole::StagingLeader,
421 RegionRoleState::Leader(RegionLeaderState::Downgrading) => {
422 RegionRole::DowngradingLeader
423 }
424 RegionRoleState::Leader(_) => RegionRole::Leader,
425 }
426 }
427
428 pub(crate) fn set_altering(&self) -> Result<()> {
431 self.compare_exchange_state(
432 RegionLeaderState::Writable,
433 RegionRoleState::Leader(RegionLeaderState::Altering),
434 )
435 }
436
437 pub(crate) fn set_dropping(&self, expect: RegionLeaderState) -> Result<()> {
440 self.compare_exchange_state(expect, RegionRoleState::Leader(RegionLeaderState::Dropping))
441 }
442
443 pub(crate) fn set_truncating(&self) -> Result<()> {
446 self.compare_exchange_state(
447 RegionLeaderState::Writable,
448 RegionRoleState::Leader(RegionLeaderState::Truncating),
449 )
450 }
451
452 pub(crate) fn set_editing(&self, expect: RegionLeaderState) -> Result<()> {
455 self.compare_exchange_state(expect, RegionRoleState::Leader(RegionLeaderState::Editing))
456 }
457
458 pub(crate) async fn set_staging(
464 &self,
465 manager: &mut RwLockWriteGuard<'_, RegionManifestManager>,
466 ) -> Result<()> {
467 manager.store().clear_staging_manifests().await?;
468
469 self.compare_exchange_state(
470 RegionLeaderState::Writable,
471 RegionRoleState::Leader(RegionLeaderState::Staging),
472 )
473 }
474
475 pub(crate) fn set_entering_staging(&self) -> Result<()> {
477 self.compare_exchange_state(
478 RegionLeaderState::Writable,
479 RegionRoleState::Leader(RegionLeaderState::EnteringStaging),
480 )
481 }
482
483 pub fn exit_staging(&self) -> Result<()> {
488 self.manifest_ctx.exit_staging(
489 self.region_id,
490 RegionRoleState::Leader(RegionLeaderState::Writable),
491 )
492 }
493
494 pub(crate) async fn set_role_state_gracefully(
496 &self,
497 state: SettableRegionRoleState,
498 ) -> Result<()> {
499 let mut manager: RwLockWriteGuard<'_, RegionManifestManager> =
500 self.manifest_ctx.manifest_manager.write().await;
501 let current_state = self.state();
502 let mut wait_for_checkpoint = false;
503
504 let hook_payload: Option<PendingManifestHook> = match state {
505 SettableRegionRoleState::Leader => {
506 match current_state {
509 RegionRoleState::Leader(RegionLeaderState::Staging) => {
510 info!("Exiting staging mode for region {}", self.region_id);
511 self.exit_staging_on_success(&mut manager).await?
513 }
514 RegionRoleState::Leader(RegionLeaderState::Writable) => {
515 info!("Region {} already in normal leader mode", self.region_id);
517 None
518 }
519 _ => {
520 return Err(RegionStateSnafu {
522 region_id: self.region_id,
523 state: current_state,
524 expect: RegionRoleState::Leader(RegionLeaderState::Staging),
525 }
526 .build());
527 }
528 }
529 }
530
531 SettableRegionRoleState::StagingLeader => {
532 match current_state {
535 RegionRoleState::Leader(RegionLeaderState::Writable) => {
536 info!("Entering staging mode for region {}", self.region_id);
537 self.set_staging(&mut manager).await?;
538 }
539 RegionRoleState::Leader(RegionLeaderState::Staging) => {
540 info!("Region {} already in staging mode", self.region_id);
542 }
543 _ => {
544 return Err(RegionStateSnafu {
545 region_id: self.region_id,
546 state: current_state,
547 expect: RegionRoleState::Leader(RegionLeaderState::Writable),
548 }
549 .build());
550 }
551 }
552 None
553 }
554
555 SettableRegionRoleState::Follower => {
556 match current_state {
558 RegionRoleState::Leader(RegionLeaderState::Staging) => {
559 info!(
560 "Exiting staging and demoting region {} to follower",
561 self.region_id
562 );
563 self.exit_staging()?;
564 self.set_role(RegionRole::Follower);
565 wait_for_checkpoint = true;
566 }
567 RegionRoleState::Leader(_) => {
568 info!("Demoting region {} from leader to follower", self.region_id);
569 self.set_role(RegionRole::Follower);
570 wait_for_checkpoint = true;
571 }
572 RegionRoleState::Follower => {
573 info!("Region {} already in follower mode", self.region_id);
575 }
576 }
577 None
578 }
579
580 SettableRegionRoleState::DowngradingLeader => {
581 match current_state {
583 RegionRoleState::Leader(RegionLeaderState::Staging) => {
584 info!(
585 "Exiting staging and entering downgrade for region {}",
586 self.region_id
587 );
588 self.exit_staging()?;
589 self.set_role(RegionRole::DowngradingLeader);
590 wait_for_checkpoint = true;
591 }
592 RegionRoleState::Leader(RegionLeaderState::Writable) => {
593 info!("Starting downgrade for region {}", self.region_id);
594 self.set_role(RegionRole::DowngradingLeader);
595 wait_for_checkpoint = true;
596 }
597 RegionRoleState::Leader(RegionLeaderState::Downgrading) => {
598 info!("Region {} already in downgrading mode", self.region_id);
600 wait_for_checkpoint = true;
601 }
602 RegionRoleState::Follower => {
603 warn!(
604 "Cannot start downgrade for region {} from state {:?}",
605 self.region_id, current_state
606 );
607 }
608 RegionRoleState::Leader(_) => {
609 return Err(RegionStateSnafu {
614 region_id: self.region_id,
615 state: current_state,
616 expect: RegionRoleState::Leader(RegionLeaderState::Writable),
617 }
618 .build());
619 }
620 }
621 None
622 }
623 };
624
625 if wait_for_checkpoint {
629 manager.wait_for_pending_checkpoint().await;
630 }
631
632 let mut backfill_hook_payload: Option<PendingManifestHook> = None;
634 if self.state() == RegionRoleState::Leader(RegionLeaderState::Writable) {
635 let manifest_meta = &manager.manifest().metadata;
637 let current_version = self.version();
638 let current_meta = ¤t_version.metadata;
639 if manifest_meta.partition_expr.is_none() && current_meta.partition_expr.is_some() {
640 let action = RegionMetaAction::Change(RegionChange {
641 metadata: current_meta.clone(),
642 sst_format: current_version.options.sst_format.unwrap_or_default(),
643 append_mode: None,
644 });
645 let action_list = RegionMetaActionList::with_action(action);
646 match self
647 .manifest_ctx
648 .update_locked(&mut manager, action_list, false)
649 .await
650 {
651 Ok(pending) => {
652 info!(
653 "Successfully persisted backfilled metadata for region {}, version: {}",
654 self.region_id,
655 pending.version()
656 );
657 backfill_hook_payload = Some(pending);
658 }
659 Err(e) => {
660 warn!(e; "Failed to persist backfilled metadata for region {}", self.region_id);
661 }
662 }
663 }
664 }
665
666 drop(manager);
667
668 let merged = match (hook_payload, backfill_hook_payload) {
671 (Some(staging), Some(backfill)) => Some(staging.merge(backfill)),
672 (Some(payload), None) => Some(payload),
673 (None, Some(payload)) => Some(payload),
674 (None, None) => None,
675 };
676
677 if let Some(pending) = merged {
678 pending.fire().await;
679 }
680
681 Ok(())
682 }
683
684 pub(crate) fn switch_state_to_writable(&self, expect: RegionLeaderState) {
687 if let Err(e) = self
688 .compare_exchange_state(expect, RegionRoleState::Leader(RegionLeaderState::Writable))
689 {
690 error!(e; "failed to switch region state to writable, expect state is {:?}", expect);
691 }
692 }
693
694 pub(crate) fn switch_state_to_staging(&self, expect: RegionLeaderState) {
697 if let Err(e) =
698 self.compare_exchange_state(expect, RegionRoleState::Leader(RegionLeaderState::Staging))
699 {
700 error!(e; "failed to switch region state to staging, expect state is {:?}", expect);
701 }
702 }
703
704 pub(crate) fn region_statistic(&self) -> RegionStatistic {
706 let version = self.version();
707 let memtables = &version.memtables;
708 let memtable_usage = (memtables.mutable_usage() + memtables.immutables_usage()) as u64;
709
710 let sst_usage = version.ssts.owned_sst_usage(self.region_id);
711 let index_usage = version.ssts.owned_index_usage(self.region_id);
712 let flushed_entry_id = version.flushed_entry_id;
713
714 let wal_usage = self.estimated_wal_usage(memtable_usage);
715 let manifest_usage = self.stats.total_manifest_size();
716 let num_rows = version.ssts.owned_num_rows(self.region_id) + version.memtables.num_rows();
717 let num_files = version.ssts.owned_num_files(self.region_id);
718 let manifest_version = self.stats.manifest_version();
719 let file_removed_cnt = self.stats.file_removed_cnt();
720
721 let time_range = match (version.ssts.time_range(), version.memtables.time_range()) {
722 (Some((sst_min, sst_max)), Some((mem_min, mem_max))) => {
723 Some((sst_min.min(mem_min), sst_max.max(mem_max)))
724 }
725 (range, None) | (None, range) => range,
726 };
727
728 let topic_latest_entry_id = self.topic_latest_entry_id.load(Ordering::Relaxed);
729 let written_bytes = self.region_stats.written_bytes.load(Ordering::Relaxed);
730 let query_cpu_time = self.region_stats.query_cpu_time.load(Ordering::Relaxed);
731 let query_scanned_bytes = self
732 .region_stats
733 .query_scanned_bytes
734 .load(Ordering::Relaxed);
735
736 RegionStatistic {
737 num_rows,
738 memtable_size: memtable_usage,
739 wal_size: wal_usage,
740 manifest_size: manifest_usage,
741 sst_size: sst_usage,
742 sst_num: num_files,
743 index_size: index_usage,
744 manifest: RegionManifestInfo::Mito {
745 manifest_version,
746 flushed_entry_id,
747 file_removed_cnt,
748 },
749 data_topic_latest_entry_id: topic_latest_entry_id,
750 metadata_topic_latest_entry_id: topic_latest_entry_id,
751 written_bytes,
752 query_cpu_time,
753 query_scanned_bytes,
754 min_timestamp: time_range.map(|(min, _)| min),
755 max_timestamp: time_range.map(|(_, max)| max),
756 }
757 }
758
759 fn estimated_wal_usage(&self, memtable_usage: u64) -> u64 {
762 ((memtable_usage as f32) * ESTIMATED_WAL_FACTOR) as u64
763 }
764
765 fn compare_exchange_state(
768 &self,
769 expect: RegionLeaderState,
770 state: RegionRoleState,
771 ) -> Result<()> {
772 self.manifest_ctx
773 .state
774 .compare_exchange(RegionRoleState::Leader(expect), state)
775 .map_err(|actual| {
776 RegionStateSnafu {
777 region_id: self.region_id,
778 state: actual,
779 expect: RegionRoleState::Leader(expect),
780 }
781 .build()
782 })?;
783 Ok(())
784 }
785
786 pub fn access_layer(&self) -> AccessLayerRef {
787 self.access_layer.clone()
788 }
789
790 pub(crate) fn region_info_entry(&self, node_id: Option<u64>) -> RegionInfoEntry {
792 let region_id = self.region_id;
793 let version = self.version();
794 let state = self.state();
795 let role = self.region_role();
796 let region_options = serde_json::to_string(&version.options)
797 .unwrap_or_else(|err| serde_json::json!({ "error": err.to_string() }).to_string());
798 let sst_format = match version.options.sst_format.unwrap_or_default() {
799 crate::sst::FormatType::PrimaryKey => "primary_key",
800 crate::sst::FormatType::Flat => "flat",
801 }
802 .to_string();
803
804 RegionInfoEntry {
805 region_id,
806 table_id: region_id.table_id(),
807 region_number: region_id.region_number(),
808 region_group: region_id.region_group(),
809 region_sequence: region_id.region_sequence(),
810 state: state.as_str().to_string(),
811 role: role.to_string(),
812 writable: self.is_writable(),
813 committed_sequence: self.find_committed_sequence(),
814 flushed_sequence: Some(self.flushed_sequence()).filter(|sequence| *sequence > 0),
815 manifest_version: self.stats.manifest_version(),
816 compaction_time_window: version
817 .compaction_time_window
818 .map(|duration| humantime::format_duration(duration).to_string()),
819 region_options,
820 sst_format,
821 node_id,
822 }
823 }
824
825 pub async fn manifest_sst_entries(&self) -> Vec<ManifestSstEntry> {
827 let table_dir = self.table_dir();
828 let path_type = self.access_layer.path_type();
829
830 let visible_ssts = self
831 .version()
832 .ssts
833 .levels()
834 .iter()
835 .flat_map(|level| level.files().map(|file| file.file_id().file_id()))
836 .collect::<HashSet<_>>();
837
838 let manifest_files = self.manifest_ctx.manifest().await.files.clone();
839 let staging_files = self
840 .manifest_ctx
841 .staging_manifest()
842 .await
843 .map(|m| m.files.clone())
844 .unwrap_or_default();
845 let files = manifest_files
846 .into_iter()
847 .chain(staging_files)
848 .collect::<HashMap<_, _>>();
849
850 files
851 .values()
852 .map(|meta| {
853 let region_id = self.region_id;
854 let origin_region_id = meta.region_id;
855 let (index_version, index_file_path, index_file_size) = if meta.index_file_size > 0
856 {
857 let index_file_path = index_file_path(table_dir, meta.index_id(), path_type);
858 (
859 meta.index_version,
860 Some(index_file_path),
861 Some(meta.index_file_size),
862 )
863 } else {
864 (0, None, None)
865 };
866 let visible = visible_ssts.contains(&meta.file_id);
867 ManifestSstEntry {
868 table_dir: table_dir.to_string(),
869 region_id,
870 table_id: region_id.table_id(),
871 region_number: region_id.region_number(),
872 region_group: region_id.region_group(),
873 region_sequence: region_id.region_sequence(),
874 file_id: meta.file_id.to_string(),
875 index_version,
876 level: meta.level,
877 file_path: sst_file_path(table_dir, meta.file_id(), path_type),
878 file_size: meta.file_size,
879 max_row_group_uncompressed_size: meta.max_row_group_uncompressed_size,
880 index_file_path,
881 index_file_size,
882 num_rows: meta.num_rows,
883 num_row_groups: meta.num_row_groups,
884 num_series: Some(meta.num_series),
885 min_ts: meta.time_range.0,
886 max_ts: meta.time_range.1,
887 sequence: meta.sequence.map(|s| s.get()),
888 partition_expr: meta.partition_expr.as_ref().map(ToString::to_string),
889 origin_region_id,
890 node_id: None,
891 visible,
892 primary_key_min: meta.primary_key_min.clone(),
893 primary_key_max: meta.primary_key_max.clone(),
894 }
895 })
896 .collect()
897 }
898
899 pub async fn file_metas(&self, file_ids: &[FileId]) -> Vec<Option<FileMeta>> {
901 let manifest_files = self.manifest_ctx.manifest().await.files.clone();
902
903 file_ids
904 .iter()
905 .map(|file_id| manifest_files.get(file_id).cloned())
906 .collect::<Vec<_>>()
907 }
908
909 pub async fn all_manifest_files(&self) -> (Vec<FileMeta>, ManifestVersion) {
919 let manifest = self.manifest_ctx.manifest().await;
920 let staging = self
921 .manifest_ctx
922 .staging_manifest()
923 .await
924 .map(|m| (m.files.clone(), m.manifest_version));
925
926 let version = staging
927 .as_ref()
928 .map(|(_, v)| *v)
929 .unwrap_or(manifest.manifest_version);
930
931 let files = match staging {
932 Some((staging_files, _)) => {
933 let merged = manifest
934 .files
935 .clone()
936 .into_iter()
937 .chain(staging_files)
938 .collect::<std::collections::HashMap<_, _>>();
939 merged.into_values().collect()
940 }
941 None => manifest.files.values().cloned().collect(),
942 };
943
944 (files, version)
945 }
946
947 pub(crate) async fn exit_staging_on_success(
957 &self,
958 manager: &mut RwLockWriteGuard<'_, RegionManifestManager>,
959 ) -> Result<Option<PendingManifestHook>> {
960 let current_state = self.manifest_ctx.current_state();
961 ensure!(
962 current_state == RegionRoleState::Leader(RegionLeaderState::Staging),
963 RegionStateSnafu {
964 region_id: self.region_id,
965 state: current_state,
966 expect: RegionRoleState::Leader(RegionLeaderState::Staging),
967 }
968 );
969
970 let merged_actions = match manager.merge_staged_actions(current_state).await? {
972 Some(actions) => actions,
973 None => {
974 info!(
975 "No staged manifests to merge for region {}, exiting staging mode without changes",
976 self.region_id
977 );
978 self.exit_staging()?;
980 return Ok(None);
981 }
982 };
983 let expect_change = merged_actions.actions.iter().any(|a| a.is_change());
984 let expect_partition_expr_change = merged_actions
985 .actions
986 .iter()
987 .any(|a| a.is_partition_expr_change());
988 let expect_edit = merged_actions.actions.iter().any(|a| a.is_edit());
989 ensure!(
990 !(expect_change && expect_partition_expr_change),
991 UnexpectedSnafu {
992 reason: "unexpected both change and partition expr change actions in merged actions"
993 }
994 );
995 ensure!(
996 expect_change || expect_partition_expr_change,
997 UnexpectedSnafu {
998 reason: "expect a change or partition expr change action in merged actions"
999 }
1000 );
1001 ensure!(
1002 expect_edit,
1003 UnexpectedSnafu {
1004 reason: "expect an edit action in merged actions"
1005 }
1006 );
1007
1008 let (merged_partition_expr_change, merged_change, merged_edit) =
1009 merged_actions.clone().split_region_change_and_edit();
1010 if let Some(change) = &merged_change {
1011 let current_column_metadatas = &self.version().metadata.column_metadatas;
1015 ensure!(
1016 change.metadata.column_metadatas == *current_column_metadatas,
1017 UnexpectedSnafu {
1018 reason: "change action alters column metadata in staging exit"
1019 }
1020 );
1021 }
1022
1023 let pending = self
1026 .manifest_ctx
1027 .update_locked(manager, merged_actions, false)
1028 .await?;
1029 let new_version = pending.version();
1030 info!(
1031 "Successfully submitted merged staged manifests for region {}, new version: {}",
1032 self.region_id, new_version
1033 );
1034
1035 if let Some(change) = merged_partition_expr_change {
1037 let mut new_metadata = self.version().metadata.as_ref().clone();
1038 new_metadata.set_partition_expr(change.partition_expr);
1039 self.version_control.alter_metadata(new_metadata.into());
1040 }
1041 if let Some(change) = merged_change {
1042 self.version_control.alter_metadata(change.metadata);
1043 }
1044 self.version_control
1045 .apply_edit(Some(merged_edit), &[], self.file_purger.clone());
1046
1047 if let Err(e) = manager.clear_staging_manifest_and_dir().await {
1049 error!(e; "Failed to clear staging manifest dir for region {}", self.region_id);
1050 }
1051 self.exit_staging()?;
1052
1053 Ok(Some(pending))
1055 }
1056
1057 pub fn maybe_staging_partition_expr_str(&self) -> Option<String> {
1063 let is_staging = self.is_staging();
1064 if is_staging {
1065 let staging_partition_info = self.manifest_ctx.staging_partition_info();
1066 if staging_partition_info.is_none() {
1067 warn!(
1068 "Staging partition expr is none for region {} in staging state",
1069 self.region_id
1070 );
1071 }
1072 staging_partition_info
1073 .as_ref()
1074 .and_then(|info| info.partition_expr().map(ToString::to_string))
1075 } else {
1076 let version = self.version();
1077 version.metadata.partition_expr.clone()
1078 }
1079 }
1080
1081 pub fn expected_partition_expr_version(&self) -> u64 {
1082 if self.is_staging() {
1083 self.manifest_ctx
1084 .staging_partition_info()
1085 .as_ref()
1086 .map(|info| info.partition_rule_version)
1087 .unwrap_or_default()
1088 } else {
1089 self.version().metadata.partition_expr_version
1090 }
1091 }
1092
1093 pub(crate) fn reject_all_writes_in_staging(&self) -> bool {
1095 if !self.is_staging() {
1096 return false;
1097 }
1098 self.manifest_ctx
1099 .staging_partition_info()
1100 .as_ref()
1101 .map(|info| {
1102 matches!(
1103 info.partition_directive,
1104 StagingPartitionDirective::RejectAllWrites
1105 )
1106 })
1107 .unwrap_or(false)
1108 }
1109}
1110
1111impl Drop for MitoRegion {
1112 fn drop(&mut self) {
1113 self.remove_region_metrics();
1114 }
1115}
1116
1117#[derive(Debug)]
1119pub(crate) enum IndexPublication {
1120 Committed {
1122 manifest_version: ManifestVersion,
1123 file_meta: FileMeta,
1124 },
1125 Stale(IndexPublicationStale),
1127}
1128
1129#[derive(Debug, PartialEq, Eq)]
1131pub(crate) enum IndexPublicationStale {
1132 SourceChanged,
1134 SchemaChanged,
1136}
1137
1138#[derive(Clone, Debug, PartialEq, Eq)]
1144pub(crate) struct IndexBuildSource {
1145 pub(crate) file_meta: FileMeta,
1146 pub(crate) schema_version: u64,
1147}
1148
1149impl IndexBuildSource {
1150 pub(crate) fn new(file_meta: FileMeta, schema_version: u64) -> Self {
1151 Self {
1152 file_meta,
1153 schema_version,
1154 }
1155 }
1156}
1157
1158#[derive(Debug)]
1160pub(crate) struct ManifestContext {
1161 pub(crate) manifest_manager: tokio::sync::RwLock<RegionManifestManager>,
1165 state: AtomicCell<RegionRoleState>,
1168 staging_partition_info: Mutex<Option<StagingPartitionInfo>>,
1173 hook: Option<RegionHookRef>,
1175}
1176
1177impl ManifestContext {
1178 pub(crate) fn new(
1179 manager: RegionManifestManager,
1180 state: RegionRoleState,
1181 hook: Option<RegionHookRef>,
1182 ) -> Self {
1183 ManifestContext {
1184 manifest_manager: tokio::sync::RwLock::new(manager),
1185 state: AtomicCell::new(state),
1186 staging_partition_info: Mutex::new(None),
1187 hook,
1188 }
1189 }
1190
1191 pub(crate) fn hook(&self) -> Option<RegionHookRef> {
1193 self.hook.clone()
1194 }
1195
1196 pub(crate) fn staging_partition_info(&self) -> Option<StagingPartitionInfo> {
1197 self.staging_partition_info.lock().unwrap().clone()
1198 }
1199
1200 pub(crate) fn set_staging_partition_info(&self, staging_partition_info: StagingPartitionInfo) {
1201 let mut current = self.staging_partition_info.lock().unwrap();
1202 debug_assert!(current.is_none());
1203 *current = Some(staging_partition_info);
1204 }
1205
1206 fn clear_staging_partition_info(&self) {
1207 *self.staging_partition_info.lock().unwrap() = None;
1208 }
1209
1210 pub(crate) fn exit_staging(
1211 &self,
1212 region_id: RegionId,
1213 next_state: RegionRoleState,
1214 ) -> Result<()> {
1215 self.state
1216 .compare_exchange(
1217 RegionRoleState::Leader(RegionLeaderState::Staging),
1218 next_state,
1219 )
1220 .map_err(|actual| {
1221 RegionStateSnafu {
1222 region_id,
1223 state: actual,
1224 expect: RegionRoleState::Leader(RegionLeaderState::Staging),
1225 }
1226 .build()
1227 })?;
1228 self.clear_staging_partition_info();
1229 Ok(())
1230 }
1231
1232 pub(crate) async fn manifest_version(&self) -> ManifestVersion {
1233 self.manifest_manager
1234 .read()
1235 .await
1236 .manifest()
1237 .manifest_version
1238 }
1239
1240 pub(crate) async fn has_update(&self) -> Result<bool> {
1241 self.manifest_manager.read().await.has_update().await
1242 }
1243
1244 pub(crate) fn current_state(&self) -> RegionRoleState {
1246 self.state.load()
1247 }
1248
1249 pub(crate) async fn install_manifest_to(
1255 &self,
1256 version: ManifestVersion,
1257 ) -> Result<Arc<RegionManifest>> {
1258 let mut manager = self.manifest_manager.write().await;
1259 manager.install_manifest_to(version).await?;
1260
1261 Ok(manager.manifest())
1262 }
1263
1264 pub(crate) async fn update_manifest(
1266 &self,
1267 expect_state: RegionLeaderState,
1268 action_list: RegionMetaActionList,
1269 is_staging: bool,
1270 ) -> Result<ManifestVersion> {
1271 self.update_manifest_with_state_check(action_list, is_staging, |current_state, region_id| {
1272 if expect_state != RegionLeaderState::Downgrading {
1277 if current_state == RegionRoleState::Leader(RegionLeaderState::Downgrading) {
1278 info!(
1279 "Region {} is in downgrading leader state, updating manifest. Expect state is {:?}",
1280 region_id, expect_state
1281 );
1282 }
1283 ensure!(
1284 current_state == RegionRoleState::Leader(expect_state)
1285 || current_state == RegionRoleState::Leader(RegionLeaderState::Downgrading),
1286 UpdateManifestSnafu {
1287 region_id,
1288 state: current_state,
1289 }
1290 );
1291 } else {
1292 ensure!(
1293 current_state == RegionRoleState::Leader(expect_state),
1294 RegionStateSnafu {
1295 region_id,
1296 state: current_state,
1297 expect: RegionRoleState::Leader(expect_state),
1298 }
1299 );
1300 }
1301
1302 Ok(())
1303 })
1304 .await
1305 }
1306
1307 pub(crate) async fn update_manifest_for_compaction(
1324 &self,
1325 action_list: RegionMetaActionList,
1326 ) -> Result<ManifestVersion> {
1327 self.update_manifest_with_state_check(action_list, false, |current_state, region_id| {
1328 ensure!(
1329 matches!(
1330 current_state,
1331 RegionRoleState::Leader(RegionLeaderState::Writable)
1332 | RegionRoleState::Leader(RegionLeaderState::Editing)
1333 | RegionRoleState::Leader(RegionLeaderState::Downgrading)
1334 ),
1335 UpdateManifestSnafu {
1336 region_id,
1337 state: current_state,
1338 }
1339 );
1340
1341 Ok(())
1342 })
1343 .await
1344 }
1345
1346 pub(crate) async fn update_manifest_for_index(
1352 &self,
1353 source: &IndexBuildSource,
1354 updated: FileMeta,
1355 ) -> Result<IndexPublication> {
1356 let manager = self.manifest_manager.write().await;
1357 let manifest = manager.manifest();
1358 let current_state = self.state.load();
1359 if !matches!(
1360 current_state,
1361 RegionRoleState::Leader(RegionLeaderState::Writable)
1362 | RegionRoleState::Leader(RegionLeaderState::Downgrading)
1363 ) || manager.is_stopped()
1364 {
1365 return Ok(IndexPublication::Stale(
1366 IndexPublicationStale::SourceChanged,
1367 ));
1368 }
1369
1370 if manifest.files.get(&source.file_meta.file_id) != Some(&source.file_meta) {
1371 return Ok(IndexPublication::Stale(
1372 IndexPublicationStale::SourceChanged,
1373 ));
1374 }
1375
1376 if manifest.metadata.schema_version != source.schema_version {
1377 return Ok(IndexPublication::Stale(
1378 IndexPublicationStale::SchemaChanged,
1379 ));
1380 }
1381
1382 let mut committed = source.file_meta.clone();
1384 committed.available_indexes = updated.available_indexes;
1385 committed.indexes = updated.indexes;
1386 committed.index_file_size = updated.index_file_size;
1387 committed.index_version = updated.index_version;
1388
1389 let edit = crate::manifest::action::RegionEdit {
1390 files_to_add: vec![committed.clone()],
1391 files_to_remove: Vec::new(),
1392 timestamp_ms: Some(chrono::Utc::now().timestamp_millis()),
1393 flushed_sequence: None,
1394 flushed_entry_id: None,
1395 committed_sequence: None,
1396 compaction_time_window: None,
1397 };
1398 let action_list = RegionMetaActionList::with_action(RegionMetaAction::Edit(edit));
1399 let manifest_version = self.update_and_fire(manager, action_list, false).await?;
1400
1401 Ok(IndexPublication::Committed {
1402 manifest_version,
1403 file_meta: committed,
1404 })
1405 }
1406
1407 pub(crate) async fn update_locked(
1417 &self,
1418 manager: &mut RegionManifestManager,
1419 action_list: RegionMetaActionList,
1420 is_staging: bool,
1421 ) -> Result<PendingManifestHook> {
1422 let region_id = manager.manifest().metadata.region_id;
1423 let action_list_for_hook = self.hook.as_ref().map(|_| action_list.clone());
1426 let version = if !is_staging
1427 && self.state.load() == RegionRoleState::Leader(RegionLeaderState::Downgrading)
1428 {
1429 manager.update_normal_without_checkpoint(action_list).await
1430 } else {
1431 manager.update(action_list, is_staging).await
1432 }
1433 .inspect_err(|e| error!(e; "Failed to update manifest, region_id: {}", region_id))?;
1434
1435 Ok(PendingManifestHook::new(
1436 region_id,
1437 action_list_for_hook,
1438 version,
1439 self.hook.clone(),
1440 is_staging,
1441 ))
1442 }
1443
1444 async fn update_and_fire(
1450 &self,
1451 mut manager: RwLockWriteGuard<'_, RegionManifestManager>,
1452 action_list: RegionMetaActionList,
1453 is_staging: bool,
1454 ) -> Result<ManifestVersion> {
1455 let region_id = manager.manifest().metadata.region_id;
1456 let pending = self
1457 .update_locked(&mut manager, action_list, is_staging)
1458 .await?;
1459 let version = pending.version();
1460
1461 drop(manager);
1464
1465 if self.state.load() == RegionRoleState::Follower {
1466 warn!(
1467 "Region {} becomes follower while updating manifest which may cause inconsistency, manifest version: {version}",
1468 region_id
1469 );
1470 }
1471
1472 pending.fire().await;
1473 Ok(version)
1474 }
1475
1476 async fn update_manifest_with_state_check(
1477 &self,
1478 action_list: RegionMetaActionList,
1479 is_staging: bool,
1480 check_state: impl FnOnce(RegionRoleState, RegionId) -> Result<()>,
1481 ) -> Result<ManifestVersion> {
1482 let manager = self.manifest_manager.write().await;
1484 let manifest = manager.manifest();
1486 let current_state = self.state.load();
1489 check_state(current_state, manifest.metadata.region_id)?;
1490
1491 for action in &action_list.actions {
1492 let RegionMetaAction::Edit(edit) = &action else {
1494 continue;
1495 };
1496
1497 let Some(truncated_entry_id) = manifest.truncated_entry_id else {
1499 continue;
1500 };
1501
1502 if let Some(flushed_entry_id) = edit.flushed_entry_id {
1504 let is_newer_entry = truncated_entry_id < flushed_entry_id;
1514 let is_same_entry_with_newer_sequence = truncated_entry_id == flushed_entry_id
1515 && edit.flushed_sequence.is_some_and(|flushed_sequence| {
1516 manifest.flushed_sequence < flushed_sequence
1517 });
1518
1519 ensure!(
1520 is_newer_entry || is_same_entry_with_newer_sequence,
1521 RegionTruncatedSnafu {
1522 region_id: manifest.metadata.region_id,
1523 }
1524 );
1525 }
1526
1527 if !edit.files_to_remove.is_empty() {
1529 for file in &edit.files_to_remove {
1531 ensure!(
1532 manifest.files.contains_key(&file.file_id),
1533 RegionTruncatedSnafu {
1534 region_id: manifest.metadata.region_id,
1535 }
1536 );
1537 }
1538 }
1539 }
1540
1541 self.update_and_fire(manager, action_list, is_staging).await
1542 }
1543
1544 pub(crate) fn set_role(&self, next_role: RegionRole, region_id: RegionId) {
1578 match next_role {
1579 RegionRole::Follower => {
1580 if self
1581 .exit_staging(region_id, RegionRoleState::Follower)
1582 .is_ok()
1583 {
1584 info!(
1585 "Convert region {} to follower, previous role state: {:?}",
1586 region_id,
1587 RegionRoleState::Leader(RegionLeaderState::Staging)
1588 );
1589 return;
1590 }
1591 match self.state.fetch_update(|state| {
1592 if !matches!(state, RegionRoleState::Follower) {
1593 Some(RegionRoleState::Follower)
1594 } else {
1595 None
1596 }
1597 }) {
1598 Ok(state) => info!(
1599 "Convert region {} to follower, previous role state: {:?}",
1600 region_id, state
1601 ),
1602 Err(state) => {
1603 if state != RegionRoleState::Follower {
1604 warn!(
1605 "Failed to convert region {} to follower, current role state: {:?}",
1606 region_id, state
1607 )
1608 }
1609 }
1610 }
1611 }
1612 RegionRole::Leader => {
1613 if self
1614 .exit_staging(
1615 region_id,
1616 RegionRoleState::Leader(RegionLeaderState::Writable),
1617 )
1618 .is_ok()
1619 {
1620 info!(
1621 "Convert region {} to leader, previous role state: {:?}",
1622 region_id,
1623 RegionRoleState::Leader(RegionLeaderState::Staging)
1624 );
1625 return;
1626 }
1627 match self.state.fetch_update(|state| {
1628 if matches!(
1629 state,
1630 RegionRoleState::Follower
1631 | RegionRoleState::Leader(RegionLeaderState::Downgrading)
1632 ) {
1633 Some(RegionRoleState::Leader(RegionLeaderState::Writable))
1634 } else {
1635 None
1636 }
1637 }) {
1638 Ok(state) => info!(
1639 "Convert region {} to leader, previous role state: {:?}",
1640 region_id, state
1641 ),
1642 Err(state) => {
1643 if state != RegionRoleState::Leader(RegionLeaderState::Writable) {
1644 warn!(
1645 "Failed to convert region {} to leader, current role state: {:?}",
1646 region_id, state
1647 )
1648 }
1649 }
1650 }
1651 }
1652 RegionRole::StagingLeader => {
1653 info!(
1654 "Ignore direct conversion of region {} to staging leader; staging requires the dedicated workflow",
1655 region_id
1656 );
1657 }
1658 RegionRole::DowngradingLeader => {
1659 if self
1660 .exit_staging(
1661 region_id,
1662 RegionRoleState::Leader(RegionLeaderState::Downgrading),
1663 )
1664 .is_ok()
1665 {
1666 info!(
1667 "Convert region {} to downgrading region, previous role state: {:?}",
1668 region_id,
1669 RegionRoleState::Leader(RegionLeaderState::Staging)
1670 );
1671 return;
1672 }
1673 match self.state.compare_exchange(
1674 RegionRoleState::Leader(RegionLeaderState::Writable),
1675 RegionRoleState::Leader(RegionLeaderState::Downgrading),
1676 ) {
1677 Ok(state) => info!(
1678 "Convert region {} to downgrading region, previous role state: {:?}",
1679 region_id, state
1680 ),
1681 Err(state) => {
1682 if state != RegionRoleState::Leader(RegionLeaderState::Downgrading) {
1683 warn!(
1684 "Failed to convert region {} to downgrading leader, current role state: {:?}",
1685 region_id, state
1686 )
1687 }
1688 }
1689 }
1690 }
1691 }
1692 }
1693
1694 pub(crate) async fn manifest(&self) -> Arc<crate::manifest::action::RegionManifest> {
1696 self.manifest_manager.read().await.manifest()
1697 }
1698
1699 pub(crate) async fn staging_manifest(
1701 &self,
1702 ) -> Option<Arc<crate::manifest::action::RegionManifest>> {
1703 self.manifest_manager.read().await.staging_manifest()
1704 }
1705}
1706
1707pub(crate) type ManifestContextRef = Arc<ManifestContext>;
1708
1709#[derive(Debug, Default)]
1711pub(crate) struct RegionMap {
1712 regions: RwLock<HashMap<RegionId, MitoRegionRef>>,
1713}
1714
1715impl RegionMap {
1716 pub(crate) fn is_region_exists(&self, region_id: RegionId) -> bool {
1718 let regions = self.regions.read().unwrap();
1719 regions.contains_key(®ion_id)
1720 }
1721
1722 pub(crate) fn insert_region(&self, region: MitoRegionRef) {
1724 let mut regions = self.regions.write().unwrap();
1725 regions.insert(region.region_id, region);
1726 }
1727
1728 pub(crate) fn get_region(&self, region_id: RegionId) -> Option<MitoRegionRef> {
1730 let regions = self.regions.read().unwrap();
1731 regions.get(®ion_id).cloned()
1732 }
1733
1734 pub(crate) fn writable_region(&self, region_id: RegionId) -> Result<MitoRegionRef> {
1738 let region = self
1739 .get_region(region_id)
1740 .context(RegionNotFoundSnafu { region_id })?;
1741 ensure!(
1742 region.is_writable(),
1743 RegionStateSnafu {
1744 region_id,
1745 state: region.state(),
1746 expect: RegionRoleState::Leader(RegionLeaderState::Writable),
1747 }
1748 );
1749 Ok(region)
1750 }
1751
1752 pub(crate) fn follower_region(&self, region_id: RegionId) -> Result<MitoRegionRef> {
1756 let region = self
1757 .get_region(region_id)
1758 .context(RegionNotFoundSnafu { region_id })?;
1759 ensure!(
1760 region.is_follower(),
1761 RegionStateSnafu {
1762 region_id,
1763 state: region.state(),
1764 expect: RegionRoleState::Follower,
1765 }
1766 );
1767
1768 Ok(region)
1769 }
1770
1771 pub(crate) fn get_region_or<F: OnFailure>(
1775 &self,
1776 region_id: RegionId,
1777 cb: &mut F,
1778 ) -> Option<MitoRegionRef> {
1779 match self
1780 .get_region(region_id)
1781 .context(RegionNotFoundSnafu { region_id })
1782 {
1783 Ok(region) => Some(region),
1784 Err(e) => {
1785 cb.on_failure(e);
1786 None
1787 }
1788 }
1789 }
1790
1791 pub(crate) fn writable_region_or<F: OnFailure>(
1795 &self,
1796 region_id: RegionId,
1797 cb: &mut F,
1798 ) -> Option<MitoRegionRef> {
1799 match self.writable_region(region_id) {
1800 Ok(region) => Some(region),
1801 Err(e) => {
1802 cb.on_failure(e);
1803 None
1804 }
1805 }
1806 }
1807
1808 pub(crate) fn writable_non_staging_region(&self, region_id: RegionId) -> Result<MitoRegionRef> {
1812 let region = self.writable_region(region_id)?;
1813 if region.is_staging() {
1814 return Err(crate::error::RegionStateSnafu {
1815 region_id,
1816 state: region.state(),
1817 expect: RegionRoleState::Leader(RegionLeaderState::Writable),
1818 }
1819 .build());
1820 }
1821 Ok(region)
1822 }
1823
1824 pub(crate) fn staging_region(&self, region_id: RegionId) -> Result<MitoRegionRef> {
1828 let region = self
1829 .get_region(region_id)
1830 .context(RegionNotFoundSnafu { region_id })?;
1831 ensure!(
1832 region.is_staging(),
1833 RegionStateSnafu {
1834 region_id,
1835 state: region.state(),
1836 expect: RegionRoleState::Leader(RegionLeaderState::Staging),
1837 }
1838 );
1839 Ok(region)
1840 }
1841
1842 pub(crate) fn flushable_region(&self, region_id: RegionId) -> Result<MitoRegionRef> {
1846 let region = self
1847 .get_region(region_id)
1848 .context(RegionNotFoundSnafu { region_id })?;
1849 ensure!(
1850 region.is_flushable(),
1851 FlushableRegionStateSnafu {
1852 region_id,
1853 state: region.state(),
1854 }
1855 );
1856 Ok(region)
1857 }
1858
1859 pub(crate) fn remove_region(&self, region_id: RegionId) -> Option<MitoRegionRef> {
1861 let mut regions = self.regions.write().unwrap();
1862 regions.remove(®ion_id)
1863 }
1864
1865 pub(crate) fn list_regions(&self) -> Vec<MitoRegionRef> {
1867 let regions = self.regions.read().unwrap();
1868 regions.values().cloned().collect()
1869 }
1870
1871 pub(crate) fn clear(&self) {
1873 self.regions.write().unwrap().clear();
1874 }
1875}
1876
1877pub(crate) type RegionMapRef = Arc<RegionMap>;
1878
1879#[derive(Debug, Default)]
1881pub(crate) struct OpeningRegions {
1882 regions: RwLock<HashMap<RegionId, Vec<OptionOutputTx>>>,
1883}
1884
1885impl OpeningRegions {
1886 pub(crate) fn wait_for_opening_region(
1888 &self,
1889 region_id: RegionId,
1890 sender: OptionOutputTx,
1891 ) -> Option<OptionOutputTx> {
1892 let mut regions = self.regions.write().unwrap();
1893 match regions.entry(region_id) {
1894 Entry::Occupied(mut senders) => {
1895 senders.get_mut().push(sender);
1896 None
1897 }
1898 Entry::Vacant(_) => Some(sender),
1899 }
1900 }
1901
1902 pub(crate) fn is_region_exists(&self, region_id: RegionId) -> bool {
1904 let regions = self.regions.read().unwrap();
1905 regions.contains_key(®ion_id)
1906 }
1907
1908 pub(crate) fn insert_sender(&self, region: RegionId, sender: OptionOutputTx) {
1910 let mut regions = self.regions.write().unwrap();
1911 regions.insert(region, vec![sender]);
1912 }
1913
1914 pub(crate) fn remove_sender(&self, region_id: RegionId) -> Vec<OptionOutputTx> {
1916 let mut regions = self.regions.write().unwrap();
1917 regions.remove(®ion_id).unwrap_or_default()
1918 }
1919
1920 #[cfg(test)]
1921 pub(crate) fn sender_len(&self, region_id: RegionId) -> usize {
1922 let regions = self.regions.read().unwrap();
1923 if let Some(senders) = regions.get(®ion_id) {
1924 senders.len()
1925 } else {
1926 0
1927 }
1928 }
1929}
1930
1931pub(crate) type OpeningRegionsRef = Arc<OpeningRegions>;
1932
1933#[derive(Debug, Default)]
1935pub(crate) struct CatchupRegions {
1936 regions: RwLock<HashSet<RegionId>>,
1937}
1938
1939impl CatchupRegions {
1940 pub(crate) fn is_region_exists(&self, region_id: RegionId) -> bool {
1942 let regions = self.regions.read().unwrap();
1943 regions.contains(®ion_id)
1944 }
1945
1946 pub(crate) fn insert_region(&self, region_id: RegionId) {
1948 let mut regions = self.regions.write().unwrap();
1949 regions.insert(region_id);
1950 }
1951
1952 pub(crate) fn remove_region(&self, region_id: RegionId) {
1954 let mut regions = self.regions.write().unwrap();
1955 regions.remove(®ion_id);
1956 }
1957}
1958
1959pub(crate) type CatchupRegionsRef = Arc<CatchupRegions>;
1960
1961#[derive(Default, Debug, Clone)]
1963pub struct ManifestStats {
1964 pub(crate) total_manifest_size: Arc<AtomicU64>,
1965 pub(crate) manifest_version: Arc<AtomicU64>,
1966 pub(crate) file_removed_cnt: Arc<AtomicU64>,
1967}
1968
1969impl ManifestStats {
1970 fn total_manifest_size(&self) -> u64 {
1971 self.total_manifest_size.load(Ordering::Relaxed)
1972 }
1973
1974 fn manifest_version(&self) -> u64 {
1975 self.manifest_version.load(Ordering::Relaxed)
1976 }
1977
1978 fn file_removed_cnt(&self) -> u64 {
1979 self.file_removed_cnt.load(Ordering::Relaxed)
1980 }
1981}
1982
1983pub fn parse_partition_expr(partition_expr_str: Option<&str>) -> Result<Option<PartitionExpr>> {
1985 match partition_expr_str {
1986 None => Ok(None),
1987 Some("") => Ok(None),
1988 Some(json_str) => {
1989 let expr = partition::expr::PartitionExpr::from_json_str(json_str)
1990 .with_context(|_| InvalidPartitionExprSnafu { expr: json_str })?;
1991 Ok(expr)
1992 }
1993 }
1994}
1995
1996#[cfg(test)]
1997mod tests {
1998 use std::sync::Arc;
1999
2000 use common_datasource::compression::CompressionType;
2001 use common_test_util::temp_dir::create_temp_dir;
2002 use crossbeam_utils::atomic::AtomicCell;
2003 use object_store::ObjectStore;
2004 use object_store::services::Fs;
2005 use store_api::logstore::provider::Provider;
2006 use store_api::region_engine::RegionRole;
2007 use store_api::region_request::PathType;
2008 use store_api::storage::{FileId, RegionId};
2009
2010 use crate::access_layer::AccessLayer;
2011 use crate::error::Error;
2012 use crate::manifest::action::{
2013 RegionChange, RegionEdit, RegionMetaAction, RegionMetaActionList, RegionPartitionExprChange,
2014 };
2015 use crate::manifest::manager::{RegionManifestManager, RegionManifestOptions};
2016 use crate::region::{
2017 IndexBuildSource, IndexPublication, IndexPublicationStale, ManifestContext, ManifestStats,
2018 MitoRegion, RegionLeaderState, RegionRoleState, RegionStats,
2019 };
2020 use crate::sst::FormatType;
2021 use crate::sst::index::intermediate::IntermediateManager;
2022 use crate::sst::index::puffin_manager::PuffinManagerFactory;
2023 use crate::test_util::scheduler_util::SchedulerEnv;
2024 use crate::test_util::version_util::VersionControlBuilder;
2025 use crate::time_provider::StdTimeProvider;
2026
2027 #[test]
2028 fn test_region_state_lock_free() {
2029 assert!(AtomicCell::<RegionRoleState>::is_lock_free());
2030 }
2031
2032 #[test]
2033 fn test_region_role_state_as_str() {
2034 assert_eq!("Follower", RegionRoleState::Follower.as_str());
2035 assert_eq!(
2036 "Leader(Writable)",
2037 RegionRoleState::Leader(RegionLeaderState::Writable).as_str()
2038 );
2039 assert_eq!(
2040 "Leader(Staging)",
2041 RegionRoleState::Leader(RegionLeaderState::Staging).as_str()
2042 );
2043 assert_eq!(
2044 "Leader(Downgrading)",
2045 RegionRoleState::Leader(RegionLeaderState::Downgrading).as_str()
2046 );
2047 }
2048
2049 async fn build_test_region(env: &SchedulerEnv) -> MitoRegion {
2050 let builder = VersionControlBuilder::new();
2051 let version_control = Arc::new(builder.build());
2052 let metadata = version_control.current().version.metadata.clone();
2053
2054 let manager = RegionManifestManager::new(
2055 metadata.clone(),
2056 0,
2057 RegionManifestOptions {
2058 manifest_dir: "".to_string(),
2059 object_store: env.access_layer.object_store().clone(),
2060 compress_type: CompressionType::Uncompressed,
2061 checkpoint_distance: 10,
2062 remove_file_options: Default::default(),
2063 manifest_cache: None,
2064 },
2065 FormatType::PrimaryKey,
2066 &Default::default(),
2067 )
2068 .await
2069 .unwrap();
2070
2071 let manifest_ctx = Arc::new(ManifestContext::new(
2072 manager,
2073 RegionRoleState::Leader(RegionLeaderState::Writable),
2074 None,
2075 ));
2076
2077 MitoRegion {
2078 region_id: metadata.region_id,
2079 version_control,
2080 series_index_version_control: Default::default(),
2081 series_index_store: None,
2082 access_layer: env.access_layer.clone(),
2083 manifest_ctx,
2084 file_purger: crate::test_util::new_noop_file_purger(),
2085 provider: Provider::noop_provider(),
2086 last_flush_millis: Default::default(),
2087 last_schedule_compaction_millis: Default::default(),
2088 time_provider: Arc::new(StdTimeProvider),
2089 topic_latest_entry_id: Default::default(),
2090 region_stats: RegionStats::new(),
2091 stats: ManifestStats::default(),
2092 }
2093 }
2094
2095 fn empty_edit() -> RegionEdit {
2096 RegionEdit {
2097 files_to_add: Vec::new(),
2098 files_to_remove: Vec::new(),
2099 timestamp_ms: None,
2100 compaction_time_window: None,
2101 flushed_entry_id: None,
2102 flushed_sequence: None,
2103 committed_sequence: None,
2104 }
2105 }
2106
2107 #[tokio::test]
2108 async fn test_compaction_update_manifest_allows_editing_state() {
2109 let env = SchedulerEnv::new().await;
2110 let region = build_test_region(&env).await;
2111 region.set_editing(RegionLeaderState::Writable).unwrap();
2112
2113 let file_id = FileId::random();
2114 let action_list = RegionMetaActionList::with_action(RegionMetaAction::Edit(RegionEdit {
2115 files_to_add: vec![crate::sst::file::FileMeta {
2116 region_id: region.region_id,
2117 file_id,
2118 level: 1,
2119 ..Default::default()
2120 }],
2121 files_to_remove: Vec::new(),
2122 timestamp_ms: None,
2123 compaction_time_window: None,
2124 flushed_entry_id: None,
2125 flushed_sequence: None,
2126 committed_sequence: None,
2127 }));
2128
2129 region
2130 .manifest_ctx
2131 .update_manifest_for_compaction(action_list)
2132 .await
2133 .unwrap();
2134
2135 assert!(
2136 region
2137 .manifest_ctx
2138 .manifest()
2139 .await
2140 .files
2141 .contains_key(&file_id)
2142 );
2143 }
2144
2145 #[tokio::test]
2146 async fn test_index_publication_rejects_older_source_generation() {
2147 let env = SchedulerEnv::new().await;
2148 let region = build_test_region(&env).await;
2149 let source = crate::sst::file::FileMeta {
2150 region_id: region.region_id,
2151 file_id: FileId::random(),
2152 level: 1,
2153 file_size: 1024,
2154 ..Default::default()
2155 };
2156 region
2157 .manifest_ctx
2158 .update_manifest(
2159 RegionLeaderState::Writable,
2160 RegionMetaActionList::with_action(RegionMetaAction::Edit(RegionEdit {
2161 files_to_add: vec![source.clone()],
2162 ..empty_edit()
2163 })),
2164 false,
2165 )
2166 .await
2167 .unwrap();
2168
2169 let mut first_update = source.clone();
2170 first_update.index_version = 1;
2171 first_update.index_file_size = 128;
2172 let schema_version = region.version().metadata.schema_version;
2173 let initial_source = IndexBuildSource::new(source.clone(), schema_version);
2174 let first_committed = match region
2175 .manifest_ctx
2176 .update_manifest_for_index(&initial_source, first_update)
2177 .await
2178 .unwrap()
2179 {
2180 IndexPublication::Committed { file_meta, .. } => file_meta,
2181 IndexPublication::Stale(_) => panic!("first index publication should commit"),
2182 };
2183
2184 let (ready_tx, ready_rx) = tokio::sync::oneshot::channel();
2185 let (release_tx, release_rx) = tokio::sync::oneshot::channel();
2186 let delayed_manifest_ctx = region.manifest_ctx.clone();
2187 let delayed_source = IndexBuildSource::new(first_committed.clone(), schema_version);
2188 let delayed_publication = tokio::spawn(async move {
2189 let mut delayed_update = delayed_source.file_meta.clone();
2190 delayed_update.index_version = 2;
2191 delayed_update.index_file_size = 128;
2192 ready_tx.send(()).unwrap();
2193 release_rx.await.unwrap();
2194 delayed_manifest_ctx
2195 .update_manifest_for_index(&delayed_source, delayed_update)
2196 .await
2197 });
2198 ready_rx.await.unwrap();
2199
2200 let mut newer_update = first_committed.clone();
2201 newer_update.index_version = 3;
2202 newer_update.index_file_size = 256;
2203 let newer_source = IndexBuildSource::new(first_committed, schema_version);
2204 let newer_committed = match region
2205 .manifest_ctx
2206 .update_manifest_for_index(&newer_source, newer_update)
2207 .await
2208 .unwrap()
2209 {
2210 IndexPublication::Committed { file_meta, .. } => file_meta,
2211 IndexPublication::Stale(_) => panic!("newer index publication should commit"),
2212 };
2213
2214 release_tx.send(()).unwrap();
2215 assert!(matches!(
2216 delayed_publication.await.unwrap().unwrap(),
2217 IndexPublication::Stale(IndexPublicationStale::SourceChanged)
2218 ));
2219 assert_eq!(
2220 region
2221 .manifest_ctx
2222 .manifest()
2223 .await
2224 .files
2225 .get(&source.file_id),
2226 Some(&newer_committed)
2227 );
2228 }
2229
2230 #[tokio::test]
2231 async fn test_index_publication_rejects_older_schema_generation() {
2232 let env = SchedulerEnv::new().await;
2233 let region = build_test_region(&env).await;
2234 let source_meta = crate::sst::file::FileMeta {
2235 region_id: region.region_id,
2236 file_id: FileId::random(),
2237 level: 1,
2238 file_size: 1024,
2239 ..Default::default()
2240 };
2241 region
2242 .manifest_ctx
2243 .update_manifest(
2244 RegionLeaderState::Writable,
2245 RegionMetaActionList::with_action(RegionMetaAction::Edit(RegionEdit {
2246 files_to_add: vec![source_meta.clone()],
2247 ..empty_edit()
2248 })),
2249 false,
2250 )
2251 .await
2252 .unwrap();
2253
2254 let old_schema_version = region.version().metadata.schema_version;
2255 let source = IndexBuildSource::new(source_meta.clone(), old_schema_version);
2256 let mut new_metadata = region.version().metadata.as_ref().clone();
2257 new_metadata.schema_version += 1;
2258 region
2259 .manifest_ctx
2260 .update_manifest(
2261 RegionLeaderState::Writable,
2262 RegionMetaActionList::with_action(RegionMetaAction::Change(RegionChange {
2263 metadata: Arc::new(new_metadata),
2264 sst_format: FormatType::PrimaryKey,
2265 append_mode: None,
2266 })),
2267 false,
2268 )
2269 .await
2270 .unwrap();
2271
2272 let mut updated = source_meta.clone();
2273 updated.index_version = 1;
2274 updated.index_file_size = 128;
2275 assert!(matches!(
2276 region
2277 .manifest_ctx
2278 .update_manifest_for_index(&source, updated)
2279 .await
2280 .unwrap(),
2281 IndexPublication::Stale(IndexPublicationStale::SchemaChanged)
2282 ));
2283
2284 let manifest = region.manifest_ctx.manifest().await;
2285 assert_eq!(manifest.metadata.schema_version, old_schema_version + 1);
2286 assert_eq!(manifest.files.get(&source_meta.file_id), Some(&source_meta));
2287 }
2288
2289 #[tokio::test]
2290 async fn test_exit_staging_partition_expr_change_and_edit_success() {
2291 let env = SchedulerEnv::new().await;
2292 let region = build_test_region(&env).await;
2293
2294 let mut manager = region.manifest_ctx.manifest_manager.write().await;
2295 region.set_staging(&mut manager).await.unwrap();
2296 manager
2297 .update(
2298 RegionMetaActionList::new(vec![
2299 RegionMetaAction::PartitionExprChange(RegionPartitionExprChange {
2300 partition_expr: Some("expr_a".to_string()),
2301 }),
2302 RegionMetaAction::Edit(empty_edit()),
2303 ]),
2304 true,
2305 )
2306 .await
2307 .unwrap();
2308
2309 let _hook_payload = region.exit_staging_on_success(&mut manager).await.unwrap();
2310 drop(manager);
2311
2312 assert_eq!(
2313 region.version().metadata.partition_expr.as_deref(),
2314 Some("expr_a")
2315 );
2316 assert_eq!(
2317 region.state(),
2318 RegionRoleState::Leader(RegionLeaderState::Writable)
2319 );
2320 }
2321
2322 #[tokio::test]
2323 async fn test_exit_staging_change_with_same_columns_success() {
2324 let env = SchedulerEnv::new().await;
2325 let region = build_test_region(&env).await;
2326
2327 let mut manager = region.manifest_ctx.manifest_manager.write().await;
2328 region.set_staging(&mut manager).await.unwrap();
2329
2330 let mut changed_metadata = region.version().metadata.as_ref().clone();
2331 changed_metadata.set_partition_expr(Some("expr_b".to_string()));
2332
2333 manager
2334 .update(
2335 RegionMetaActionList::new(vec![
2336 RegionMetaAction::Change(RegionChange {
2337 metadata: Arc::new(changed_metadata),
2338 sst_format: FormatType::PrimaryKey,
2339 append_mode: None,
2340 }),
2341 RegionMetaAction::Edit(empty_edit()),
2342 ]),
2343 true,
2344 )
2345 .await
2346 .unwrap();
2347
2348 let _hook_payload = region.exit_staging_on_success(&mut manager).await.unwrap();
2349 drop(manager);
2350
2351 assert_eq!(
2352 region.version().metadata.partition_expr.as_deref(),
2353 Some("expr_b")
2354 );
2355 assert_eq!(
2356 region.state(),
2357 RegionRoleState::Leader(RegionLeaderState::Writable)
2358 );
2359 }
2360
2361 #[tokio::test]
2362 async fn test_exit_staging_change_with_different_columns_fails() {
2363 let env = SchedulerEnv::new().await;
2364 let region = build_test_region(&env).await;
2365
2366 let mut manager = region.manifest_ctx.manifest_manager.write().await;
2367 region.set_staging(&mut manager).await.unwrap();
2368
2369 let mut changed_metadata = region.version().metadata.as_ref().clone();
2370 changed_metadata.column_metadatas.rotate_left(1);
2371
2372 manager
2373 .update(
2374 RegionMetaActionList::new(vec![
2375 RegionMetaAction::Change(RegionChange {
2376 metadata: Arc::new(changed_metadata),
2377 sst_format: FormatType::PrimaryKey,
2378 append_mode: None,
2379 }),
2380 RegionMetaAction::Edit(empty_edit()),
2381 ]),
2382 true,
2383 )
2384 .await
2385 .unwrap();
2386
2387 let result = region.exit_staging_on_success(&mut manager).await;
2388 assert!(matches!(result, Err(Error::Unexpected { .. })));
2389 }
2390
2391 #[tokio::test]
2392 async fn test_exit_staging_partition_expr_change_and_change_conflict_fails() {
2393 let env = SchedulerEnv::new().await;
2394 let region = build_test_region(&env).await;
2395
2396 let mut manager = region.manifest_ctx.manifest_manager.write().await;
2397 region.set_staging(&mut manager).await.unwrap();
2398
2399 let mut changed_metadata = region.version().metadata.as_ref().clone();
2400 changed_metadata.set_partition_expr(Some("expr_c".to_string()));
2401
2402 manager
2403 .update(
2404 RegionMetaActionList::new(vec![
2405 RegionMetaAction::PartitionExprChange(RegionPartitionExprChange {
2406 partition_expr: Some("expr_c".to_string()),
2407 }),
2408 RegionMetaAction::Change(RegionChange {
2409 metadata: Arc::new(changed_metadata),
2410 sst_format: FormatType::PrimaryKey,
2411 append_mode: None,
2412 }),
2413 RegionMetaAction::Edit(empty_edit()),
2414 ]),
2415 true,
2416 )
2417 .await
2418 .unwrap();
2419
2420 let result = region.exit_staging_on_success(&mut manager).await;
2421 assert!(matches!(result, Err(Error::Unexpected { .. })));
2422 }
2423
2424 #[tokio::test]
2425 async fn test_set_region_state() {
2426 let env = SchedulerEnv::new().await;
2427 let builder = VersionControlBuilder::new();
2428 let version_control = Arc::new(builder.build());
2429 let manifest_ctx = env
2430 .mock_manifest_context(version_control.current().version.metadata.clone())
2431 .await;
2432
2433 let region_id = RegionId::new(1024, 0);
2434 manifest_ctx.set_role(RegionRole::Follower, region_id);
2436 assert_eq!(manifest_ctx.state.load(), RegionRoleState::Follower);
2437
2438 manifest_ctx.set_role(RegionRole::Leader, region_id);
2440 assert_eq!(
2441 manifest_ctx.state.load(),
2442 RegionRoleState::Leader(RegionLeaderState::Writable)
2443 );
2444
2445 manifest_ctx.set_role(RegionRole::StagingLeader, region_id);
2447 assert_eq!(
2448 manifest_ctx.state.load(),
2449 RegionRoleState::Leader(RegionLeaderState::Writable)
2450 );
2451
2452 manifest_ctx.set_role(RegionRole::DowngradingLeader, region_id);
2454 assert_eq!(
2455 manifest_ctx.state.load(),
2456 RegionRoleState::Leader(RegionLeaderState::Downgrading)
2457 );
2458
2459 manifest_ctx.set_role(RegionRole::Follower, region_id);
2461 assert_eq!(manifest_ctx.state.load(), RegionRoleState::Follower);
2462
2463 manifest_ctx.set_role(RegionRole::DowngradingLeader, region_id);
2465 assert_eq!(manifest_ctx.state.load(), RegionRoleState::Follower);
2466
2467 manifest_ctx.set_role(RegionRole::Leader, region_id);
2469 manifest_ctx.set_role(RegionRole::DowngradingLeader, region_id);
2470 assert_eq!(
2471 manifest_ctx.state.load(),
2472 RegionRoleState::Leader(RegionLeaderState::Downgrading)
2473 );
2474
2475 manifest_ctx.set_role(RegionRole::Leader, region_id);
2477 assert_eq!(
2478 manifest_ctx.state.load(),
2479 RegionRoleState::Leader(RegionLeaderState::Writable)
2480 );
2481 }
2482
2483 #[tokio::test]
2484 async fn test_staging_state_validation() {
2485 let env = SchedulerEnv::new().await;
2486 let builder = VersionControlBuilder::new();
2487 let version_control = Arc::new(builder.build());
2488
2489 let staging_ctx = {
2491 let manager = RegionManifestManager::new(
2492 version_control.current().version.metadata.clone(),
2493 0,
2494 RegionManifestOptions {
2495 manifest_dir: "".to_string(),
2496 object_store: env.access_layer.object_store().clone(),
2497 compress_type: CompressionType::Uncompressed,
2498 checkpoint_distance: 10,
2499 remove_file_options: Default::default(),
2500 manifest_cache: None,
2501 },
2502 FormatType::PrimaryKey,
2503 &Default::default(),
2504 )
2505 .await
2506 .unwrap();
2507 Arc::new(ManifestContext::new(
2508 manager,
2509 RegionRoleState::Leader(RegionLeaderState::Staging),
2510 None,
2511 ))
2512 };
2513
2514 assert_eq!(
2516 staging_ctx.current_state(),
2517 RegionRoleState::Leader(RegionLeaderState::Staging)
2518 );
2519
2520 let writable_ctx = env
2522 .mock_manifest_context(version_control.current().version.metadata.clone())
2523 .await;
2524
2525 assert_eq!(
2526 writable_ctx.current_state(),
2527 RegionRoleState::Leader(RegionLeaderState::Writable)
2528 );
2529 }
2530
2531 #[tokio::test]
2532 async fn test_staging_state_transitions() {
2533 let builder = VersionControlBuilder::new();
2534 let version_control = Arc::new(builder.build());
2535 let metadata = version_control.current().version.metadata.clone();
2536
2537 let temp_dir = create_temp_dir("");
2539 let path_str = temp_dir.path().display().to_string();
2540 let fs_builder = Fs::default().root(&path_str);
2541 let object_store = ObjectStore::new(fs_builder).unwrap();
2542
2543 let index_aux_path = temp_dir.path().join("index_aux");
2544 let puffin_mgr = PuffinManagerFactory::new(&index_aux_path, 4096, None, None)
2545 .await
2546 .unwrap();
2547 let intm_mgr = IntermediateManager::init_fs(index_aux_path.to_str().unwrap())
2548 .await
2549 .unwrap();
2550
2551 let access_layer = Arc::new(AccessLayer::new(
2552 "",
2553 PathType::Bare,
2554 object_store,
2555 puffin_mgr,
2556 intm_mgr,
2557 ));
2558
2559 let manager = RegionManifestManager::new(
2560 metadata.clone(),
2561 0,
2562 RegionManifestOptions {
2563 manifest_dir: "".to_string(),
2564 object_store: access_layer.object_store().clone(),
2565 compress_type: CompressionType::Uncompressed,
2566 checkpoint_distance: 10,
2567 remove_file_options: Default::default(),
2568 manifest_cache: None,
2569 },
2570 FormatType::PrimaryKey,
2571 &Default::default(),
2572 )
2573 .await
2574 .unwrap();
2575
2576 let manifest_ctx = Arc::new(ManifestContext::new(
2577 manager,
2578 RegionRoleState::Leader(RegionLeaderState::Writable),
2579 None,
2580 ));
2581
2582 let region = MitoRegion {
2583 region_id: metadata.region_id,
2584 version_control,
2585 series_index_version_control: Default::default(),
2586 series_index_store: None,
2587 access_layer,
2588 manifest_ctx: manifest_ctx.clone(),
2589 file_purger: crate::test_util::new_noop_file_purger(),
2590 provider: Provider::noop_provider(),
2591 last_flush_millis: Default::default(),
2592 last_schedule_compaction_millis: Default::default(),
2593 time_provider: Arc::new(StdTimeProvider),
2594 topic_latest_entry_id: Default::default(),
2595 region_stats: RegionStats::new(),
2596 stats: ManifestStats::default(),
2597 };
2598
2599 assert_eq!(
2601 region.state(),
2602 RegionRoleState::Leader(RegionLeaderState::Writable)
2603 );
2604 assert!(!region.is_staging());
2605
2606 let mut manager = manifest_ctx.manifest_manager.write().await;
2608 region.set_staging(&mut manager).await.unwrap();
2609 drop(manager);
2610 assert_eq!(
2611 region.state(),
2612 RegionRoleState::Leader(RegionLeaderState::Staging)
2613 );
2614 assert!(region.is_staging());
2615
2616 region.exit_staging().unwrap();
2618 assert_eq!(
2619 region.state(),
2620 RegionRoleState::Leader(RegionLeaderState::Writable)
2621 );
2622 assert!(!region.is_staging());
2623
2624 {
2626 let manager = manifest_ctx.manifest_manager.write().await;
2628 let dummy_actions = RegionMetaActionList::new(vec![]);
2629 let dummy_bytes = dummy_actions.encode().unwrap();
2630
2631 manager.store().save(100, &dummy_bytes, true).await.unwrap();
2633 manager.store().save(101, &dummy_bytes, true).await.unwrap();
2634 drop(manager);
2635
2636 let manager = manifest_ctx.manifest_manager.read().await;
2638 let dirty_manifests = manager.store().fetch_staging_manifests().await.unwrap();
2639 assert_eq!(
2640 dirty_manifests.len(),
2641 2,
2642 "Should have 2 dirty staging files"
2643 );
2644 drop(manager);
2645
2646 let mut manager = manifest_ctx.manifest_manager.write().await;
2648 region.set_staging(&mut manager).await.unwrap();
2649 drop(manager);
2650
2651 let manager = manifest_ctx.manifest_manager.read().await;
2653 let cleaned_manifests = manager.store().fetch_staging_manifests().await.unwrap();
2654 assert_eq!(
2655 cleaned_manifests.len(),
2656 0,
2657 "Dirty staging files should be cleaned up"
2658 );
2659 drop(manager);
2660
2661 region.exit_staging().unwrap();
2663 }
2664
2665 let mut manager = manifest_ctx.manifest_manager.write().await;
2667 assert!(region.set_staging(&mut manager).await.is_ok()); drop(manager);
2669 let mut manager = manifest_ctx.manifest_manager.write().await;
2670 assert!(region.set_staging(&mut manager).await.is_err()); drop(manager);
2672 assert!(region.exit_staging().is_ok()); assert!(region.exit_staging().is_err()); }
2675}