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 _ => {
603 warn!(
604 "Cannot start downgrade for region {} from state {:?}",
605 self.region_id, current_state
606 );
607 }
608 }
609 None
610 }
611 };
612
613 if wait_for_checkpoint {
617 manager.wait_for_pending_checkpoint().await;
618 }
619
620 let mut backfill_hook_payload: Option<PendingManifestHook> = None;
622 if self.state() == RegionRoleState::Leader(RegionLeaderState::Writable) {
623 let manifest_meta = &manager.manifest().metadata;
625 let current_version = self.version();
626 let current_meta = ¤t_version.metadata;
627 if manifest_meta.partition_expr.is_none() && current_meta.partition_expr.is_some() {
628 let action = RegionMetaAction::Change(RegionChange {
629 metadata: current_meta.clone(),
630 sst_format: current_version.options.sst_format.unwrap_or_default(),
631 append_mode: None,
632 });
633 let action_list = RegionMetaActionList::with_action(action);
634 match self
635 .manifest_ctx
636 .update_locked(&mut manager, action_list, false)
637 .await
638 {
639 Ok(pending) => {
640 info!(
641 "Successfully persisted backfilled metadata for region {}, version: {}",
642 self.region_id,
643 pending.version()
644 );
645 backfill_hook_payload = Some(pending);
646 }
647 Err(e) => {
648 warn!(e; "Failed to persist backfilled metadata for region {}", self.region_id);
649 }
650 }
651 }
652 }
653
654 drop(manager);
655
656 let merged = match (hook_payload, backfill_hook_payload) {
659 (Some(staging), Some(backfill)) => Some(staging.merge(backfill)),
660 (Some(payload), None) => Some(payload),
661 (None, Some(payload)) => Some(payload),
662 (None, None) => None,
663 };
664
665 if let Some(pending) = merged {
666 pending.fire().await;
667 }
668
669 Ok(())
670 }
671
672 pub(crate) fn switch_state_to_writable(&self, expect: RegionLeaderState) {
675 if let Err(e) = self
676 .compare_exchange_state(expect, RegionRoleState::Leader(RegionLeaderState::Writable))
677 {
678 error!(e; "failed to switch region state to writable, expect state is {:?}", expect);
679 }
680 }
681
682 pub(crate) fn switch_state_to_staging(&self, expect: RegionLeaderState) {
685 if let Err(e) =
686 self.compare_exchange_state(expect, RegionRoleState::Leader(RegionLeaderState::Staging))
687 {
688 error!(e; "failed to switch region state to staging, expect state is {:?}", expect);
689 }
690 }
691
692 pub(crate) fn region_statistic(&self) -> RegionStatistic {
694 let version = self.version();
695 let memtables = &version.memtables;
696 let memtable_usage = (memtables.mutable_usage() + memtables.immutables_usage()) as u64;
697
698 let sst_usage = version.ssts.owned_sst_usage(self.region_id);
699 let index_usage = version.ssts.owned_index_usage(self.region_id);
700 let flushed_entry_id = version.flushed_entry_id;
701
702 let wal_usage = self.estimated_wal_usage(memtable_usage);
703 let manifest_usage = self.stats.total_manifest_size();
704 let num_rows = version.ssts.owned_num_rows(self.region_id) + version.memtables.num_rows();
705 let num_files = version.ssts.owned_num_files(self.region_id);
706 let manifest_version = self.stats.manifest_version();
707 let file_removed_cnt = self.stats.file_removed_cnt();
708
709 let time_range = match (version.ssts.time_range(), version.memtables.time_range()) {
710 (Some((sst_min, sst_max)), Some((mem_min, mem_max))) => {
711 Some((sst_min.min(mem_min), sst_max.max(mem_max)))
712 }
713 (range, None) | (None, range) => range,
714 };
715
716 let topic_latest_entry_id = self.topic_latest_entry_id.load(Ordering::Relaxed);
717 let written_bytes = self.region_stats.written_bytes.load(Ordering::Relaxed);
718 let query_cpu_time = self.region_stats.query_cpu_time.load(Ordering::Relaxed);
719 let query_scanned_bytes = self
720 .region_stats
721 .query_scanned_bytes
722 .load(Ordering::Relaxed);
723
724 RegionStatistic {
725 num_rows,
726 memtable_size: memtable_usage,
727 wal_size: wal_usage,
728 manifest_size: manifest_usage,
729 sst_size: sst_usage,
730 sst_num: num_files,
731 index_size: index_usage,
732 manifest: RegionManifestInfo::Mito {
733 manifest_version,
734 flushed_entry_id,
735 file_removed_cnt,
736 },
737 data_topic_latest_entry_id: topic_latest_entry_id,
738 metadata_topic_latest_entry_id: topic_latest_entry_id,
739 written_bytes,
740 query_cpu_time,
741 query_scanned_bytes,
742 min_timestamp: time_range.map(|(min, _)| min),
743 max_timestamp: time_range.map(|(_, max)| max),
744 }
745 }
746
747 fn estimated_wal_usage(&self, memtable_usage: u64) -> u64 {
750 ((memtable_usage as f32) * ESTIMATED_WAL_FACTOR) as u64
751 }
752
753 fn compare_exchange_state(
756 &self,
757 expect: RegionLeaderState,
758 state: RegionRoleState,
759 ) -> Result<()> {
760 self.manifest_ctx
761 .state
762 .compare_exchange(RegionRoleState::Leader(expect), state)
763 .map_err(|actual| {
764 RegionStateSnafu {
765 region_id: self.region_id,
766 state: actual,
767 expect: RegionRoleState::Leader(expect),
768 }
769 .build()
770 })?;
771 Ok(())
772 }
773
774 pub fn access_layer(&self) -> AccessLayerRef {
775 self.access_layer.clone()
776 }
777
778 pub(crate) fn region_info_entry(&self, node_id: Option<u64>) -> RegionInfoEntry {
780 let region_id = self.region_id;
781 let version = self.version();
782 let state = self.state();
783 let role = self.region_role();
784 let region_options = serde_json::to_string(&version.options)
785 .unwrap_or_else(|err| serde_json::json!({ "error": err.to_string() }).to_string());
786 let sst_format = match version.options.sst_format.unwrap_or_default() {
787 crate::sst::FormatType::PrimaryKey => "primary_key",
788 crate::sst::FormatType::Flat => "flat",
789 }
790 .to_string();
791
792 RegionInfoEntry {
793 region_id,
794 table_id: region_id.table_id(),
795 region_number: region_id.region_number(),
796 region_group: region_id.region_group(),
797 region_sequence: region_id.region_sequence(),
798 state: state.as_str().to_string(),
799 role: role.to_string(),
800 writable: self.is_writable(),
801 committed_sequence: self.find_committed_sequence(),
802 flushed_sequence: Some(self.flushed_sequence()).filter(|sequence| *sequence > 0),
803 manifest_version: self.stats.manifest_version(),
804 compaction_time_window: version
805 .compaction_time_window
806 .map(|duration| humantime::format_duration(duration).to_string()),
807 region_options,
808 sst_format,
809 node_id,
810 }
811 }
812
813 pub async fn manifest_sst_entries(&self) -> Vec<ManifestSstEntry> {
815 let table_dir = self.table_dir();
816 let path_type = self.access_layer.path_type();
817
818 let visible_ssts = self
819 .version()
820 .ssts
821 .levels()
822 .iter()
823 .flat_map(|level| level.files().map(|file| file.file_id().file_id()))
824 .collect::<HashSet<_>>();
825
826 let manifest_files = self.manifest_ctx.manifest().await.files.clone();
827 let staging_files = self
828 .manifest_ctx
829 .staging_manifest()
830 .await
831 .map(|m| m.files.clone())
832 .unwrap_or_default();
833 let files = manifest_files
834 .into_iter()
835 .chain(staging_files)
836 .collect::<HashMap<_, _>>();
837
838 files
839 .values()
840 .map(|meta| {
841 let region_id = self.region_id;
842 let origin_region_id = meta.region_id;
843 let (index_version, index_file_path, index_file_size) = if meta.index_file_size > 0
844 {
845 let index_file_path = index_file_path(table_dir, meta.index_id(), path_type);
846 (
847 meta.index_version,
848 Some(index_file_path),
849 Some(meta.index_file_size),
850 )
851 } else {
852 (0, None, None)
853 };
854 let visible = visible_ssts.contains(&meta.file_id);
855 ManifestSstEntry {
856 table_dir: table_dir.to_string(),
857 region_id,
858 table_id: region_id.table_id(),
859 region_number: region_id.region_number(),
860 region_group: region_id.region_group(),
861 region_sequence: region_id.region_sequence(),
862 file_id: meta.file_id.to_string(),
863 index_version,
864 level: meta.level,
865 file_path: sst_file_path(table_dir, meta.file_id(), path_type),
866 file_size: meta.file_size,
867 max_row_group_uncompressed_size: meta.max_row_group_uncompressed_size,
868 index_file_path,
869 index_file_size,
870 num_rows: meta.num_rows,
871 num_row_groups: meta.num_row_groups,
872 num_series: Some(meta.num_series),
873 min_ts: meta.time_range.0,
874 max_ts: meta.time_range.1,
875 sequence: meta.sequence.map(|s| s.get()),
876 partition_expr: meta.partition_expr.as_ref().map(ToString::to_string),
877 origin_region_id,
878 node_id: None,
879 visible,
880 primary_key_min: meta.primary_key_min.clone(),
881 primary_key_max: meta.primary_key_max.clone(),
882 }
883 })
884 .collect()
885 }
886
887 pub async fn file_metas(&self, file_ids: &[FileId]) -> Vec<Option<FileMeta>> {
889 let manifest_files = self.manifest_ctx.manifest().await.files.clone();
890
891 file_ids
892 .iter()
893 .map(|file_id| manifest_files.get(file_id).cloned())
894 .collect::<Vec<_>>()
895 }
896
897 pub async fn all_manifest_files(&self) -> (Vec<FileMeta>, ManifestVersion) {
907 let manifest = self.manifest_ctx.manifest().await;
908 let staging = self
909 .manifest_ctx
910 .staging_manifest()
911 .await
912 .map(|m| (m.files.clone(), m.manifest_version));
913
914 let version = staging
915 .as_ref()
916 .map(|(_, v)| *v)
917 .unwrap_or(manifest.manifest_version);
918
919 let files = match staging {
920 Some((staging_files, _)) => {
921 let merged = manifest
922 .files
923 .clone()
924 .into_iter()
925 .chain(staging_files)
926 .collect::<std::collections::HashMap<_, _>>();
927 merged.into_values().collect()
928 }
929 None => manifest.files.values().cloned().collect(),
930 };
931
932 (files, version)
933 }
934
935 pub(crate) async fn exit_staging_on_success(
945 &self,
946 manager: &mut RwLockWriteGuard<'_, RegionManifestManager>,
947 ) -> Result<Option<PendingManifestHook>> {
948 let current_state = self.manifest_ctx.current_state();
949 ensure!(
950 current_state == RegionRoleState::Leader(RegionLeaderState::Staging),
951 RegionStateSnafu {
952 region_id: self.region_id,
953 state: current_state,
954 expect: RegionRoleState::Leader(RegionLeaderState::Staging),
955 }
956 );
957
958 let merged_actions = match manager.merge_staged_actions(current_state).await? {
960 Some(actions) => actions,
961 None => {
962 info!(
963 "No staged manifests to merge for region {}, exiting staging mode without changes",
964 self.region_id
965 );
966 self.exit_staging()?;
968 return Ok(None);
969 }
970 };
971 let expect_change = merged_actions.actions.iter().any(|a| a.is_change());
972 let expect_partition_expr_change = merged_actions
973 .actions
974 .iter()
975 .any(|a| a.is_partition_expr_change());
976 let expect_edit = merged_actions.actions.iter().any(|a| a.is_edit());
977 ensure!(
978 !(expect_change && expect_partition_expr_change),
979 UnexpectedSnafu {
980 reason: "unexpected both change and partition expr change actions in merged actions"
981 }
982 );
983 ensure!(
984 expect_change || expect_partition_expr_change,
985 UnexpectedSnafu {
986 reason: "expect a change or partition expr change action in merged actions"
987 }
988 );
989 ensure!(
990 expect_edit,
991 UnexpectedSnafu {
992 reason: "expect an edit action in merged actions"
993 }
994 );
995
996 let (merged_partition_expr_change, merged_change, merged_edit) =
997 merged_actions.clone().split_region_change_and_edit();
998 if let Some(change) = &merged_change {
999 let current_column_metadatas = &self.version().metadata.column_metadatas;
1003 ensure!(
1004 change.metadata.column_metadatas == *current_column_metadatas,
1005 UnexpectedSnafu {
1006 reason: "change action alters column metadata in staging exit"
1007 }
1008 );
1009 }
1010
1011 let pending = self
1014 .manifest_ctx
1015 .update_locked(manager, merged_actions, false)
1016 .await?;
1017 let new_version = pending.version();
1018 info!(
1019 "Successfully submitted merged staged manifests for region {}, new version: {}",
1020 self.region_id, new_version
1021 );
1022
1023 if let Some(change) = merged_partition_expr_change {
1025 let mut new_metadata = self.version().metadata.as_ref().clone();
1026 new_metadata.set_partition_expr(change.partition_expr);
1027 self.version_control.alter_metadata(new_metadata.into());
1028 }
1029 if let Some(change) = merged_change {
1030 self.version_control.alter_metadata(change.metadata);
1031 }
1032 self.version_control
1033 .apply_edit(Some(merged_edit), &[], self.file_purger.clone());
1034
1035 if let Err(e) = manager.clear_staging_manifest_and_dir().await {
1037 error!(e; "Failed to clear staging manifest dir for region {}", self.region_id);
1038 }
1039 self.exit_staging()?;
1040
1041 Ok(Some(pending))
1043 }
1044
1045 pub fn maybe_staging_partition_expr_str(&self) -> Option<String> {
1051 let is_staging = self.is_staging();
1052 if is_staging {
1053 let staging_partition_info = self.manifest_ctx.staging_partition_info();
1054 if staging_partition_info.is_none() {
1055 warn!(
1056 "Staging partition expr is none for region {} in staging state",
1057 self.region_id
1058 );
1059 }
1060 staging_partition_info
1061 .as_ref()
1062 .and_then(|info| info.partition_expr().map(ToString::to_string))
1063 } else {
1064 let version = self.version();
1065 version.metadata.partition_expr.clone()
1066 }
1067 }
1068
1069 pub fn expected_partition_expr_version(&self) -> u64 {
1070 if self.is_staging() {
1071 self.manifest_ctx
1072 .staging_partition_info()
1073 .as_ref()
1074 .map(|info| info.partition_rule_version)
1075 .unwrap_or_default()
1076 } else {
1077 self.version().metadata.partition_expr_version
1078 }
1079 }
1080
1081 pub(crate) fn reject_all_writes_in_staging(&self) -> bool {
1083 if !self.is_staging() {
1084 return false;
1085 }
1086 self.manifest_ctx
1087 .staging_partition_info()
1088 .as_ref()
1089 .map(|info| {
1090 matches!(
1091 info.partition_directive,
1092 StagingPartitionDirective::RejectAllWrites
1093 )
1094 })
1095 .unwrap_or(false)
1096 }
1097}
1098
1099impl Drop for MitoRegion {
1100 fn drop(&mut self) {
1101 self.remove_region_metrics();
1102 }
1103}
1104
1105#[derive(Debug)]
1107pub(crate) enum IndexPublication {
1108 Committed {
1110 manifest_version: ManifestVersion,
1111 file_meta: FileMeta,
1112 },
1113 Stale(IndexPublicationStale),
1115}
1116
1117#[derive(Debug, PartialEq, Eq)]
1119pub(crate) enum IndexPublicationStale {
1120 SourceChanged,
1122 SchemaChanged,
1124}
1125
1126#[derive(Clone, Debug, PartialEq, Eq)]
1132pub(crate) struct IndexBuildSource {
1133 pub(crate) file_meta: FileMeta,
1134 pub(crate) schema_version: u64,
1135}
1136
1137impl IndexBuildSource {
1138 pub(crate) fn new(file_meta: FileMeta, schema_version: u64) -> Self {
1139 Self {
1140 file_meta,
1141 schema_version,
1142 }
1143 }
1144}
1145
1146#[derive(Debug)]
1148pub(crate) struct ManifestContext {
1149 pub(crate) manifest_manager: tokio::sync::RwLock<RegionManifestManager>,
1153 state: AtomicCell<RegionRoleState>,
1156 staging_partition_info: Mutex<Option<StagingPartitionInfo>>,
1161 hook: Option<RegionHookRef>,
1163}
1164
1165impl ManifestContext {
1166 pub(crate) fn new(
1167 manager: RegionManifestManager,
1168 state: RegionRoleState,
1169 hook: Option<RegionHookRef>,
1170 ) -> Self {
1171 ManifestContext {
1172 manifest_manager: tokio::sync::RwLock::new(manager),
1173 state: AtomicCell::new(state),
1174 staging_partition_info: Mutex::new(None),
1175 hook,
1176 }
1177 }
1178
1179 pub(crate) fn hook(&self) -> Option<RegionHookRef> {
1181 self.hook.clone()
1182 }
1183
1184 pub(crate) fn staging_partition_info(&self) -> Option<StagingPartitionInfo> {
1185 self.staging_partition_info.lock().unwrap().clone()
1186 }
1187
1188 pub(crate) fn set_staging_partition_info(&self, staging_partition_info: StagingPartitionInfo) {
1189 let mut current = self.staging_partition_info.lock().unwrap();
1190 debug_assert!(current.is_none());
1191 *current = Some(staging_partition_info);
1192 }
1193
1194 fn clear_staging_partition_info(&self) {
1195 *self.staging_partition_info.lock().unwrap() = None;
1196 }
1197
1198 pub(crate) fn exit_staging(
1199 &self,
1200 region_id: RegionId,
1201 next_state: RegionRoleState,
1202 ) -> Result<()> {
1203 self.state
1204 .compare_exchange(
1205 RegionRoleState::Leader(RegionLeaderState::Staging),
1206 next_state,
1207 )
1208 .map_err(|actual| {
1209 RegionStateSnafu {
1210 region_id,
1211 state: actual,
1212 expect: RegionRoleState::Leader(RegionLeaderState::Staging),
1213 }
1214 .build()
1215 })?;
1216 self.clear_staging_partition_info();
1217 Ok(())
1218 }
1219
1220 pub(crate) async fn manifest_version(&self) -> ManifestVersion {
1221 self.manifest_manager
1222 .read()
1223 .await
1224 .manifest()
1225 .manifest_version
1226 }
1227
1228 pub(crate) async fn has_update(&self) -> Result<bool> {
1229 self.manifest_manager.read().await.has_update().await
1230 }
1231
1232 pub(crate) fn current_state(&self) -> RegionRoleState {
1234 self.state.load()
1235 }
1236
1237 pub(crate) async fn install_manifest_to(
1243 &self,
1244 version: ManifestVersion,
1245 ) -> Result<Arc<RegionManifest>> {
1246 let mut manager = self.manifest_manager.write().await;
1247 manager.install_manifest_to(version).await?;
1248
1249 Ok(manager.manifest())
1250 }
1251
1252 pub(crate) async fn update_manifest(
1254 &self,
1255 expect_state: RegionLeaderState,
1256 action_list: RegionMetaActionList,
1257 is_staging: bool,
1258 ) -> Result<ManifestVersion> {
1259 self.update_manifest_with_state_check(action_list, is_staging, |current_state, region_id| {
1260 if expect_state != RegionLeaderState::Downgrading {
1265 if current_state == RegionRoleState::Leader(RegionLeaderState::Downgrading) {
1266 info!(
1267 "Region {} is in downgrading leader state, updating manifest. Expect state is {:?}",
1268 region_id, expect_state
1269 );
1270 }
1271 ensure!(
1272 current_state == RegionRoleState::Leader(expect_state)
1273 || current_state == RegionRoleState::Leader(RegionLeaderState::Downgrading),
1274 UpdateManifestSnafu {
1275 region_id,
1276 state: current_state,
1277 }
1278 );
1279 } else {
1280 ensure!(
1281 current_state == RegionRoleState::Leader(expect_state),
1282 RegionStateSnafu {
1283 region_id,
1284 state: current_state,
1285 expect: RegionRoleState::Leader(expect_state),
1286 }
1287 );
1288 }
1289
1290 Ok(())
1291 })
1292 .await
1293 }
1294
1295 pub(crate) async fn update_manifest_for_compaction(
1312 &self,
1313 action_list: RegionMetaActionList,
1314 ) -> Result<ManifestVersion> {
1315 self.update_manifest_with_state_check(action_list, false, |current_state, region_id| {
1316 ensure!(
1317 matches!(
1318 current_state,
1319 RegionRoleState::Leader(RegionLeaderState::Writable)
1320 | RegionRoleState::Leader(RegionLeaderState::Editing)
1321 | RegionRoleState::Leader(RegionLeaderState::Downgrading)
1322 ),
1323 UpdateManifestSnafu {
1324 region_id,
1325 state: current_state,
1326 }
1327 );
1328
1329 Ok(())
1330 })
1331 .await
1332 }
1333
1334 pub(crate) async fn update_manifest_for_index(
1340 &self,
1341 source: &IndexBuildSource,
1342 updated: FileMeta,
1343 ) -> Result<IndexPublication> {
1344 let manager = self.manifest_manager.write().await;
1345 let manifest = manager.manifest();
1346 let current_state = self.state.load();
1347 if !matches!(
1348 current_state,
1349 RegionRoleState::Leader(RegionLeaderState::Writable)
1350 | RegionRoleState::Leader(RegionLeaderState::Downgrading)
1351 ) || manager.is_stopped()
1352 {
1353 return Ok(IndexPublication::Stale(
1354 IndexPublicationStale::SourceChanged,
1355 ));
1356 }
1357
1358 if manifest.files.get(&source.file_meta.file_id) != Some(&source.file_meta) {
1359 return Ok(IndexPublication::Stale(
1360 IndexPublicationStale::SourceChanged,
1361 ));
1362 }
1363
1364 if manifest.metadata.schema_version != source.schema_version {
1365 return Ok(IndexPublication::Stale(
1366 IndexPublicationStale::SchemaChanged,
1367 ));
1368 }
1369
1370 let mut committed = source.file_meta.clone();
1372 committed.available_indexes = updated.available_indexes;
1373 committed.indexes = updated.indexes;
1374 committed.index_file_size = updated.index_file_size;
1375 committed.index_version = updated.index_version;
1376
1377 let edit = crate::manifest::action::RegionEdit {
1378 files_to_add: vec![committed.clone()],
1379 files_to_remove: Vec::new(),
1380 timestamp_ms: Some(chrono::Utc::now().timestamp_millis()),
1381 flushed_sequence: None,
1382 flushed_entry_id: None,
1383 committed_sequence: None,
1384 compaction_time_window: None,
1385 };
1386 let action_list = RegionMetaActionList::with_action(RegionMetaAction::Edit(edit));
1387 let manifest_version = self.update_and_fire(manager, action_list, false).await?;
1388
1389 Ok(IndexPublication::Committed {
1390 manifest_version,
1391 file_meta: committed,
1392 })
1393 }
1394
1395 pub(crate) async fn update_locked(
1405 &self,
1406 manager: &mut RegionManifestManager,
1407 action_list: RegionMetaActionList,
1408 is_staging: bool,
1409 ) -> Result<PendingManifestHook> {
1410 let region_id = manager.manifest().metadata.region_id;
1411 let action_list_for_hook = self.hook.as_ref().map(|_| action_list.clone());
1414 let version = if !is_staging
1415 && self.state.load() == RegionRoleState::Leader(RegionLeaderState::Downgrading)
1416 {
1417 manager.update_normal_without_checkpoint(action_list).await
1418 } else {
1419 manager.update(action_list, is_staging).await
1420 }
1421 .inspect_err(|e| error!(e; "Failed to update manifest, region_id: {}", region_id))?;
1422
1423 Ok(PendingManifestHook::new(
1424 region_id,
1425 action_list_for_hook,
1426 version,
1427 self.hook.clone(),
1428 is_staging,
1429 ))
1430 }
1431
1432 async fn update_and_fire(
1438 &self,
1439 mut manager: RwLockWriteGuard<'_, RegionManifestManager>,
1440 action_list: RegionMetaActionList,
1441 is_staging: bool,
1442 ) -> Result<ManifestVersion> {
1443 let region_id = manager.manifest().metadata.region_id;
1444 let pending = self
1445 .update_locked(&mut manager, action_list, is_staging)
1446 .await?;
1447 let version = pending.version();
1448
1449 drop(manager);
1452
1453 if self.state.load() == RegionRoleState::Follower {
1454 warn!(
1455 "Region {} becomes follower while updating manifest which may cause inconsistency, manifest version: {version}",
1456 region_id
1457 );
1458 }
1459
1460 pending.fire().await;
1461 Ok(version)
1462 }
1463
1464 async fn update_manifest_with_state_check(
1465 &self,
1466 action_list: RegionMetaActionList,
1467 is_staging: bool,
1468 check_state: impl FnOnce(RegionRoleState, RegionId) -> Result<()>,
1469 ) -> Result<ManifestVersion> {
1470 let manager = self.manifest_manager.write().await;
1472 let manifest = manager.manifest();
1474 let current_state = self.state.load();
1477 check_state(current_state, manifest.metadata.region_id)?;
1478
1479 for action in &action_list.actions {
1480 let RegionMetaAction::Edit(edit) = &action else {
1482 continue;
1483 };
1484
1485 let Some(truncated_entry_id) = manifest.truncated_entry_id else {
1487 continue;
1488 };
1489
1490 if let Some(flushed_entry_id) = edit.flushed_entry_id {
1492 let is_newer_entry = truncated_entry_id < flushed_entry_id;
1502 let is_same_entry_with_newer_sequence = truncated_entry_id == flushed_entry_id
1503 && edit.flushed_sequence.is_some_and(|flushed_sequence| {
1504 manifest.flushed_sequence < flushed_sequence
1505 });
1506
1507 ensure!(
1508 is_newer_entry || is_same_entry_with_newer_sequence,
1509 RegionTruncatedSnafu {
1510 region_id: manifest.metadata.region_id,
1511 }
1512 );
1513 }
1514
1515 if !edit.files_to_remove.is_empty() {
1517 for file in &edit.files_to_remove {
1519 ensure!(
1520 manifest.files.contains_key(&file.file_id),
1521 RegionTruncatedSnafu {
1522 region_id: manifest.metadata.region_id,
1523 }
1524 );
1525 }
1526 }
1527 }
1528
1529 self.update_and_fire(manager, action_list, is_staging).await
1530 }
1531
1532 pub(crate) fn set_role(&self, next_role: RegionRole, region_id: RegionId) {
1566 match next_role {
1567 RegionRole::Follower => {
1568 if self
1569 .exit_staging(region_id, RegionRoleState::Follower)
1570 .is_ok()
1571 {
1572 info!(
1573 "Convert region {} to follower, previous role state: {:?}",
1574 region_id,
1575 RegionRoleState::Leader(RegionLeaderState::Staging)
1576 );
1577 return;
1578 }
1579 match self.state.fetch_update(|state| {
1580 if !matches!(state, RegionRoleState::Follower) {
1581 Some(RegionRoleState::Follower)
1582 } else {
1583 None
1584 }
1585 }) {
1586 Ok(state) => info!(
1587 "Convert region {} to follower, previous role state: {:?}",
1588 region_id, state
1589 ),
1590 Err(state) => {
1591 if state != RegionRoleState::Follower {
1592 warn!(
1593 "Failed to convert region {} to follower, current role state: {:?}",
1594 region_id, state
1595 )
1596 }
1597 }
1598 }
1599 }
1600 RegionRole::Leader => {
1601 if self
1602 .exit_staging(
1603 region_id,
1604 RegionRoleState::Leader(RegionLeaderState::Writable),
1605 )
1606 .is_ok()
1607 {
1608 info!(
1609 "Convert region {} to leader, previous role state: {:?}",
1610 region_id,
1611 RegionRoleState::Leader(RegionLeaderState::Staging)
1612 );
1613 return;
1614 }
1615 match self.state.fetch_update(|state| {
1616 if matches!(
1617 state,
1618 RegionRoleState::Follower
1619 | RegionRoleState::Leader(RegionLeaderState::Downgrading)
1620 ) {
1621 Some(RegionRoleState::Leader(RegionLeaderState::Writable))
1622 } else {
1623 None
1624 }
1625 }) {
1626 Ok(state) => info!(
1627 "Convert region {} to leader, previous role state: {:?}",
1628 region_id, state
1629 ),
1630 Err(state) => {
1631 if state != RegionRoleState::Leader(RegionLeaderState::Writable) {
1632 warn!(
1633 "Failed to convert region {} to leader, current role state: {:?}",
1634 region_id, state
1635 )
1636 }
1637 }
1638 }
1639 }
1640 RegionRole::StagingLeader => {
1641 info!(
1642 "Ignore direct conversion of region {} to staging leader; staging requires the dedicated workflow",
1643 region_id
1644 );
1645 }
1646 RegionRole::DowngradingLeader => {
1647 if self
1648 .exit_staging(
1649 region_id,
1650 RegionRoleState::Leader(RegionLeaderState::Downgrading),
1651 )
1652 .is_ok()
1653 {
1654 info!(
1655 "Convert region {} to downgrading region, previous role state: {:?}",
1656 region_id,
1657 RegionRoleState::Leader(RegionLeaderState::Staging)
1658 );
1659 return;
1660 }
1661 match self.state.compare_exchange(
1662 RegionRoleState::Leader(RegionLeaderState::Writable),
1663 RegionRoleState::Leader(RegionLeaderState::Downgrading),
1664 ) {
1665 Ok(state) => info!(
1666 "Convert region {} to downgrading region, previous role state: {:?}",
1667 region_id, state
1668 ),
1669 Err(state) => {
1670 if state != RegionRoleState::Leader(RegionLeaderState::Downgrading) {
1671 warn!(
1672 "Failed to convert region {} to downgrading leader, current role state: {:?}",
1673 region_id, state
1674 )
1675 }
1676 }
1677 }
1678 }
1679 }
1680 }
1681
1682 pub(crate) async fn manifest(&self) -> Arc<crate::manifest::action::RegionManifest> {
1684 self.manifest_manager.read().await.manifest()
1685 }
1686
1687 pub(crate) async fn staging_manifest(
1689 &self,
1690 ) -> Option<Arc<crate::manifest::action::RegionManifest>> {
1691 self.manifest_manager.read().await.staging_manifest()
1692 }
1693}
1694
1695pub(crate) type ManifestContextRef = Arc<ManifestContext>;
1696
1697#[derive(Debug, Default)]
1699pub(crate) struct RegionMap {
1700 regions: RwLock<HashMap<RegionId, MitoRegionRef>>,
1701}
1702
1703impl RegionMap {
1704 pub(crate) fn is_region_exists(&self, region_id: RegionId) -> bool {
1706 let regions = self.regions.read().unwrap();
1707 regions.contains_key(®ion_id)
1708 }
1709
1710 pub(crate) fn insert_region(&self, region: MitoRegionRef) {
1712 let mut regions = self.regions.write().unwrap();
1713 regions.insert(region.region_id, region);
1714 }
1715
1716 pub(crate) fn get_region(&self, region_id: RegionId) -> Option<MitoRegionRef> {
1718 let regions = self.regions.read().unwrap();
1719 regions.get(®ion_id).cloned()
1720 }
1721
1722 pub(crate) fn writable_region(&self, region_id: RegionId) -> Result<MitoRegionRef> {
1726 let region = self
1727 .get_region(region_id)
1728 .context(RegionNotFoundSnafu { region_id })?;
1729 ensure!(
1730 region.is_writable(),
1731 RegionStateSnafu {
1732 region_id,
1733 state: region.state(),
1734 expect: RegionRoleState::Leader(RegionLeaderState::Writable),
1735 }
1736 );
1737 Ok(region)
1738 }
1739
1740 pub(crate) fn follower_region(&self, region_id: RegionId) -> Result<MitoRegionRef> {
1744 let region = self
1745 .get_region(region_id)
1746 .context(RegionNotFoundSnafu { region_id })?;
1747 ensure!(
1748 region.is_follower(),
1749 RegionStateSnafu {
1750 region_id,
1751 state: region.state(),
1752 expect: RegionRoleState::Follower,
1753 }
1754 );
1755
1756 Ok(region)
1757 }
1758
1759 pub(crate) fn get_region_or<F: OnFailure>(
1763 &self,
1764 region_id: RegionId,
1765 cb: &mut F,
1766 ) -> Option<MitoRegionRef> {
1767 match self
1768 .get_region(region_id)
1769 .context(RegionNotFoundSnafu { region_id })
1770 {
1771 Ok(region) => Some(region),
1772 Err(e) => {
1773 cb.on_failure(e);
1774 None
1775 }
1776 }
1777 }
1778
1779 pub(crate) fn writable_region_or<F: OnFailure>(
1783 &self,
1784 region_id: RegionId,
1785 cb: &mut F,
1786 ) -> Option<MitoRegionRef> {
1787 match self.writable_region(region_id) {
1788 Ok(region) => Some(region),
1789 Err(e) => {
1790 cb.on_failure(e);
1791 None
1792 }
1793 }
1794 }
1795
1796 pub(crate) fn writable_non_staging_region(&self, region_id: RegionId) -> Result<MitoRegionRef> {
1800 let region = self.writable_region(region_id)?;
1801 if region.is_staging() {
1802 return Err(crate::error::RegionStateSnafu {
1803 region_id,
1804 state: region.state(),
1805 expect: RegionRoleState::Leader(RegionLeaderState::Writable),
1806 }
1807 .build());
1808 }
1809 Ok(region)
1810 }
1811
1812 pub(crate) fn staging_region(&self, region_id: RegionId) -> Result<MitoRegionRef> {
1816 let region = self
1817 .get_region(region_id)
1818 .context(RegionNotFoundSnafu { region_id })?;
1819 ensure!(
1820 region.is_staging(),
1821 RegionStateSnafu {
1822 region_id,
1823 state: region.state(),
1824 expect: RegionRoleState::Leader(RegionLeaderState::Staging),
1825 }
1826 );
1827 Ok(region)
1828 }
1829
1830 pub(crate) fn flushable_region(&self, region_id: RegionId) -> Result<MitoRegionRef> {
1834 let region = self
1835 .get_region(region_id)
1836 .context(RegionNotFoundSnafu { region_id })?;
1837 ensure!(
1838 region.is_flushable(),
1839 FlushableRegionStateSnafu {
1840 region_id,
1841 state: region.state(),
1842 }
1843 );
1844 Ok(region)
1845 }
1846
1847 pub(crate) fn remove_region(&self, region_id: RegionId) -> Option<MitoRegionRef> {
1849 let mut regions = self.regions.write().unwrap();
1850 regions.remove(®ion_id)
1851 }
1852
1853 pub(crate) fn list_regions(&self) -> Vec<MitoRegionRef> {
1855 let regions = self.regions.read().unwrap();
1856 regions.values().cloned().collect()
1857 }
1858
1859 pub(crate) fn clear(&self) {
1861 self.regions.write().unwrap().clear();
1862 }
1863}
1864
1865pub(crate) type RegionMapRef = Arc<RegionMap>;
1866
1867#[derive(Debug, Default)]
1869pub(crate) struct OpeningRegions {
1870 regions: RwLock<HashMap<RegionId, Vec<OptionOutputTx>>>,
1871}
1872
1873impl OpeningRegions {
1874 pub(crate) fn wait_for_opening_region(
1876 &self,
1877 region_id: RegionId,
1878 sender: OptionOutputTx,
1879 ) -> Option<OptionOutputTx> {
1880 let mut regions = self.regions.write().unwrap();
1881 match regions.entry(region_id) {
1882 Entry::Occupied(mut senders) => {
1883 senders.get_mut().push(sender);
1884 None
1885 }
1886 Entry::Vacant(_) => Some(sender),
1887 }
1888 }
1889
1890 pub(crate) fn is_region_exists(&self, region_id: RegionId) -> bool {
1892 let regions = self.regions.read().unwrap();
1893 regions.contains_key(®ion_id)
1894 }
1895
1896 pub(crate) fn insert_sender(&self, region: RegionId, sender: OptionOutputTx) {
1898 let mut regions = self.regions.write().unwrap();
1899 regions.insert(region, vec![sender]);
1900 }
1901
1902 pub(crate) fn remove_sender(&self, region_id: RegionId) -> Vec<OptionOutputTx> {
1904 let mut regions = self.regions.write().unwrap();
1905 regions.remove(®ion_id).unwrap_or_default()
1906 }
1907
1908 #[cfg(test)]
1909 pub(crate) fn sender_len(&self, region_id: RegionId) -> usize {
1910 let regions = self.regions.read().unwrap();
1911 if let Some(senders) = regions.get(®ion_id) {
1912 senders.len()
1913 } else {
1914 0
1915 }
1916 }
1917}
1918
1919pub(crate) type OpeningRegionsRef = Arc<OpeningRegions>;
1920
1921#[derive(Debug, Default)]
1923pub(crate) struct CatchupRegions {
1924 regions: RwLock<HashSet<RegionId>>,
1925}
1926
1927impl CatchupRegions {
1928 pub(crate) fn is_region_exists(&self, region_id: RegionId) -> bool {
1930 let regions = self.regions.read().unwrap();
1931 regions.contains(®ion_id)
1932 }
1933
1934 pub(crate) fn insert_region(&self, region_id: RegionId) {
1936 let mut regions = self.regions.write().unwrap();
1937 regions.insert(region_id);
1938 }
1939
1940 pub(crate) fn remove_region(&self, region_id: RegionId) {
1942 let mut regions = self.regions.write().unwrap();
1943 regions.remove(®ion_id);
1944 }
1945}
1946
1947pub(crate) type CatchupRegionsRef = Arc<CatchupRegions>;
1948
1949#[derive(Default, Debug, Clone)]
1951pub struct ManifestStats {
1952 pub(crate) total_manifest_size: Arc<AtomicU64>,
1953 pub(crate) manifest_version: Arc<AtomicU64>,
1954 pub(crate) file_removed_cnt: Arc<AtomicU64>,
1955}
1956
1957impl ManifestStats {
1958 fn total_manifest_size(&self) -> u64 {
1959 self.total_manifest_size.load(Ordering::Relaxed)
1960 }
1961
1962 fn manifest_version(&self) -> u64 {
1963 self.manifest_version.load(Ordering::Relaxed)
1964 }
1965
1966 fn file_removed_cnt(&self) -> u64 {
1967 self.file_removed_cnt.load(Ordering::Relaxed)
1968 }
1969}
1970
1971pub fn parse_partition_expr(partition_expr_str: Option<&str>) -> Result<Option<PartitionExpr>> {
1973 match partition_expr_str {
1974 None => Ok(None),
1975 Some("") => Ok(None),
1976 Some(json_str) => {
1977 let expr = partition::expr::PartitionExpr::from_json_str(json_str)
1978 .with_context(|_| InvalidPartitionExprSnafu { expr: json_str })?;
1979 Ok(expr)
1980 }
1981 }
1982}
1983
1984#[cfg(test)]
1985mod tests {
1986 use std::sync::Arc;
1987
1988 use common_datasource::compression::CompressionType;
1989 use common_test_util::temp_dir::create_temp_dir;
1990 use crossbeam_utils::atomic::AtomicCell;
1991 use object_store::ObjectStore;
1992 use object_store::services::Fs;
1993 use store_api::logstore::provider::Provider;
1994 use store_api::region_engine::RegionRole;
1995 use store_api::region_request::PathType;
1996 use store_api::storage::{FileId, RegionId};
1997
1998 use crate::access_layer::AccessLayer;
1999 use crate::error::Error;
2000 use crate::manifest::action::{
2001 RegionChange, RegionEdit, RegionMetaAction, RegionMetaActionList, RegionPartitionExprChange,
2002 };
2003 use crate::manifest::manager::{RegionManifestManager, RegionManifestOptions};
2004 use crate::region::{
2005 IndexBuildSource, IndexPublication, IndexPublicationStale, ManifestContext, ManifestStats,
2006 MitoRegion, RegionLeaderState, RegionRoleState, RegionStats,
2007 };
2008 use crate::sst::FormatType;
2009 use crate::sst::index::intermediate::IntermediateManager;
2010 use crate::sst::index::puffin_manager::PuffinManagerFactory;
2011 use crate::test_util::scheduler_util::SchedulerEnv;
2012 use crate::test_util::version_util::VersionControlBuilder;
2013 use crate::time_provider::StdTimeProvider;
2014
2015 #[test]
2016 fn test_region_state_lock_free() {
2017 assert!(AtomicCell::<RegionRoleState>::is_lock_free());
2018 }
2019
2020 #[test]
2021 fn test_region_role_state_as_str() {
2022 assert_eq!("Follower", RegionRoleState::Follower.as_str());
2023 assert_eq!(
2024 "Leader(Writable)",
2025 RegionRoleState::Leader(RegionLeaderState::Writable).as_str()
2026 );
2027 assert_eq!(
2028 "Leader(Staging)",
2029 RegionRoleState::Leader(RegionLeaderState::Staging).as_str()
2030 );
2031 assert_eq!(
2032 "Leader(Downgrading)",
2033 RegionRoleState::Leader(RegionLeaderState::Downgrading).as_str()
2034 );
2035 }
2036
2037 async fn build_test_region(env: &SchedulerEnv) -> MitoRegion {
2038 let builder = VersionControlBuilder::new();
2039 let version_control = Arc::new(builder.build());
2040 let metadata = version_control.current().version.metadata.clone();
2041
2042 let manager = RegionManifestManager::new(
2043 metadata.clone(),
2044 0,
2045 RegionManifestOptions {
2046 manifest_dir: "".to_string(),
2047 object_store: env.access_layer.object_store().clone(),
2048 compress_type: CompressionType::Uncompressed,
2049 checkpoint_distance: 10,
2050 remove_file_options: Default::default(),
2051 manifest_cache: None,
2052 },
2053 FormatType::PrimaryKey,
2054 &Default::default(),
2055 )
2056 .await
2057 .unwrap();
2058
2059 let manifest_ctx = Arc::new(ManifestContext::new(
2060 manager,
2061 RegionRoleState::Leader(RegionLeaderState::Writable),
2062 None,
2063 ));
2064
2065 MitoRegion {
2066 region_id: metadata.region_id,
2067 version_control,
2068 series_index_version_control: Default::default(),
2069 series_index_store: None,
2070 access_layer: env.access_layer.clone(),
2071 manifest_ctx,
2072 file_purger: crate::test_util::new_noop_file_purger(),
2073 provider: Provider::noop_provider(),
2074 last_flush_millis: Default::default(),
2075 last_schedule_compaction_millis: Default::default(),
2076 time_provider: Arc::new(StdTimeProvider),
2077 topic_latest_entry_id: Default::default(),
2078 region_stats: RegionStats::new(),
2079 stats: ManifestStats::default(),
2080 }
2081 }
2082
2083 fn empty_edit() -> RegionEdit {
2084 RegionEdit {
2085 files_to_add: Vec::new(),
2086 files_to_remove: Vec::new(),
2087 timestamp_ms: None,
2088 compaction_time_window: None,
2089 flushed_entry_id: None,
2090 flushed_sequence: None,
2091 committed_sequence: None,
2092 }
2093 }
2094
2095 #[tokio::test]
2096 async fn test_compaction_update_manifest_allows_editing_state() {
2097 let env = SchedulerEnv::new().await;
2098 let region = build_test_region(&env).await;
2099 region.set_editing(RegionLeaderState::Writable).unwrap();
2100
2101 let file_id = FileId::random();
2102 let action_list = RegionMetaActionList::with_action(RegionMetaAction::Edit(RegionEdit {
2103 files_to_add: vec![crate::sst::file::FileMeta {
2104 region_id: region.region_id,
2105 file_id,
2106 level: 1,
2107 ..Default::default()
2108 }],
2109 files_to_remove: Vec::new(),
2110 timestamp_ms: None,
2111 compaction_time_window: None,
2112 flushed_entry_id: None,
2113 flushed_sequence: None,
2114 committed_sequence: None,
2115 }));
2116
2117 region
2118 .manifest_ctx
2119 .update_manifest_for_compaction(action_list)
2120 .await
2121 .unwrap();
2122
2123 assert!(
2124 region
2125 .manifest_ctx
2126 .manifest()
2127 .await
2128 .files
2129 .contains_key(&file_id)
2130 );
2131 }
2132
2133 #[tokio::test]
2134 async fn test_index_publication_rejects_older_source_generation() {
2135 let env = SchedulerEnv::new().await;
2136 let region = build_test_region(&env).await;
2137 let source = crate::sst::file::FileMeta {
2138 region_id: region.region_id,
2139 file_id: FileId::random(),
2140 level: 1,
2141 file_size: 1024,
2142 ..Default::default()
2143 };
2144 region
2145 .manifest_ctx
2146 .update_manifest(
2147 RegionLeaderState::Writable,
2148 RegionMetaActionList::with_action(RegionMetaAction::Edit(RegionEdit {
2149 files_to_add: vec![source.clone()],
2150 ..empty_edit()
2151 })),
2152 false,
2153 )
2154 .await
2155 .unwrap();
2156
2157 let mut first_update = source.clone();
2158 first_update.index_version = 1;
2159 first_update.index_file_size = 128;
2160 let schema_version = region.version().metadata.schema_version;
2161 let initial_source = IndexBuildSource::new(source.clone(), schema_version);
2162 let first_committed = match region
2163 .manifest_ctx
2164 .update_manifest_for_index(&initial_source, first_update)
2165 .await
2166 .unwrap()
2167 {
2168 IndexPublication::Committed { file_meta, .. } => file_meta,
2169 IndexPublication::Stale(_) => panic!("first index publication should commit"),
2170 };
2171
2172 let (ready_tx, ready_rx) = tokio::sync::oneshot::channel();
2173 let (release_tx, release_rx) = tokio::sync::oneshot::channel();
2174 let delayed_manifest_ctx = region.manifest_ctx.clone();
2175 let delayed_source = IndexBuildSource::new(first_committed.clone(), schema_version);
2176 let delayed_publication = tokio::spawn(async move {
2177 let mut delayed_update = delayed_source.file_meta.clone();
2178 delayed_update.index_version = 2;
2179 delayed_update.index_file_size = 128;
2180 ready_tx.send(()).unwrap();
2181 release_rx.await.unwrap();
2182 delayed_manifest_ctx
2183 .update_manifest_for_index(&delayed_source, delayed_update)
2184 .await
2185 });
2186 ready_rx.await.unwrap();
2187
2188 let mut newer_update = first_committed.clone();
2189 newer_update.index_version = 3;
2190 newer_update.index_file_size = 256;
2191 let newer_source = IndexBuildSource::new(first_committed, schema_version);
2192 let newer_committed = match region
2193 .manifest_ctx
2194 .update_manifest_for_index(&newer_source, newer_update)
2195 .await
2196 .unwrap()
2197 {
2198 IndexPublication::Committed { file_meta, .. } => file_meta,
2199 IndexPublication::Stale(_) => panic!("newer index publication should commit"),
2200 };
2201
2202 release_tx.send(()).unwrap();
2203 assert!(matches!(
2204 delayed_publication.await.unwrap().unwrap(),
2205 IndexPublication::Stale(IndexPublicationStale::SourceChanged)
2206 ));
2207 assert_eq!(
2208 region
2209 .manifest_ctx
2210 .manifest()
2211 .await
2212 .files
2213 .get(&source.file_id),
2214 Some(&newer_committed)
2215 );
2216 }
2217
2218 #[tokio::test]
2219 async fn test_index_publication_rejects_older_schema_generation() {
2220 let env = SchedulerEnv::new().await;
2221 let region = build_test_region(&env).await;
2222 let source_meta = crate::sst::file::FileMeta {
2223 region_id: region.region_id,
2224 file_id: FileId::random(),
2225 level: 1,
2226 file_size: 1024,
2227 ..Default::default()
2228 };
2229 region
2230 .manifest_ctx
2231 .update_manifest(
2232 RegionLeaderState::Writable,
2233 RegionMetaActionList::with_action(RegionMetaAction::Edit(RegionEdit {
2234 files_to_add: vec![source_meta.clone()],
2235 ..empty_edit()
2236 })),
2237 false,
2238 )
2239 .await
2240 .unwrap();
2241
2242 let old_schema_version = region.version().metadata.schema_version;
2243 let source = IndexBuildSource::new(source_meta.clone(), old_schema_version);
2244 let mut new_metadata = region.version().metadata.as_ref().clone();
2245 new_metadata.schema_version += 1;
2246 region
2247 .manifest_ctx
2248 .update_manifest(
2249 RegionLeaderState::Writable,
2250 RegionMetaActionList::with_action(RegionMetaAction::Change(RegionChange {
2251 metadata: Arc::new(new_metadata),
2252 sst_format: FormatType::PrimaryKey,
2253 append_mode: None,
2254 })),
2255 false,
2256 )
2257 .await
2258 .unwrap();
2259
2260 let mut updated = source_meta.clone();
2261 updated.index_version = 1;
2262 updated.index_file_size = 128;
2263 assert!(matches!(
2264 region
2265 .manifest_ctx
2266 .update_manifest_for_index(&source, updated)
2267 .await
2268 .unwrap(),
2269 IndexPublication::Stale(IndexPublicationStale::SchemaChanged)
2270 ));
2271
2272 let manifest = region.manifest_ctx.manifest().await;
2273 assert_eq!(manifest.metadata.schema_version, old_schema_version + 1);
2274 assert_eq!(manifest.files.get(&source_meta.file_id), Some(&source_meta));
2275 }
2276
2277 #[tokio::test]
2278 async fn test_exit_staging_partition_expr_change_and_edit_success() {
2279 let env = SchedulerEnv::new().await;
2280 let region = build_test_region(&env).await;
2281
2282 let mut manager = region.manifest_ctx.manifest_manager.write().await;
2283 region.set_staging(&mut manager).await.unwrap();
2284 manager
2285 .update(
2286 RegionMetaActionList::new(vec![
2287 RegionMetaAction::PartitionExprChange(RegionPartitionExprChange {
2288 partition_expr: Some("expr_a".to_string()),
2289 }),
2290 RegionMetaAction::Edit(empty_edit()),
2291 ]),
2292 true,
2293 )
2294 .await
2295 .unwrap();
2296
2297 let _hook_payload = region.exit_staging_on_success(&mut manager).await.unwrap();
2298 drop(manager);
2299
2300 assert_eq!(
2301 region.version().metadata.partition_expr.as_deref(),
2302 Some("expr_a")
2303 );
2304 assert_eq!(
2305 region.state(),
2306 RegionRoleState::Leader(RegionLeaderState::Writable)
2307 );
2308 }
2309
2310 #[tokio::test]
2311 async fn test_exit_staging_change_with_same_columns_success() {
2312 let env = SchedulerEnv::new().await;
2313 let region = build_test_region(&env).await;
2314
2315 let mut manager = region.manifest_ctx.manifest_manager.write().await;
2316 region.set_staging(&mut manager).await.unwrap();
2317
2318 let mut changed_metadata = region.version().metadata.as_ref().clone();
2319 changed_metadata.set_partition_expr(Some("expr_b".to_string()));
2320
2321 manager
2322 .update(
2323 RegionMetaActionList::new(vec![
2324 RegionMetaAction::Change(RegionChange {
2325 metadata: Arc::new(changed_metadata),
2326 sst_format: FormatType::PrimaryKey,
2327 append_mode: None,
2328 }),
2329 RegionMetaAction::Edit(empty_edit()),
2330 ]),
2331 true,
2332 )
2333 .await
2334 .unwrap();
2335
2336 let _hook_payload = region.exit_staging_on_success(&mut manager).await.unwrap();
2337 drop(manager);
2338
2339 assert_eq!(
2340 region.version().metadata.partition_expr.as_deref(),
2341 Some("expr_b")
2342 );
2343 assert_eq!(
2344 region.state(),
2345 RegionRoleState::Leader(RegionLeaderState::Writable)
2346 );
2347 }
2348
2349 #[tokio::test]
2350 async fn test_exit_staging_change_with_different_columns_fails() {
2351 let env = SchedulerEnv::new().await;
2352 let region = build_test_region(&env).await;
2353
2354 let mut manager = region.manifest_ctx.manifest_manager.write().await;
2355 region.set_staging(&mut manager).await.unwrap();
2356
2357 let mut changed_metadata = region.version().metadata.as_ref().clone();
2358 changed_metadata.column_metadatas.rotate_left(1);
2359
2360 manager
2361 .update(
2362 RegionMetaActionList::new(vec![
2363 RegionMetaAction::Change(RegionChange {
2364 metadata: Arc::new(changed_metadata),
2365 sst_format: FormatType::PrimaryKey,
2366 append_mode: None,
2367 }),
2368 RegionMetaAction::Edit(empty_edit()),
2369 ]),
2370 true,
2371 )
2372 .await
2373 .unwrap();
2374
2375 let result = region.exit_staging_on_success(&mut manager).await;
2376 assert!(matches!(result, Err(Error::Unexpected { .. })));
2377 }
2378
2379 #[tokio::test]
2380 async fn test_exit_staging_partition_expr_change_and_change_conflict_fails() {
2381 let env = SchedulerEnv::new().await;
2382 let region = build_test_region(&env).await;
2383
2384 let mut manager = region.manifest_ctx.manifest_manager.write().await;
2385 region.set_staging(&mut manager).await.unwrap();
2386
2387 let mut changed_metadata = region.version().metadata.as_ref().clone();
2388 changed_metadata.set_partition_expr(Some("expr_c".to_string()));
2389
2390 manager
2391 .update(
2392 RegionMetaActionList::new(vec![
2393 RegionMetaAction::PartitionExprChange(RegionPartitionExprChange {
2394 partition_expr: Some("expr_c".to_string()),
2395 }),
2396 RegionMetaAction::Change(RegionChange {
2397 metadata: Arc::new(changed_metadata),
2398 sst_format: FormatType::PrimaryKey,
2399 append_mode: None,
2400 }),
2401 RegionMetaAction::Edit(empty_edit()),
2402 ]),
2403 true,
2404 )
2405 .await
2406 .unwrap();
2407
2408 let result = region.exit_staging_on_success(&mut manager).await;
2409 assert!(matches!(result, Err(Error::Unexpected { .. })));
2410 }
2411
2412 #[tokio::test]
2413 async fn test_set_region_state() {
2414 let env = SchedulerEnv::new().await;
2415 let builder = VersionControlBuilder::new();
2416 let version_control = Arc::new(builder.build());
2417 let manifest_ctx = env
2418 .mock_manifest_context(version_control.current().version.metadata.clone())
2419 .await;
2420
2421 let region_id = RegionId::new(1024, 0);
2422 manifest_ctx.set_role(RegionRole::Follower, region_id);
2424 assert_eq!(manifest_ctx.state.load(), RegionRoleState::Follower);
2425
2426 manifest_ctx.set_role(RegionRole::Leader, region_id);
2428 assert_eq!(
2429 manifest_ctx.state.load(),
2430 RegionRoleState::Leader(RegionLeaderState::Writable)
2431 );
2432
2433 manifest_ctx.set_role(RegionRole::StagingLeader, region_id);
2435 assert_eq!(
2436 manifest_ctx.state.load(),
2437 RegionRoleState::Leader(RegionLeaderState::Writable)
2438 );
2439
2440 manifest_ctx.set_role(RegionRole::DowngradingLeader, region_id);
2442 assert_eq!(
2443 manifest_ctx.state.load(),
2444 RegionRoleState::Leader(RegionLeaderState::Downgrading)
2445 );
2446
2447 manifest_ctx.set_role(RegionRole::Follower, region_id);
2449 assert_eq!(manifest_ctx.state.load(), RegionRoleState::Follower);
2450
2451 manifest_ctx.set_role(RegionRole::DowngradingLeader, region_id);
2453 assert_eq!(manifest_ctx.state.load(), RegionRoleState::Follower);
2454
2455 manifest_ctx.set_role(RegionRole::Leader, region_id);
2457 manifest_ctx.set_role(RegionRole::DowngradingLeader, region_id);
2458 assert_eq!(
2459 manifest_ctx.state.load(),
2460 RegionRoleState::Leader(RegionLeaderState::Downgrading)
2461 );
2462
2463 manifest_ctx.set_role(RegionRole::Leader, region_id);
2465 assert_eq!(
2466 manifest_ctx.state.load(),
2467 RegionRoleState::Leader(RegionLeaderState::Writable)
2468 );
2469 }
2470
2471 #[tokio::test]
2472 async fn test_staging_state_validation() {
2473 let env = SchedulerEnv::new().await;
2474 let builder = VersionControlBuilder::new();
2475 let version_control = Arc::new(builder.build());
2476
2477 let staging_ctx = {
2479 let manager = RegionManifestManager::new(
2480 version_control.current().version.metadata.clone(),
2481 0,
2482 RegionManifestOptions {
2483 manifest_dir: "".to_string(),
2484 object_store: env.access_layer.object_store().clone(),
2485 compress_type: CompressionType::Uncompressed,
2486 checkpoint_distance: 10,
2487 remove_file_options: Default::default(),
2488 manifest_cache: None,
2489 },
2490 FormatType::PrimaryKey,
2491 &Default::default(),
2492 )
2493 .await
2494 .unwrap();
2495 Arc::new(ManifestContext::new(
2496 manager,
2497 RegionRoleState::Leader(RegionLeaderState::Staging),
2498 None,
2499 ))
2500 };
2501
2502 assert_eq!(
2504 staging_ctx.current_state(),
2505 RegionRoleState::Leader(RegionLeaderState::Staging)
2506 );
2507
2508 let writable_ctx = env
2510 .mock_manifest_context(version_control.current().version.metadata.clone())
2511 .await;
2512
2513 assert_eq!(
2514 writable_ctx.current_state(),
2515 RegionRoleState::Leader(RegionLeaderState::Writable)
2516 );
2517 }
2518
2519 #[tokio::test]
2520 async fn test_staging_state_transitions() {
2521 let builder = VersionControlBuilder::new();
2522 let version_control = Arc::new(builder.build());
2523 let metadata = version_control.current().version.metadata.clone();
2524
2525 let temp_dir = create_temp_dir("");
2527 let path_str = temp_dir.path().display().to_string();
2528 let fs_builder = Fs::default().root(&path_str);
2529 let object_store = ObjectStore::new(fs_builder).unwrap();
2530
2531 let index_aux_path = temp_dir.path().join("index_aux");
2532 let puffin_mgr = PuffinManagerFactory::new(&index_aux_path, 4096, None, None)
2533 .await
2534 .unwrap();
2535 let intm_mgr = IntermediateManager::init_fs(index_aux_path.to_str().unwrap())
2536 .await
2537 .unwrap();
2538
2539 let access_layer = Arc::new(AccessLayer::new(
2540 "",
2541 PathType::Bare,
2542 object_store,
2543 puffin_mgr,
2544 intm_mgr,
2545 ));
2546
2547 let manager = RegionManifestManager::new(
2548 metadata.clone(),
2549 0,
2550 RegionManifestOptions {
2551 manifest_dir: "".to_string(),
2552 object_store: access_layer.object_store().clone(),
2553 compress_type: CompressionType::Uncompressed,
2554 checkpoint_distance: 10,
2555 remove_file_options: Default::default(),
2556 manifest_cache: None,
2557 },
2558 FormatType::PrimaryKey,
2559 &Default::default(),
2560 )
2561 .await
2562 .unwrap();
2563
2564 let manifest_ctx = Arc::new(ManifestContext::new(
2565 manager,
2566 RegionRoleState::Leader(RegionLeaderState::Writable),
2567 None,
2568 ));
2569
2570 let region = MitoRegion {
2571 region_id: metadata.region_id,
2572 version_control,
2573 series_index_version_control: Default::default(),
2574 series_index_store: None,
2575 access_layer,
2576 manifest_ctx: manifest_ctx.clone(),
2577 file_purger: crate::test_util::new_noop_file_purger(),
2578 provider: Provider::noop_provider(),
2579 last_flush_millis: Default::default(),
2580 last_schedule_compaction_millis: Default::default(),
2581 time_provider: Arc::new(StdTimeProvider),
2582 topic_latest_entry_id: Default::default(),
2583 region_stats: RegionStats::new(),
2584 stats: ManifestStats::default(),
2585 };
2586
2587 assert_eq!(
2589 region.state(),
2590 RegionRoleState::Leader(RegionLeaderState::Writable)
2591 );
2592 assert!(!region.is_staging());
2593
2594 let mut manager = manifest_ctx.manifest_manager.write().await;
2596 region.set_staging(&mut manager).await.unwrap();
2597 drop(manager);
2598 assert_eq!(
2599 region.state(),
2600 RegionRoleState::Leader(RegionLeaderState::Staging)
2601 );
2602 assert!(region.is_staging());
2603
2604 region.exit_staging().unwrap();
2606 assert_eq!(
2607 region.state(),
2608 RegionRoleState::Leader(RegionLeaderState::Writable)
2609 );
2610 assert!(!region.is_staging());
2611
2612 {
2614 let manager = manifest_ctx.manifest_manager.write().await;
2616 let dummy_actions = RegionMetaActionList::new(vec![]);
2617 let dummy_bytes = dummy_actions.encode().unwrap();
2618
2619 manager.store().save(100, &dummy_bytes, true).await.unwrap();
2621 manager.store().save(101, &dummy_bytes, true).await.unwrap();
2622 drop(manager);
2623
2624 let manager = manifest_ctx.manifest_manager.read().await;
2626 let dirty_manifests = manager.store().fetch_staging_manifests().await.unwrap();
2627 assert_eq!(
2628 dirty_manifests.len(),
2629 2,
2630 "Should have 2 dirty staging files"
2631 );
2632 drop(manager);
2633
2634 let mut manager = manifest_ctx.manifest_manager.write().await;
2636 region.set_staging(&mut manager).await.unwrap();
2637 drop(manager);
2638
2639 let manager = manifest_ctx.manifest_manager.read().await;
2641 let cleaned_manifests = manager.store().fetch_staging_manifests().await.unwrap();
2642 assert_eq!(
2643 cleaned_manifests.len(),
2644 0,
2645 "Dirty staging files should be cleaned up"
2646 );
2647 drop(manager);
2648
2649 region.exit_staging().unwrap();
2651 }
2652
2653 let mut manager = manifest_ctx.manifest_manager.write().await;
2655 assert!(region.set_staging(&mut manager).await.is_ok()); drop(manager);
2657 let mut manager = manifest_ctx.manifest_manager.write().await;
2658 assert!(region.set_staging(&mut manager).await.is_err()); drop(manager);
2660 assert!(region.exit_staging().is_ok()); assert!(region.exit_staging().is_err()); }
2663}