1pub mod catchup;
18pub mod opener;
19pub mod options;
20pub mod utils;
21pub(crate) mod version;
22
23use std::cell::OnceCell;
24use std::collections::hash_map::Entry;
25use std::collections::{HashMap, HashSet};
26use std::sync::atomic::{AtomicI64, AtomicU64, Ordering};
27use std::sync::{Arc, Mutex, RwLock};
28use std::time::Instant;
29
30use common_base::hash::partition_expr_version;
31use common_recordbatch::adapter::RegionQueryStatCounters;
32use common_recordbatch::filter::MetadataFilter;
33use common_telemetry::{error, info, warn};
34use common_time::timestamp::TimeUnit;
35use crossbeam_utils::atomic::AtomicCell;
36use datafusion_common::ScalarValue;
37use futures::stream::{BoxStream, StreamExt};
38use object_store::ObjectStore;
39use partition::expr::PartitionExpr;
40use snafu::{OptionExt, ResultExt, ensure};
41use store_api::ManifestVersion;
42use store_api::codec::PrimaryKeyEncoding;
43use store_api::logstore::provider::Provider;
44use store_api::metadata::RegionMetadataRef;
45use store_api::metrics::{REGION_QUERY_CPU_TIME, REGION_QUERY_SCANNED_BYTES};
46use store_api::region_engine::{
47 RegionManifestInfo, RegionRole, RegionStatistic, SettableRegionRoleState,
48};
49use store_api::region_info::RegionInfoEntry;
50use store_api::region_request::{PathType, StagingPartitionDirective};
51use store_api::sst_entry::{self, ManifestSstEntry};
52use store_api::storage::{FileId, RegionId, SequenceNumber};
53use tokio::sync::RwLockWriteGuard;
54pub use utils::*;
55
56use crate::access_layer::AccessLayerRef;
57use crate::compaction::CompactionStateRef;
58use crate::engine::region_hook::{PendingManifestHook, RegionHookRef};
59use crate::error::{
60 FlushableRegionStateSnafu, InvalidPartitionExprSnafu, RegionNotFoundSnafu, RegionStateSnafu,
61 RegionTruncatedSnafu, Result, UnexpectedSnafu, UpdateManifestSnafu,
62};
63use crate::lease::{Deadline, NodeLease};
64use crate::manifest::action::{
65 RegionChange, RegionManifest, RegionMetaAction, RegionMetaActionList,
66};
67use crate::manifest::manager::RegionManifestManager;
68use crate::metrics::manifest_size::RegionManifestSizeMetric;
69use crate::region::version::{VersionControlRef, VersionRef};
70use crate::request::{OnFailure, OptionOutputTx};
71use crate::series_index::{SeriesIndexVersion, SeriesIndexVersionControl};
72use crate::sst::file::FileMeta;
73use crate::sst::file_purger::FilePurgerRef;
74use crate::sst::location::{index_file_path, sst_file_path};
75use crate::time_provider::TimeProviderRef;
76
77const ESTIMATED_WAL_FACTOR: f32 = 0.42825;
79
80#[derive(Debug)]
82pub struct RegionUsage {
83 pub region_id: RegionId,
84 pub wal_usage: u64,
85 pub sst_usage: u64,
86 pub manifest_usage: u64,
87}
88
89impl RegionUsage {
90 pub fn disk_usage(&self) -> u64 {
91 self.wal_usage + self.sst_usage + self.manifest_usage
92 }
93}
94
95#[derive(Debug, Clone, Copy, PartialEq, Eq)]
96pub enum RegionLeaderState {
97 Writable,
99 Staging,
101 EnteringStaging,
103 Altering,
105 Dropping,
107 Truncating,
109 Editing,
111 Downgrading,
113}
114
115#[derive(Debug, Clone, Copy, PartialEq, Eq)]
116pub enum RegionRoleState {
117 Leader(RegionLeaderState),
118 Follower,
119}
120
121impl RegionRoleState {
122 pub fn into_leader_state(self) -> Option<RegionLeaderState> {
124 match self {
125 RegionRoleState::Leader(leader_state) => Some(leader_state),
126 RegionRoleState::Follower => None,
127 }
128 }
129
130 pub(crate) fn as_str(&self) -> &'static str {
131 match self {
132 RegionRoleState::Follower => "Follower",
133 RegionRoleState::Leader(RegionLeaderState::Writable) => "Leader(Writable)",
134 RegionRoleState::Leader(RegionLeaderState::Staging) => "Leader(Staging)",
135 RegionRoleState::Leader(RegionLeaderState::EnteringStaging) => {
136 "Leader(EnteringStaging)"
137 }
138 RegionRoleState::Leader(RegionLeaderState::Altering) => "Leader(Altering)",
139 RegionRoleState::Leader(RegionLeaderState::Dropping) => "Leader(Dropping)",
140 RegionRoleState::Leader(RegionLeaderState::Truncating) => "Leader(Truncating)",
141 RegionRoleState::Leader(RegionLeaderState::Editing) => "Leader(Editing)",
142 RegionRoleState::Leader(RegionLeaderState::Downgrading) => "Leader(Downgrading)",
143 }
144 }
145}
146
147#[derive(Debug)]
153pub struct MitoRegion {
154 pub(crate) region_id: RegionId,
159 pub(crate) compaction_state: CompactionStateRef,
161
162 pub(crate) version_control: VersionControlRef,
166 pub(crate) series_index_version_control: SeriesIndexVersionControl,
168 pub(crate) series_index_store: Option<ObjectStore>,
170 pub(crate) access_layer: AccessLayerRef,
172 pub(crate) manifest_ctx: ManifestContextRef,
174 pub(crate) file_purger: FilePurgerRef,
176 pub(crate) provider: Provider,
178 last_flush_millis: AtomicI64,
180 last_schedule_compaction_millis: AtomicI64,
182 time_provider: TimeProviderRef,
184 pub(crate) topic_latest_entry_id: AtomicU64,
194 pub(crate) region_stats: RegionStats,
196 stats: ManifestStats,
198}
199
200#[derive(Debug)]
202pub(crate) struct RegionStats {
203 pub(crate) written_bytes: Arc<AtomicU64>,
205 pub(crate) ingested_parquet_bytes: AtomicU64,
207 pub(crate) query_cpu_time: Arc<AtomicU64>,
209 pub(crate) query_scanned_bytes: Arc<AtomicU64>,
211}
212
213impl RegionStats {
214 pub(crate) fn new() -> Self {
215 Self {
216 written_bytes: Arc::new(AtomicU64::new(0)),
217 ingested_parquet_bytes: AtomicU64::new(0),
218 query_cpu_time: Arc::new(AtomicU64::new(0)),
219 query_scanned_bytes: Arc::new(AtomicU64::new(0)),
220 }
221 }
222
223 pub(crate) fn query_stat_counters(&self) -> RegionQueryStatCounters {
224 RegionQueryStatCounters {
225 query_cpu_time: self.query_cpu_time.clone(),
226 query_scanned_bytes: self.query_scanned_bytes.clone(),
227 }
228 }
229}
230
231pub type MitoRegionRef = Arc<MitoRegion>;
232
233#[derive(Debug, Clone)]
234pub(crate) struct StagingPartitionInfo {
235 pub(crate) partition_directive: StagingPartitionDirective,
236 pub(crate) partition_rule_version: u64,
237}
238
239impl StagingPartitionInfo {
240 pub(crate) fn partition_expr(&self) -> Option<&str> {
242 self.partition_directive.partition_expr()
243 }
244
245 pub(crate) fn from_partition_directive(partition_directive: StagingPartitionDirective) -> Self {
247 let partition_rule_version = match &partition_directive {
248 StagingPartitionDirective::UpdatePartitionExpr(expr) => {
249 partition_expr_version(Some(expr))
250 }
251 StagingPartitionDirective::RejectAllWrites => 0,
252 };
253 Self {
254 partition_directive,
255 partition_rule_version,
256 }
257 }
258}
259
260impl MitoRegion {
261 pub(crate) fn activate_region_manifest_size_metric(&self) {
263 if let Some(metric) = self.stats.metric() {
264 metric.activate(&self.stats.total_manifest_size);
265 }
266 }
267
268 #[cfg(test)]
270 pub(crate) fn set_manifest_size_metric_threshold(&self, threshold: u64) {
271 let metric = self.stats.metric().unwrap();
272 metric.set_test_threshold(threshold);
273 metric.publish(self.stats.total_manifest_size());
274 }
275
276 pub(crate) fn deactivate_region_manifest_size_metric(&self) {
278 if let Some(metric) = self.stats.metric() {
279 metric.deactivate();
280 }
281 }
282
283 pub(crate) fn series_index_version(&self) -> Arc<SeriesIndexVersion> {
285 self.series_index_version_control.current()
286 }
287
288 fn remove_region_metrics(&self) {
289 let region_id = self.region_id.as_u64().to_string();
290 let labels = &[region_id.as_str()];
291 let _ = REGION_QUERY_CPU_TIME.remove_label_values(labels);
292 let _ = REGION_QUERY_SCANNED_BYTES.remove_label_values(labels);
293 }
294
295 pub(crate) async fn stop(&self) {
297 self.manifest_ctx
298 .manifest_manager
299 .write()
300 .await
301 .stop()
302 .await;
303
304 info!(
305 "Stopped region manifest manager, region_id: {}",
306 self.region_id
307 );
308 }
309
310 pub fn metadata(&self) -> RegionMetadataRef {
312 let version_data = self.version_control.current();
313 version_data.version.metadata.clone()
314 }
315
316 pub(crate) fn primary_key_encoding(&self) -> PrimaryKeyEncoding {
318 let version_data = self.version_control.current();
319 version_data.version.metadata.primary_key_encoding
320 }
321
322 pub(crate) fn version(&self) -> VersionRef {
324 let version_data = self.version_control.current();
325 version_data.version
326 }
327
328 pub(crate) fn skip_wal(&self) -> bool {
330 self.provider == Provider::Noop || self.version().options.skip_wal
331 }
332
333 pub(crate) fn last_flush_millis(&self) -> i64 {
335 self.last_flush_millis.load(Ordering::Relaxed)
336 }
337
338 pub(crate) fn update_flush_millis(&self) {
340 let now = self.time_provider.current_time_millis();
341 self.last_flush_millis.store(now, Ordering::Relaxed);
342 }
343
344 pub(crate) fn last_schedule_compaction_millis(&self) -> i64 {
346 self.last_schedule_compaction_millis.load(Ordering::Relaxed)
347 }
348
349 pub(crate) fn update_schedule_compaction_millis(&self) {
351 let now = self.time_provider.current_time_millis();
352 self.last_schedule_compaction_millis
353 .store(now, Ordering::Relaxed);
354 }
355
356 pub(crate) fn table_dir(&self) -> &str {
358 self.access_layer.table_dir()
359 }
360
361 pub(crate) fn path_type(&self) -> PathType {
363 self.access_layer.path_type()
364 }
365
366 pub(crate) fn is_writable(&self) -> bool {
368 matches!(
369 self.manifest_ctx.state.load(),
370 RegionRoleState::Leader(RegionLeaderState::Writable)
371 | RegionRoleState::Leader(RegionLeaderState::Staging)
372 )
373 }
374
375 pub(crate) fn is_flushable(&self) -> bool {
377 matches!(
378 self.manifest_ctx.state.load(),
379 RegionRoleState::Leader(RegionLeaderState::Writable)
380 | RegionRoleState::Leader(RegionLeaderState::Staging)
381 | RegionRoleState::Leader(RegionLeaderState::Downgrading)
382 )
383 }
384
385 pub(crate) fn should_abort_index(&self) -> bool {
387 matches!(
388 self.manifest_ctx.state.load(),
389 RegionRoleState::Follower
390 | RegionRoleState::Leader(RegionLeaderState::Dropping)
391 | RegionRoleState::Leader(RegionLeaderState::Truncating)
392 | RegionRoleState::Leader(RegionLeaderState::Downgrading)
393 | RegionRoleState::Leader(RegionLeaderState::Staging)
394 )
395 }
396
397 pub(crate) fn is_downgrading(&self) -> bool {
399 matches!(
400 self.manifest_ctx.state.load(),
401 RegionRoleState::Leader(RegionLeaderState::Downgrading)
402 )
403 }
404
405 pub(crate) fn is_staging(&self) -> bool {
407 self.manifest_ctx.state.load() == RegionRoleState::Leader(RegionLeaderState::Staging)
408 }
409
410 pub(crate) fn is_staging_or_staged_edit(&self) -> bool {
412 match self.manifest_ctx.state.load() {
413 RegionRoleState::Leader(RegionLeaderState::Staging) => true,
414 RegionRoleState::Leader(RegionLeaderState::Editing) => {
415 self.manifest_ctx.staging_partition_info().is_some()
416 }
417 _ => false,
418 }
419 }
420
421 pub(crate) fn is_enter_staging(&self) -> bool {
423 self.manifest_ctx.state.load()
424 == RegionRoleState::Leader(RegionLeaderState::EnteringStaging)
425 }
426
427 pub(crate) fn is_truncating(&self) -> bool {
429 self.manifest_ctx.state.load() == RegionRoleState::Leader(RegionLeaderState::Truncating)
430 }
431
432 pub fn region_id(&self) -> RegionId {
433 self.region_id
434 }
435
436 pub fn find_committed_sequence(&self) -> SequenceNumber {
437 self.version_control.committed_sequence()
438 }
439
440 pub fn flushed_sequence(&self) -> SequenceNumber {
446 self.version_control.current().version.flushed_sequence
447 }
448
449 pub fn is_follower(&self) -> bool {
451 self.manifest_ctx.state.load() == RegionRoleState::Follower
452 }
453
454 pub(crate) fn state(&self) -> RegionRoleState {
456 self.manifest_ctx.state.load()
457 }
458
459 pub(crate) fn set_role(&self, next_role: RegionRole) {
461 self.manifest_ctx.set_role(next_role, self.region_id);
462 }
463
464 pub(crate) fn region_role(&self) -> RegionRole {
465 match self.state() {
466 RegionRoleState::Follower => RegionRole::Follower,
467 RegionRoleState::Leader(RegionLeaderState::Staging) => RegionRole::StagingLeader,
468 RegionRoleState::Leader(RegionLeaderState::Downgrading) => {
469 RegionRole::DowngradingLeader
470 }
471 RegionRoleState::Leader(_) => RegionRole::Leader,
472 }
473 }
474
475 pub(crate) fn set_altering(&self) -> Result<()> {
478 self.compare_exchange_state(
479 RegionLeaderState::Writable,
480 RegionRoleState::Leader(RegionLeaderState::Altering),
481 )
482 }
483
484 pub(crate) fn set_dropping(&self, expect: RegionLeaderState) -> Result<()> {
487 self.compare_exchange_state(expect, RegionRoleState::Leader(RegionLeaderState::Dropping))
488 }
489
490 pub(crate) fn set_truncating(&self) -> Result<()> {
493 self.compare_exchange_state(
494 RegionLeaderState::Writable,
495 RegionRoleState::Leader(RegionLeaderState::Truncating),
496 )
497 }
498
499 pub(crate) fn set_editing(&self, expect: RegionLeaderState) -> Result<()> {
502 self.compare_exchange_state(expect, RegionRoleState::Leader(RegionLeaderState::Editing))
503 }
504
505 pub(crate) async fn set_staging(
511 &self,
512 manager: &mut RwLockWriteGuard<'_, RegionManifestManager>,
513 ) -> Result<()> {
514 manager.store().clear_staging_manifests().await?;
515
516 self.compare_exchange_state(
517 RegionLeaderState::Writable,
518 RegionRoleState::Leader(RegionLeaderState::Staging),
519 )
520 }
521
522 pub(crate) fn set_entering_staging(&self) -> Result<()> {
524 self.compare_exchange_state(
525 RegionLeaderState::Writable,
526 RegionRoleState::Leader(RegionLeaderState::EnteringStaging),
527 )
528 }
529
530 pub fn exit_staging(&self) -> Result<()> {
535 self.manifest_ctx.exit_staging(
536 self.region_id,
537 RegionRoleState::Leader(RegionLeaderState::Writable),
538 )
539 }
540
541 pub(crate) fn abort_staging(&self) {
546 let aborted = self
547 .manifest_ctx
548 .state
549 .fetch_update(|state| {
550 matches!(
551 state,
552 RegionRoleState::Leader(
553 RegionLeaderState::Staging | RegionLeaderState::Editing
554 )
555 )
556 .then_some(RegionRoleState::Leader(RegionLeaderState::Writable))
557 })
558 .is_ok();
559 if aborted {
560 self.manifest_ctx.clear_staging_partition_info();
561 }
562 }
563
564 pub(crate) async fn set_role_state_gracefully(
566 &self,
567 state: SettableRegionRoleState,
568 ) -> Result<()> {
569 let mut manager: RwLockWriteGuard<'_, RegionManifestManager> =
570 self.manifest_ctx.manifest_manager.write().await;
571 let current_state = self.state();
572 let mut wait_for_checkpoint = false;
573
574 let hook_payload: Option<PendingManifestHook> = match state {
575 SettableRegionRoleState::Leader => {
576 match current_state {
579 RegionRoleState::Leader(RegionLeaderState::Staging) => {
580 info!("Exiting staging mode for region {}", self.region_id);
581 self.exit_staging_on_success(&mut manager).await?
583 }
584 RegionRoleState::Leader(RegionLeaderState::Writable) => {
585 info!("Region {} already in normal leader mode", self.region_id);
587 None
588 }
589 _ => {
590 return Err(RegionStateSnafu {
592 region_id: self.region_id,
593 state: current_state,
594 expect: RegionRoleState::Leader(RegionLeaderState::Staging),
595 }
596 .build());
597 }
598 }
599 }
600
601 SettableRegionRoleState::StagingLeader => {
602 match current_state {
605 RegionRoleState::Leader(RegionLeaderState::Writable) => {
606 info!("Entering staging mode for region {}", self.region_id);
607 self.set_staging(&mut manager).await?;
608 }
609 RegionRoleState::Leader(RegionLeaderState::Staging) => {
610 info!("Region {} already in staging mode", self.region_id);
612 }
613 _ => {
614 return Err(RegionStateSnafu {
615 region_id: self.region_id,
616 state: current_state,
617 expect: RegionRoleState::Leader(RegionLeaderState::Writable),
618 }
619 .build());
620 }
621 }
622 None
623 }
624
625 SettableRegionRoleState::Follower => {
626 match current_state {
628 RegionRoleState::Leader(RegionLeaderState::Staging) => {
629 info!(
630 "Exiting staging and demoting region {} to follower",
631 self.region_id
632 );
633 self.exit_staging()?;
634 self.set_role(RegionRole::Follower);
635 wait_for_checkpoint = true;
636 }
637 RegionRoleState::Leader(_) => {
638 info!("Demoting region {} from leader to follower", self.region_id);
639 self.set_role(RegionRole::Follower);
640 wait_for_checkpoint = true;
641 }
642 RegionRoleState::Follower => {
643 info!("Region {} already in follower mode", self.region_id);
645 }
646 }
647 None
648 }
649
650 SettableRegionRoleState::DowngradingLeader => {
651 match current_state {
653 RegionRoleState::Leader(RegionLeaderState::Staging) => {
654 info!(
655 "Exiting staging and entering downgrade for region {}",
656 self.region_id
657 );
658 self.exit_staging()?;
659 self.set_role(RegionRole::DowngradingLeader);
660 wait_for_checkpoint = true;
661 }
662 RegionRoleState::Leader(RegionLeaderState::Writable) => {
663 info!("Starting downgrade for region {}", self.region_id);
664 self.set_role(RegionRole::DowngradingLeader);
665 wait_for_checkpoint = true;
666 }
667 RegionRoleState::Leader(RegionLeaderState::Downgrading) => {
668 info!("Region {} already in downgrading mode", self.region_id);
670 wait_for_checkpoint = true;
671 }
672 RegionRoleState::Follower => {
673 warn!(
674 "Cannot start downgrade for region {} from state {:?}",
675 self.region_id, current_state
676 );
677 }
678 RegionRoleState::Leader(_) => {
679 return Err(RegionStateSnafu {
684 region_id: self.region_id,
685 state: current_state,
686 expect: RegionRoleState::Leader(RegionLeaderState::Writable),
687 }
688 .build());
689 }
690 }
691 None
692 }
693 };
694
695 if wait_for_checkpoint {
699 manager.wait_for_pending_checkpoint().await;
700 }
701
702 let mut backfill_hook_payload: Option<PendingManifestHook> = None;
704 if self.state() == RegionRoleState::Leader(RegionLeaderState::Writable) {
705 let manifest_meta = &manager.manifest().metadata;
707 let current_version = self.version();
708 let current_meta = ¤t_version.metadata;
709 if manifest_meta.partition_expr.is_none() && current_meta.partition_expr.is_some() {
710 let action = RegionMetaAction::Change(RegionChange {
711 metadata: current_meta.clone(),
712 sst_format: current_version.options.sst_format.unwrap_or_default(),
713 append_mode: None,
714 });
715 let action_list = RegionMetaActionList::with_action(action);
716 match self
717 .manifest_ctx
718 .update_locked(&mut manager, action_list, false)
719 .await
720 {
721 Ok(pending) => {
722 info!(
723 "Successfully persisted backfilled metadata for region {}, version: {}",
724 self.region_id,
725 pending.version()
726 );
727 backfill_hook_payload = Some(pending);
728 }
729 Err(e) => {
730 warn!(e; "Failed to persist backfilled metadata for region {}", self.region_id);
731 }
732 }
733 }
734 }
735
736 drop(manager);
737
738 let merged = match (hook_payload, backfill_hook_payload) {
741 (Some(staging), Some(backfill)) => Some(staging.merge(backfill)),
742 (Some(payload), None) => Some(payload),
743 (None, Some(payload)) => Some(payload),
744 (None, None) => None,
745 };
746
747 if let Some(pending) = merged {
748 pending.fire().await;
749 }
750
751 Ok(())
752 }
753
754 pub(crate) fn switch_state_to_writable(&self, expect: RegionLeaderState) {
757 if let Err(e) = self
758 .compare_exchange_state(expect, RegionRoleState::Leader(RegionLeaderState::Writable))
759 {
760 error!(e; "failed to switch region state to writable, expect state is {:?}", expect);
761 }
762 }
763
764 pub(crate) fn switch_state_to_staging(&self, expect: RegionLeaderState) {
767 if let Err(e) =
768 self.compare_exchange_state(expect, RegionRoleState::Leader(RegionLeaderState::Staging))
769 {
770 error!(e; "failed to switch region state to staging, expect state is {:?}", expect);
771 }
772 }
773
774 pub(crate) fn manifest_size(&self) -> u64 {
776 self.stats.total_manifest_size()
777 }
778
779 pub(crate) fn region_statistic(&self) -> RegionStatistic {
781 let version = self.version();
782 let memtables = &version.memtables;
783 let memtable_usage = (memtables.mutable_usage() + memtables.immutables_usage()) as u64;
784
785 let sst_usage = version.ssts.owned_sst_usage(self.region_id);
786 let index_usage = version.ssts.owned_index_usage(self.region_id);
787 let flushed_entry_id = version.flushed_entry_id;
788
789 let wal_usage = self.estimated_wal_usage(memtable_usage);
790 let manifest_usage = self.manifest_size();
791 let num_rows = version.ssts.owned_num_rows(self.region_id) + version.memtables.num_rows();
792 let num_files = version.ssts.owned_num_files(self.region_id);
793 let manifest_version = self.stats.manifest_version();
794 let file_removed_cnt = self.stats.file_removed_cnt();
795
796 let time_range = match (version.ssts.time_range(), version.memtables.time_range()) {
797 (Some((sst_min, sst_max)), Some((mem_min, mem_max))) => {
798 Some((sst_min.min(mem_min), sst_max.max(mem_max)))
799 }
800 (range, None) | (None, range) => range,
801 };
802
803 let topic_latest_entry_id = self.topic_latest_entry_id.load(Ordering::Relaxed);
804 let written_bytes = self.region_stats.written_bytes.load(Ordering::Relaxed);
805 let query_cpu_time = self.region_stats.query_cpu_time.load(Ordering::Relaxed);
806 let query_scanned_bytes = self
807 .region_stats
808 .query_scanned_bytes
809 .load(Ordering::Relaxed);
810
811 RegionStatistic {
812 num_rows,
813 memtable_size: memtable_usage,
814 wal_size: wal_usage,
815 manifest_size: manifest_usage,
816 sst_size: sst_usage,
817 sst_num: num_files,
818 index_size: index_usage,
819 manifest: RegionManifestInfo::Mito {
820 manifest_version,
821 flushed_entry_id,
822 file_removed_cnt,
823 },
824 data_topic_latest_entry_id: topic_latest_entry_id,
825 metadata_topic_latest_entry_id: topic_latest_entry_id,
826 written_bytes,
827 ingested_parquet_bytes: self
828 .region_stats
829 .ingested_parquet_bytes
830 .load(Ordering::Relaxed),
831 query_cpu_time,
832 query_scanned_bytes,
833 min_timestamp: time_range.map(|(min, _)| min),
834 max_timestamp: time_range.map(|(_, max)| max),
835 }
836 }
837
838 fn estimated_wal_usage(&self, memtable_usage: u64) -> u64 {
841 ((memtable_usage as f32) * ESTIMATED_WAL_FACTOR) as u64
842 }
843
844 fn compare_exchange_state(
847 &self,
848 expect: RegionLeaderState,
849 state: RegionRoleState,
850 ) -> Result<()> {
851 self.manifest_ctx
852 .state
853 .compare_exchange(RegionRoleState::Leader(expect), state)
854 .map_err(|actual| {
855 RegionStateSnafu {
856 region_id: self.region_id,
857 state: actual,
858 expect: RegionRoleState::Leader(expect),
859 }
860 .build()
861 })?;
862 Ok(())
863 }
864
865 pub fn access_layer(&self) -> AccessLayerRef {
866 self.access_layer.clone()
867 }
868
869 pub(crate) fn region_info_entry(&self, node_id: Option<u64>) -> RegionInfoEntry {
871 let region_id = self.region_id;
872 let version = self.version();
873 let state = self.state();
874 let role = self.region_role();
875 let region_options = serde_json::to_string(&version.options)
876 .unwrap_or_else(|err| serde_json::json!({ "error": err.to_string() }).to_string());
877 let sst_format = match version.options.sst_format.unwrap_or_default() {
878 crate::sst::FormatType::PrimaryKey => "primary_key",
879 crate::sst::FormatType::Flat => "flat",
880 }
881 .to_string();
882
883 RegionInfoEntry {
884 region_id,
885 table_id: region_id.table_id(),
886 region_number: region_id.region_number(),
887 region_group: region_id.region_group(),
888 region_sequence: region_id.region_sequence(),
889 state: state.as_str().to_string(),
890 role: role.to_string(),
891 writable: self.is_writable(),
892 committed_sequence: self.find_committed_sequence(),
893 flushed_sequence: Some(self.flushed_sequence()).filter(|sequence| *sequence > 0),
894 manifest_version: self.stats.manifest_version(),
895 compaction_time_window: version
896 .compaction_time_window
897 .map(|duration| humantime::format_duration(duration).to_string()),
898 region_options,
899 sst_format,
900 node_id,
901 }
902 }
903
904 pub(crate) async fn manifest_sst_entries(&self) -> Vec<ManifestSstEntry> {
906 self.manifest_sst_stream(MetadataFilter::default(), ManifestSstProjection::new(None))
907 .collect()
908 .await
909 }
910
911 pub(crate) fn manifest_sst_stream(
914 &self,
915 filters: MetadataFilter,
916 projection: ManifestSstProjection,
917 ) -> BoxStream<'_, ManifestSstEntry> {
918 Box::pin(async_stream::stream! {
919 let table_dir = self.table_dir();
920 let path_type = self.access_layer.path_type();
921 let visible_files = self.manifest_visible_file_ids(projection);
922 let (manifest, staging) = {
923 let manager = self.manifest_ctx.manifest_manager.read().await;
924 (manager.manifest(), manager.staging_manifest())
925 };
926 let normal_files = manifest.files.values().map(|meta| (false, meta));
927 let staging_files = staging.iter().flat_map(|s| s.files.values()).map(|meta| (true, meta));
928 for (index, (is_staging, meta)) in normal_files.chain(staging_files).enumerate() {
929 if index > 0 && index % 1024 == 0 {
932 tokio::task::yield_now().await;
933 }
934 if !is_staging && staging.as_ref().is_some_and(|s| s.files.contains_key(&meta.file_id)) {
935 continue;
936 }
937 if !manifest_file_matches(meta, &filters) {
938 continue;
939 }
940 let file_path = OnceCell::new();
943 if !filters.matches(|column| match column {
944 sst_entry::FILE_PATH => Some(file_path.get_or_init(|| {
945 ScalarValue::Utf8(Some(sst_file_path(table_dir, meta.file_id(), path_type)))
946 })),
947 _ => None,
948 }) {
949 continue;
950 }
951 let region_id = self.region_id;
952 let origin_region_id = meta.region_id;
953 let (index_version, index_file_path, index_file_size) = if meta.index_file_size > 0
954 {
955 let index_file_path = projection.index_file_path.then(|| index_file_path(table_dir, meta.index_id(), path_type));
956 (
957 meta.index_version,
958 index_file_path,
959 Some(meta.index_file_size),
960 )
961 } else {
962 (0, None, None)
963 };
964 let visible = visible_files.as_ref().is_some_and(|files| files.contains(&meta.file_id));
965 let file_path = if projection.file_path {
966 match file_path.into_inner() {
967 Some(ScalarValue::Utf8(Some(path))) => path,
968 _ => sst_file_path(table_dir, meta.file_id(), path_type),
969 }
970 } else {
971 String::new()
972 };
973 yield ManifestSstEntry {
974 table_dir: if projection.table_dir { table_dir.to_string() } else { String::new() },
975 region_id,
976 table_id: region_id.table_id(),
977 region_number: region_id.region_number(),
978 region_group: region_id.region_group(),
979 region_sequence: region_id.region_sequence(),
980 file_id: if projection.file_id { meta.file_id.to_string() } else { String::new() },
981 index_version,
982 level: meta.level,
983 file_path,
984 file_size: meta.file_size,
985 max_row_group_uncompressed_size: meta.max_row_group_uncompressed_size,
986 index_file_path,
987 index_file_size,
988 num_rows: meta.num_rows,
989 num_row_groups: meta.num_row_groups,
990 num_series: Some(meta.num_series),
991 min_ts: meta.time_range.0,
992 max_ts: meta.time_range.1,
993 sequence: meta.sequence.map(|s| s.get()),
994 partition_expr: if projection.partition_expr { meta.partition_expr.as_ref().map(ToString::to_string) } else { None },
995 origin_region_id,
996 node_id: None,
997 visible,
998 primary_key_min: if projection.primary_key_min { meta.primary_key_min.clone() } else { None },
999 primary_key_max: if projection.primary_key_max { meta.primary_key_max.clone() } else { None },
1000 };
1001 }
1002 })
1003 }
1004
1005 fn manifest_visible_file_ids(
1008 &self,
1009 projection: ManifestSstProjection,
1010 ) -> Option<HashSet<FileId>> {
1011 projection.visible.then(|| {
1012 self.version()
1013 .ssts
1014 .levels()
1015 .iter()
1016 .flat_map(|level| level.files().map(|file| file.file_id().file_id()))
1017 .collect()
1018 })
1019 }
1020
1021 pub async fn file_metas(&self, file_ids: &[FileId]) -> Vec<Option<FileMeta>> {
1023 let manifest_files = self.manifest_ctx.manifest().await.files.clone();
1024
1025 file_ids
1026 .iter()
1027 .map(|file_id| manifest_files.get(file_id).cloned())
1028 .collect::<Vec<_>>()
1029 }
1030
1031 pub async fn all_manifest_files(&self) -> (Vec<FileMeta>, ManifestVersion) {
1041 let manifest = self.manifest_ctx.manifest().await;
1042 let staging = self
1043 .manifest_ctx
1044 .staging_manifest()
1045 .await
1046 .map(|m| (m.files.clone(), m.manifest_version));
1047
1048 let version = staging
1049 .as_ref()
1050 .map(|(_, v)| *v)
1051 .unwrap_or(manifest.manifest_version);
1052
1053 let files = match staging {
1054 Some((staging_files, _)) => {
1055 let merged = manifest
1056 .files
1057 .clone()
1058 .into_iter()
1059 .chain(staging_files)
1060 .collect::<std::collections::HashMap<_, _>>();
1061 merged.into_values().collect()
1062 }
1063 None => manifest.files.values().cloned().collect(),
1064 };
1065
1066 (files, version)
1067 }
1068
1069 pub(crate) async fn exit_staging_on_success(
1079 &self,
1080 manager: &mut RwLockWriteGuard<'_, RegionManifestManager>,
1081 ) -> Result<Option<PendingManifestHook>> {
1082 let current_state = self.manifest_ctx.current_state();
1083 ensure!(
1084 current_state == RegionRoleState::Leader(RegionLeaderState::Staging),
1085 RegionStateSnafu {
1086 region_id: self.region_id,
1087 state: current_state,
1088 expect: RegionRoleState::Leader(RegionLeaderState::Staging),
1089 }
1090 );
1091
1092 let merged_actions = match manager.merge_staged_actions(current_state).await? {
1094 Some(actions) => actions,
1095 None => {
1096 info!(
1097 "No staged manifests to merge for region {}, exiting staging mode without changes",
1098 self.region_id
1099 );
1100 self.exit_staging()?;
1102 return Ok(None);
1103 }
1104 };
1105 let expect_change = merged_actions.actions.iter().any(|a| a.is_change());
1106 let expect_partition_expr_change = merged_actions
1107 .actions
1108 .iter()
1109 .any(|a| a.is_partition_expr_change());
1110 let expect_edit = merged_actions.actions.iter().any(|a| a.is_edit());
1111 ensure!(
1112 !(expect_change && expect_partition_expr_change),
1113 UnexpectedSnafu {
1114 reason: "unexpected both change and partition expr change actions in merged actions"
1115 }
1116 );
1117 ensure!(
1118 expect_change || expect_partition_expr_change,
1119 UnexpectedSnafu {
1120 reason: "expect a change or partition expr change action in merged actions"
1121 }
1122 );
1123 ensure!(
1124 expect_edit,
1125 UnexpectedSnafu {
1126 reason: "expect an edit action in merged actions"
1127 }
1128 );
1129
1130 let (merged_partition_expr_change, merged_change, merged_edit) =
1131 merged_actions.clone().split_region_change_and_edit();
1132 if let Some(change) = &merged_change {
1133 let current_column_metadatas = &self.version().metadata.column_metadatas;
1137 ensure!(
1138 change.metadata.column_metadatas == *current_column_metadatas,
1139 UnexpectedSnafu {
1140 reason: "change action alters column metadata in staging exit"
1141 }
1142 );
1143 }
1144
1145 let pending = self
1148 .manifest_ctx
1149 .update_locked(manager, merged_actions, false)
1150 .await?;
1151 let new_version = pending.version();
1152 info!(
1153 "Successfully submitted merged staged manifests for region {}, new version: {}",
1154 self.region_id, new_version
1155 );
1156
1157 if let Some(change) = merged_partition_expr_change {
1159 let mut new_metadata = self.version().metadata.as_ref().clone();
1160 new_metadata.set_partition_expr(change.partition_expr);
1161 self.version_control.alter_metadata(new_metadata.into());
1162 }
1163 if let Some(change) = merged_change {
1164 self.version_control.alter_metadata(change.metadata);
1165 }
1166 self.version_control
1167 .apply_edit(Some(merged_edit), &[], self.file_purger.clone());
1168
1169 if let Err(e) = manager.clear_staging_manifest_and_dir().await {
1171 error!(e; "Failed to clear staging manifest dir for region {}", self.region_id);
1172 }
1173 self.exit_staging()?;
1174
1175 Ok(Some(pending))
1177 }
1178
1179 pub fn expected_partition_expr_version(&self) -> u64 {
1180 if self.is_staging() {
1181 self.manifest_ctx
1182 .staging_partition_info()
1183 .as_ref()
1184 .map(|info| info.partition_rule_version)
1185 .unwrap_or_default()
1186 } else {
1187 self.version().metadata.partition_expr_version
1188 }
1189 }
1190
1191 pub(crate) fn reject_all_writes_in_staging(&self) -> bool {
1193 if !self.is_staging() {
1194 return false;
1195 }
1196 self.manifest_ctx
1197 .staging_partition_info()
1198 .as_ref()
1199 .map(|info| {
1200 matches!(
1201 info.partition_directive,
1202 StagingPartitionDirective::RejectAllWrites
1203 )
1204 })
1205 .unwrap_or(false)
1206 }
1207}
1208
1209impl Drop for MitoRegion {
1210 fn drop(&mut self) {
1211 self.remove_region_metrics();
1212 }
1213}
1214
1215#[derive(Debug)]
1217pub(crate) enum IndexPublication {
1218 Committed {
1220 manifest_version: ManifestVersion,
1221 file_meta: FileMeta,
1222 },
1223 Stale(IndexPublicationStale),
1225}
1226
1227#[derive(Debug, PartialEq, Eq)]
1229pub(crate) enum IndexPublicationStale {
1230 SourceChanged,
1232 SchemaChanged,
1234}
1235
1236#[derive(Clone, Debug, PartialEq, Eq)]
1242pub(crate) struct IndexBuildSource {
1243 pub(crate) file_meta: FileMeta,
1244 pub(crate) schema_version: u64,
1245}
1246
1247impl IndexBuildSource {
1248 pub(crate) fn new(file_meta: FileMeta, schema_version: u64) -> Self {
1249 Self {
1250 file_meta,
1251 schema_version,
1252 }
1253 }
1254}
1255
1256#[derive(Debug)]
1258pub(crate) struct ManifestContext {
1259 pub(crate) manifest_manager: tokio::sync::RwLock<RegionManifestManager>,
1263 state: AtomicCell<RegionRoleState>,
1266 downgrade_started_at: Mutex<Instant>,
1273 pub(crate) leader_deadline: Deadline,
1277 node_lease: Arc<NodeLease>,
1279 staging_partition_info: Mutex<Option<StagingPartitionInfo>>,
1284 hook: Option<RegionHookRef>,
1286}
1287
1288impl ManifestContext {
1289 pub(crate) fn new(
1290 manager: RegionManifestManager,
1291 state: RegionRoleState,
1292 hook: Option<RegionHookRef>,
1293 node_lease: Arc<NodeLease>,
1294 ) -> Self {
1295 ManifestContext {
1296 manifest_manager: tokio::sync::RwLock::new(manager),
1297 state: AtomicCell::new(state),
1298 downgrade_started_at: Mutex::new(Instant::now()),
1299 leader_deadline: Deadline::default(),
1300 node_lease,
1301 staging_partition_info: Mutex::new(None),
1302 hook,
1303 }
1304 }
1305
1306 pub(crate) fn hook(&self) -> Option<RegionHookRef> {
1308 self.hook.clone()
1309 }
1310
1311 pub(crate) fn staging_partition_info(&self) -> Option<StagingPartitionInfo> {
1312 self.staging_partition_info.lock().unwrap().clone()
1313 }
1314
1315 pub(crate) fn set_staging_partition_info(&self, staging_partition_info: StagingPartitionInfo) {
1316 let mut current = self.staging_partition_info.lock().unwrap();
1317 debug_assert!(current.is_none());
1318 *current = Some(staging_partition_info);
1319 }
1320
1321 fn clear_staging_partition_info(&self) {
1322 *self.staging_partition_info.lock().unwrap() = None;
1323 }
1324
1325 pub(crate) fn exit_staging(
1326 &self,
1327 region_id: RegionId,
1328 next_state: RegionRoleState,
1329 ) -> Result<()> {
1330 self.state
1331 .compare_exchange(
1332 RegionRoleState::Leader(RegionLeaderState::Staging),
1333 next_state,
1334 )
1335 .map_err(|actual| {
1336 RegionStateSnafu {
1337 region_id,
1338 state: actual,
1339 expect: RegionRoleState::Leader(RegionLeaderState::Staging),
1340 }
1341 .build()
1342 })?;
1343 self.clear_staging_partition_info();
1344 Ok(())
1345 }
1346
1347 pub(crate) async fn manifest_version(&self) -> ManifestVersion {
1348 self.manifest_manager
1349 .read()
1350 .await
1351 .manifest()
1352 .manifest_version
1353 }
1354
1355 pub(crate) async fn has_update(&self) -> Result<bool> {
1356 self.manifest_manager.read().await.has_update().await
1357 }
1358
1359 pub(crate) fn current_state(&self) -> RegionRoleState {
1361 self.state.load()
1362 }
1363
1364 pub(crate) async fn install_manifest_to(
1370 &self,
1371 version: ManifestVersion,
1372 ) -> Result<Arc<RegionManifest>> {
1373 let mut manager = self.manifest_manager.write().await;
1374 manager.install_manifest_to(version).await?;
1375
1376 Ok(manager.manifest())
1377 }
1378
1379 pub(crate) async fn update_manifest(
1381 &self,
1382 expect_state: RegionLeaderState,
1383 action_list: RegionMetaActionList,
1384 is_staging: bool,
1385 ) -> Result<ManifestVersion> {
1386 self.update_manifest_with_state_check(action_list, is_staging, |current_state, region_id| {
1387 if expect_state != RegionLeaderState::Downgrading {
1392 if current_state == RegionRoleState::Leader(RegionLeaderState::Downgrading) {
1393 info!(
1394 "Region {} is in downgrading leader state, updating manifest. Expect state is {:?}",
1395 region_id, expect_state
1396 );
1397 }
1398 ensure!(
1399 current_state == RegionRoleState::Leader(expect_state)
1400 || current_state == RegionRoleState::Leader(RegionLeaderState::Downgrading),
1401 UpdateManifestSnafu {
1402 region_id,
1403 state: current_state,
1404 }
1405 );
1406 } else {
1407 ensure!(
1408 current_state == RegionRoleState::Leader(expect_state),
1409 RegionStateSnafu {
1410 region_id,
1411 state: current_state,
1412 expect: RegionRoleState::Leader(expect_state),
1413 }
1414 );
1415 }
1416
1417 Ok(())
1418 })
1419 .await
1420 }
1421
1422 pub(crate) async fn update_manifest_for_compaction(
1439 &self,
1440 action_list: RegionMetaActionList,
1441 ) -> Result<ManifestVersion> {
1442 self.update_manifest_with_state_check(action_list, false, |current_state, region_id| {
1443 ensure!(
1444 matches!(
1445 current_state,
1446 RegionRoleState::Leader(RegionLeaderState::Writable)
1447 | RegionRoleState::Leader(RegionLeaderState::Editing)
1448 | RegionRoleState::Leader(RegionLeaderState::Downgrading)
1449 ),
1450 UpdateManifestSnafu {
1451 region_id,
1452 state: current_state,
1453 }
1454 );
1455
1456 Ok(())
1457 })
1458 .await
1459 }
1460
1461 pub(crate) async fn update_manifest_for_index(
1467 &self,
1468 source: &IndexBuildSource,
1469 updated: FileMeta,
1470 ) -> Result<IndexPublication> {
1471 let manager = self.manifest_manager.write().await;
1472 let manifest = manager.manifest();
1473 let current_state = self.state.load();
1474 if !matches!(
1475 current_state,
1476 RegionRoleState::Leader(RegionLeaderState::Writable)
1477 | RegionRoleState::Leader(RegionLeaderState::Downgrading)
1478 ) || manager.is_stopped()
1479 {
1480 return Ok(IndexPublication::Stale(
1481 IndexPublicationStale::SourceChanged,
1482 ));
1483 }
1484
1485 if manifest.files.get(&source.file_meta.file_id) != Some(&source.file_meta) {
1486 return Ok(IndexPublication::Stale(
1487 IndexPublicationStale::SourceChanged,
1488 ));
1489 }
1490
1491 if manifest.metadata.schema_version != source.schema_version {
1492 return Ok(IndexPublication::Stale(
1493 IndexPublicationStale::SchemaChanged,
1494 ));
1495 }
1496
1497 let mut committed = source.file_meta.clone();
1499 committed.available_indexes = updated.available_indexes;
1500 committed.indexes = updated.indexes;
1501 committed.index_file_size = updated.index_file_size;
1502 committed.index_version = updated.index_version;
1503
1504 let edit = crate::manifest::action::RegionEdit {
1505 files_to_add: vec![committed.clone()],
1506 files_to_remove: Vec::new(),
1507 timestamp_ms: Some(chrono::Utc::now().timestamp_millis()),
1508 flushed_sequence: None,
1509 flushed_entry_id: None,
1510 committed_sequence: None,
1511 compaction_time_window: None,
1512 };
1513 let action_list = RegionMetaActionList::with_action(RegionMetaAction::Edit(edit));
1514 let manifest_version = self.update_and_fire(manager, action_list, false).await?;
1515
1516 Ok(IndexPublication::Committed {
1517 manifest_version,
1518 file_meta: committed,
1519 })
1520 }
1521
1522 pub(crate) async fn update_locked(
1532 &self,
1533 manager: &mut RegionManifestManager,
1534 action_list: RegionMetaActionList,
1535 is_staging: bool,
1536 ) -> Result<PendingManifestHook> {
1537 let region_id = manager.manifest().metadata.region_id;
1538 let action_list_for_hook = self.hook.as_ref().map(|_| action_list.clone());
1541 let allow_checkpoint = !is_staging
1544 && self.state.load() != RegionRoleState::Leader(RegionLeaderState::Downgrading);
1545 let version = manager
1546 .update_with_verification(action_list, is_staging, allow_checkpoint, || {
1547 self.is_leader_lease_expired()
1548 })
1549 .await
1550 .inspect_err(|e| error!(e; "Failed to update manifest, region_id: {}", region_id))?;
1551
1552 Ok(PendingManifestHook::new(
1553 region_id,
1554 action_list_for_hook,
1555 version,
1556 self.hook.clone(),
1557 is_staging,
1558 ))
1559 }
1560
1561 fn is_leader_lease_expired(&self) -> bool {
1563 self.node_lease.now() >= self.leader_deadline.get()
1564 }
1565
1566 async fn update_and_fire(
1572 &self,
1573 mut manager: RwLockWriteGuard<'_, RegionManifestManager>,
1574 action_list: RegionMetaActionList,
1575 is_staging: bool,
1576 ) -> Result<ManifestVersion> {
1577 let region_id = manager.manifest().metadata.region_id;
1578 let pending = self
1579 .update_locked(&mut manager, action_list, is_staging)
1580 .await?;
1581 let version = pending.version();
1582
1583 drop(manager);
1586
1587 if self.state.load() == RegionRoleState::Follower {
1588 warn!(
1589 "Region {} becomes follower while updating manifest which may cause inconsistency, manifest version: {version}",
1590 region_id
1591 );
1592 }
1593
1594 pending.fire().await;
1595 Ok(version)
1596 }
1597
1598 async fn update_manifest_with_state_check(
1599 &self,
1600 action_list: RegionMetaActionList,
1601 is_staging: bool,
1602 check_state: impl FnOnce(RegionRoleState, RegionId) -> Result<()>,
1603 ) -> Result<ManifestVersion> {
1604 let manager = self.manifest_manager.write().await;
1606 let manifest = manager.manifest();
1608 let current_state = self.state.load();
1611 check_state(current_state, manifest.metadata.region_id)?;
1612
1613 for action in &action_list.actions {
1614 let RegionMetaAction::Edit(edit) = &action else {
1616 continue;
1617 };
1618
1619 let Some(truncated_entry_id) = manifest.truncated_entry_id else {
1621 continue;
1622 };
1623
1624 if let Some(flushed_entry_id) = edit.flushed_entry_id {
1626 let is_newer_entry = truncated_entry_id < flushed_entry_id;
1636 let is_same_entry_with_newer_sequence = truncated_entry_id == flushed_entry_id
1637 && edit.flushed_sequence.is_some_and(|flushed_sequence| {
1638 manifest.flushed_sequence < flushed_sequence
1639 });
1640
1641 ensure!(
1642 is_newer_entry || is_same_entry_with_newer_sequence,
1643 RegionTruncatedSnafu {
1644 region_id: manifest.metadata.region_id,
1645 }
1646 );
1647 }
1648
1649 if !edit.files_to_remove.is_empty() {
1651 for file in &edit.files_to_remove {
1653 ensure!(
1654 manifest.files.contains_key(&file.file_id),
1655 RegionTruncatedSnafu {
1656 region_id: manifest.metadata.region_id,
1657 }
1658 );
1659 }
1660 }
1661 }
1662
1663 self.update_and_fire(manager, action_list, is_staging).await
1664 }
1665
1666 pub(crate) fn begin_downgrade(&self, region_id: RegionId) {
1668 let mut downgrade_started_at = self.downgrade_started_at.lock().unwrap();
1669 *downgrade_started_at = Instant::now();
1670 self.set_role(RegionRole::DowngradingLeader, region_id);
1671 }
1672
1673 pub(crate) fn promote_unless_downgrade_started_after(
1677 &self,
1678 issued_at: Instant,
1679 region_id: RegionId,
1680 ) -> bool {
1681 if self.state.load() == RegionRoleState::Leader(RegionLeaderState::Writable) {
1683 return true;
1684 }
1685 let downgrade_started_at = self.downgrade_started_at.lock().unwrap();
1686 if issued_at < *downgrade_started_at {
1687 return false;
1688 }
1689 self.set_role(RegionRole::Leader, region_id);
1690 true
1691 }
1692
1693 pub(crate) fn set_role(&self, next_role: RegionRole, region_id: RegionId) {
1727 match next_role {
1728 RegionRole::Follower => {
1729 if self
1730 .exit_staging(region_id, RegionRoleState::Follower)
1731 .is_ok()
1732 {
1733 info!(
1734 "Convert region {} to follower, previous role state: {:?}",
1735 region_id,
1736 RegionRoleState::Leader(RegionLeaderState::Staging)
1737 );
1738 return;
1739 }
1740 match self.state.fetch_update(|state| {
1741 if !matches!(state, RegionRoleState::Follower) {
1742 Some(RegionRoleState::Follower)
1743 } else {
1744 None
1745 }
1746 }) {
1747 Ok(state) => info!(
1748 "Convert region {} to follower, previous role state: {:?}",
1749 region_id, state
1750 ),
1751 Err(state) => {
1752 if state != RegionRoleState::Follower {
1753 warn!(
1754 "Failed to convert region {} to follower, current role state: {:?}",
1755 region_id, state
1756 )
1757 }
1758 }
1759 }
1760 }
1761 RegionRole::Leader => {
1762 if self
1763 .exit_staging(
1764 region_id,
1765 RegionRoleState::Leader(RegionLeaderState::Writable),
1766 )
1767 .is_ok()
1768 {
1769 info!(
1770 "Convert region {} to leader, previous role state: {:?}",
1771 region_id,
1772 RegionRoleState::Leader(RegionLeaderState::Staging)
1773 );
1774 return;
1775 }
1776 match self.state.fetch_update(|state| {
1777 if matches!(
1778 state,
1779 RegionRoleState::Follower
1780 | RegionRoleState::Leader(RegionLeaderState::Downgrading)
1781 ) {
1782 Some(RegionRoleState::Leader(RegionLeaderState::Writable))
1783 } else {
1784 None
1785 }
1786 }) {
1787 Ok(state) => info!(
1788 "Convert region {} to leader, previous role state: {:?}",
1789 region_id, state
1790 ),
1791 Err(state) => {
1792 if !matches!(
1794 state,
1795 RegionRoleState::Leader(
1796 RegionLeaderState::Writable
1797 | RegionLeaderState::Altering
1798 | RegionLeaderState::Dropping
1799 | RegionLeaderState::Truncating
1800 | RegionLeaderState::Editing
1801 | RegionLeaderState::EnteringStaging
1802 )
1803 ) {
1804 warn!(
1805 "Failed to convert region {} to leader, current role state: {:?}",
1806 region_id, state
1807 )
1808 }
1809 }
1810 }
1811 }
1812 RegionRole::StagingLeader => {
1813 info!(
1814 "Ignore direct conversion of region {} to staging leader; staging requires the dedicated workflow",
1815 region_id
1816 );
1817 }
1818 RegionRole::DowngradingLeader => {
1819 if self
1820 .exit_staging(
1821 region_id,
1822 RegionRoleState::Leader(RegionLeaderState::Downgrading),
1823 )
1824 .is_ok()
1825 {
1826 info!(
1827 "Convert region {} to downgrading region, previous role state: {:?}",
1828 region_id,
1829 RegionRoleState::Leader(RegionLeaderState::Staging)
1830 );
1831 return;
1832 }
1833 match self.state.compare_exchange(
1834 RegionRoleState::Leader(RegionLeaderState::Writable),
1835 RegionRoleState::Leader(RegionLeaderState::Downgrading),
1836 ) {
1837 Ok(state) => info!(
1838 "Convert region {} to downgrading region, previous role state: {:?}",
1839 region_id, state
1840 ),
1841 Err(state) => {
1842 if state != RegionRoleState::Leader(RegionLeaderState::Downgrading) {
1843 warn!(
1844 "Failed to convert region {} to downgrading leader, current role state: {:?}",
1845 region_id, state
1846 )
1847 }
1848 }
1849 }
1850 }
1851 }
1852 }
1853
1854 pub(crate) async fn manifest(&self) -> Arc<crate::manifest::action::RegionManifest> {
1856 self.manifest_manager.read().await.manifest()
1857 }
1858
1859 pub(crate) async fn staging_manifest(
1861 &self,
1862 ) -> Option<Arc<crate::manifest::action::RegionManifest>> {
1863 self.manifest_manager.read().await.staging_manifest()
1864 }
1865}
1866
1867pub(crate) type ManifestContextRef = Arc<ManifestContext>;
1868
1869#[derive(Debug, Default)]
1871pub(crate) struct RegionMap {
1872 regions: RwLock<HashMap<RegionId, MitoRegionRef>>,
1873}
1874
1875impl RegionMap {
1876 pub(crate) fn is_region_exists(&self, region_id: RegionId) -> bool {
1878 let regions = self.regions.read().unwrap();
1879 regions.contains_key(®ion_id)
1880 }
1881
1882 pub(crate) fn insert_region(&self, region: MitoRegionRef) {
1884 let mut regions = self.regions.write().unwrap();
1885 regions.insert(region.region_id, region);
1886 }
1887
1888 pub(crate) fn get_region(&self, region_id: RegionId) -> Option<MitoRegionRef> {
1890 let regions = self.regions.read().unwrap();
1891 regions.get(®ion_id).cloned()
1892 }
1893
1894 pub(crate) fn writable_region(&self, region_id: RegionId) -> Result<MitoRegionRef> {
1898 let region = self
1899 .get_region(region_id)
1900 .context(RegionNotFoundSnafu { region_id })?;
1901 ensure!(
1902 region.is_writable(),
1903 RegionStateSnafu {
1904 region_id,
1905 state: region.state(),
1906 expect: RegionRoleState::Leader(RegionLeaderState::Writable),
1907 }
1908 );
1909 Ok(region)
1910 }
1911
1912 pub(crate) fn follower_region(&self, region_id: RegionId) -> Result<MitoRegionRef> {
1916 let region = self
1917 .get_region(region_id)
1918 .context(RegionNotFoundSnafu { region_id })?;
1919 ensure!(
1920 region.is_follower(),
1921 RegionStateSnafu {
1922 region_id,
1923 state: region.state(),
1924 expect: RegionRoleState::Follower,
1925 }
1926 );
1927
1928 Ok(region)
1929 }
1930
1931 pub(crate) fn get_region_or<F: OnFailure>(
1935 &self,
1936 region_id: RegionId,
1937 cb: &mut F,
1938 ) -> Option<MitoRegionRef> {
1939 match self
1940 .get_region(region_id)
1941 .context(RegionNotFoundSnafu { region_id })
1942 {
1943 Ok(region) => Some(region),
1944 Err(e) => {
1945 cb.on_failure(e);
1946 None
1947 }
1948 }
1949 }
1950
1951 pub(crate) fn writable_region_or<F: OnFailure>(
1955 &self,
1956 region_id: RegionId,
1957 cb: &mut F,
1958 ) -> Option<MitoRegionRef> {
1959 match self.writable_region(region_id) {
1960 Ok(region) => Some(region),
1961 Err(e) => {
1962 cb.on_failure(e);
1963 None
1964 }
1965 }
1966 }
1967
1968 pub(crate) fn writable_non_staging_region(&self, region_id: RegionId) -> Result<MitoRegionRef> {
1972 let region = self.writable_region(region_id)?;
1973 if region.is_staging() {
1974 return Err(crate::error::RegionStateSnafu {
1975 region_id,
1976 state: region.state(),
1977 expect: RegionRoleState::Leader(RegionLeaderState::Writable),
1978 }
1979 .build());
1980 }
1981 Ok(region)
1982 }
1983
1984 pub(crate) fn staging_region(&self, region_id: RegionId) -> Result<MitoRegionRef> {
1988 let region = self
1989 .get_region(region_id)
1990 .context(RegionNotFoundSnafu { region_id })?;
1991 ensure!(
1992 region.is_staging(),
1993 RegionStateSnafu {
1994 region_id,
1995 state: region.state(),
1996 expect: RegionRoleState::Leader(RegionLeaderState::Staging),
1997 }
1998 );
1999 Ok(region)
2000 }
2001
2002 pub(crate) fn flushable_region(&self, region_id: RegionId) -> Result<MitoRegionRef> {
2006 let region = self
2007 .get_region(region_id)
2008 .context(RegionNotFoundSnafu { region_id })?;
2009 ensure!(
2010 region.is_flushable(),
2011 FlushableRegionStateSnafu {
2012 region_id,
2013 state: region.state(),
2014 }
2015 );
2016 Ok(region)
2017 }
2018
2019 pub(crate) fn remove_region(&self, region_id: RegionId) -> Option<MitoRegionRef> {
2021 let mut regions = self.regions.write().unwrap();
2022 regions.remove(®ion_id)
2023 }
2024
2025 pub(crate) fn list_regions(&self) -> Vec<MitoRegionRef> {
2027 let regions = self.regions.read().unwrap();
2028 regions.values().cloned().collect()
2029 }
2030
2031 pub(crate) fn clear(&self) {
2033 self.regions.write().unwrap().clear();
2034 }
2035}
2036
2037pub(crate) type RegionMapRef = Arc<RegionMap>;
2038
2039#[derive(Debug, Default)]
2041pub(crate) struct OpeningRegions {
2042 regions: RwLock<HashMap<RegionId, Vec<OptionOutputTx>>>,
2043}
2044
2045impl OpeningRegions {
2046 pub(crate) fn wait_for_opening_region(
2048 &self,
2049 region_id: RegionId,
2050 sender: OptionOutputTx,
2051 ) -> Option<OptionOutputTx> {
2052 let mut regions = self.regions.write().unwrap();
2053 match regions.entry(region_id) {
2054 Entry::Occupied(mut senders) => {
2055 senders.get_mut().push(sender);
2056 None
2057 }
2058 Entry::Vacant(_) => Some(sender),
2059 }
2060 }
2061
2062 pub(crate) fn is_region_exists(&self, region_id: RegionId) -> bool {
2064 let regions = self.regions.read().unwrap();
2065 regions.contains_key(®ion_id)
2066 }
2067
2068 pub(crate) fn insert_sender(&self, region: RegionId, sender: OptionOutputTx) {
2070 let mut regions = self.regions.write().unwrap();
2071 regions.insert(region, vec![sender]);
2072 }
2073
2074 pub(crate) fn remove_sender(&self, region_id: RegionId) -> Vec<OptionOutputTx> {
2076 let mut regions = self.regions.write().unwrap();
2077 regions.remove(®ion_id).unwrap_or_default()
2078 }
2079
2080 #[cfg(test)]
2081 pub(crate) fn sender_len(&self, region_id: RegionId) -> usize {
2082 let regions = self.regions.read().unwrap();
2083 if let Some(senders) = regions.get(®ion_id) {
2084 senders.len()
2085 } else {
2086 0
2087 }
2088 }
2089}
2090
2091pub(crate) type OpeningRegionsRef = Arc<OpeningRegions>;
2092
2093#[derive(Debug, Default)]
2095pub(crate) struct CatchupRegions {
2096 regions: RwLock<HashSet<RegionId>>,
2097}
2098
2099impl CatchupRegions {
2100 pub(crate) fn is_region_exists(&self, region_id: RegionId) -> bool {
2102 let regions = self.regions.read().unwrap();
2103 regions.contains(®ion_id)
2104 }
2105
2106 pub(crate) fn insert_region(&self, region_id: RegionId) {
2108 let mut regions = self.regions.write().unwrap();
2109 regions.insert(region_id);
2110 }
2111
2112 pub(crate) fn remove_region(&self, region_id: RegionId) {
2114 let mut regions = self.regions.write().unwrap();
2115 regions.remove(®ion_id);
2116 }
2117}
2118
2119pub(crate) type CatchupRegionsRef = Arc<CatchupRegions>;
2120
2121#[derive(Default, Debug, Clone)]
2123pub struct ManifestStats {
2124 metric: Option<RegionManifestSizeMetric>,
2125 pub(crate) total_manifest_size: Arc<AtomicU64>,
2126 pub(crate) manifest_version: Arc<AtomicU64>,
2127 pub(crate) file_removed_cnt: Arc<AtomicU64>,
2128}
2129
2130impl ManifestStats {
2131 pub(crate) fn with_region_metrics(region_id: RegionId) -> Self {
2133 Self {
2134 metric: Some(RegionManifestSizeMetric::new(region_id)),
2135 ..Default::default()
2136 }
2137 }
2138
2139 pub(crate) fn metric(&self) -> Option<&RegionManifestSizeMetric> {
2140 self.metric.as_ref()
2141 }
2142
2143 fn total_manifest_size(&self) -> u64 {
2144 self.total_manifest_size.load(Ordering::Relaxed)
2145 }
2146
2147 fn manifest_version(&self) -> u64 {
2148 self.manifest_version.load(Ordering::Relaxed)
2149 }
2150
2151 fn file_removed_cnt(&self) -> u64 {
2152 self.file_removed_cnt.load(Ordering::Relaxed)
2153 }
2154}
2155
2156pub fn parse_partition_expr(partition_expr_str: Option<&str>) -> Result<Option<PartitionExpr>> {
2158 match partition_expr_str {
2159 None => Ok(None),
2160 Some("") => Ok(None),
2161 Some(json_str) => {
2162 let expr = partition::expr::PartitionExpr::from_json_str(json_str)
2163 .with_context(|_| InvalidPartitionExprSnafu { expr: json_str })?;
2164 Ok(expr)
2165 }
2166 }
2167}
2168
2169#[derive(Clone, Copy)]
2171pub(crate) struct ManifestSstProjection {
2172 visible: bool,
2173 table_dir: bool,
2174 file_id: bool,
2175 file_path: bool,
2176 index_file_path: bool,
2177 partition_expr: bool,
2178 primary_key_min: bool,
2179 primary_key_max: bool,
2180}
2181
2182impl ManifestSstProjection {
2183 pub(crate) fn new(projection: Option<&[usize]>) -> Self {
2185 let schema = ManifestSstEntry::schema();
2186 let included = |name: &str| {
2187 projection.is_none_or(|indices| {
2188 indices.iter().any(|index| {
2189 schema
2190 .column_schemas()
2191 .get(*index)
2192 .is_some_and(|column| column.name == name)
2193 })
2194 })
2195 };
2196 Self {
2197 visible: included(sst_entry::VISIBLE),
2198 table_dir: included(sst_entry::TABLE_DIR),
2199 file_id: included(sst_entry::FILE_ID),
2200 file_path: included(sst_entry::FILE_PATH),
2201 index_file_path: included(sst_entry::INDEX_FILE_PATH),
2202 partition_expr: included(sst_entry::PARTITION_EXPR),
2203 primary_key_min: included(sst_entry::PRIMARY_KEY_MIN),
2204 primary_key_max: included(sst_entry::PRIMARY_KEY_MAX),
2205 }
2206 }
2207}
2208
2209fn manifest_file_matches(meta: &FileMeta, filters: &MetadataFilter) -> bool {
2212 filters.matches(|column| {
2213 Some(match column {
2214 sst_entry::FILE_SIZE => ScalarValue::UInt64(Some(meta.file_size)),
2215 sst_entry::LEVEL => ScalarValue::UInt8(Some(meta.level)),
2216 sst_entry::NUM_ROWS => ScalarValue::UInt64(Some(meta.num_rows)),
2217 sst_entry::NUM_ROW_GROUPS => ScalarValue::UInt64(Some(meta.num_row_groups)),
2218 sst_entry::ORIGIN_REGION_ID => ScalarValue::UInt64(Some(meta.region_id.as_u64())),
2219 sst_entry::MIN_TS => ScalarValue::TimestampNanosecond(
2220 meta.time_range
2221 .0
2222 .convert_to(TimeUnit::Nanosecond)
2223 .map(|ts| ts.value()),
2224 None,
2225 ),
2226 sst_entry::MAX_TS => ScalarValue::TimestampNanosecond(
2227 meta.time_range
2228 .1
2229 .convert_to(TimeUnit::Nanosecond)
2230 .map(|ts| ts.value()),
2231 None,
2232 ),
2233 _ => return None,
2234 })
2235 })
2236}
2237
2238#[cfg(test)]
2239mod tests {
2240 use std::sync::Arc;
2241
2242 use common_datasource::compression::CompressionType;
2243 use common_test_util::temp_dir::create_temp_dir;
2244 use crossbeam_utils::atomic::AtomicCell;
2245 use object_store::ObjectStore;
2246 use object_store::services::Fs;
2247 use store_api::logstore::provider::Provider;
2248 use store_api::region_engine::RegionRole;
2249 use store_api::region_request::PathType;
2250 use store_api::storage::{FileId, RegionId};
2251
2252 use crate::access_layer::AccessLayer;
2253 use crate::error::Error;
2254 use crate::manifest::action::{
2255 RegionChange, RegionEdit, RegionMetaAction, RegionMetaActionList, RegionPartitionExprChange,
2256 };
2257 use crate::manifest::manager::{RegionManifestManager, RegionManifestOptions};
2258 use crate::region::{
2259 IndexBuildSource, IndexPublication, IndexPublicationStale, ManifestContext, ManifestStats,
2260 MitoRegion, RegionLeaderState, RegionRoleState, RegionStats,
2261 };
2262 use crate::sst::FormatType;
2263 use crate::sst::index::intermediate::IntermediateManager;
2264 use crate::sst::index::puffin_manager::PuffinManagerFactory;
2265 use crate::test_util::scheduler_util::SchedulerEnv;
2266 use crate::test_util::version_util::VersionControlBuilder;
2267 use crate::time_provider::StdTimeProvider;
2268
2269 #[test]
2270 fn test_manifest_file_predicates() {
2271 use common_recordbatch::filter::MetadataFilter;
2272 use common_time::Timestamp;
2273 use datafusion_common::ScalarValue;
2274 use datafusion_expr::{col, lit};
2275 let meta = super::FileMeta {
2276 file_size: 100,
2277 time_range: (
2278 Timestamp::new_millisecond(10),
2279 Timestamp::new_millisecond(20),
2280 ),
2281 ..Default::default()
2282 };
2283 for (expr, expected) in [
2284 (col("file_size").lt(lit(101u64)), true),
2285 (col("file_size").lt(lit(100u64)), false),
2286 (
2287 col("min_ts").eq(lit(ScalarValue::TimestampNanosecond(
2288 Some(10_000_000),
2289 None,
2290 ))),
2291 true,
2292 ),
2293 (
2294 col("max_ts").lt(lit(ScalarValue::TimestampNanosecond(
2295 Some(20_000_000),
2296 None,
2297 ))),
2298 false,
2299 ),
2300 (col("file_id").eq(lit("unknown")), true),
2301 ] {
2302 let filter = MetadataFilter::new(std::slice::from_ref(&expr));
2303 assert_eq!(
2304 super::manifest_file_matches(&meta, &filter),
2305 expected,
2306 "{expr}"
2307 );
2308 }
2309 }
2310
2311 #[test]
2312 fn test_manifest_timestamp_overflow_is_null() {
2313 use common_recordbatch::filter::MetadataFilter;
2314 use common_time::Timestamp;
2315 use datafusion_expr::col;
2316 let meta = super::FileMeta {
2317 time_range: (
2318 Timestamp::new_second(i64::MAX),
2319 Timestamp::new_second(i64::MAX),
2320 ),
2321 ..Default::default()
2322 };
2323 let filter = MetadataFilter::new(&[col("min_ts").is_null()]);
2324 assert!(super::manifest_file_matches(&meta, &filter));
2325 let filter = MetadataFilter::new(&[col("max_ts").is_not_null()]);
2326 assert!(!super::manifest_file_matches(&meta, &filter));
2327 }
2328
2329 #[test]
2330 fn test_region_state_lock_free() {
2331 assert!(AtomicCell::<RegionRoleState>::is_lock_free());
2332 }
2333
2334 #[test]
2335 fn test_region_role_state_as_str() {
2336 assert_eq!("Follower", RegionRoleState::Follower.as_str());
2337 assert_eq!(
2338 "Leader(Writable)",
2339 RegionRoleState::Leader(RegionLeaderState::Writable).as_str()
2340 );
2341 assert_eq!(
2342 "Leader(Staging)",
2343 RegionRoleState::Leader(RegionLeaderState::Staging).as_str()
2344 );
2345 assert_eq!(
2346 "Leader(Downgrading)",
2347 RegionRoleState::Leader(RegionLeaderState::Downgrading).as_str()
2348 );
2349 }
2350
2351 async fn build_test_region(env: &SchedulerEnv) -> MitoRegion {
2352 let builder = VersionControlBuilder::new();
2353 let version_control = Arc::new(builder.build());
2354 let metadata = version_control.current().version.metadata.clone();
2355
2356 let manager = RegionManifestManager::new(
2357 metadata.clone(),
2358 0,
2359 RegionManifestOptions {
2360 manifest_dir: "".to_string(),
2361 object_store: env.access_layer.object_store().clone(),
2362 compress_type: CompressionType::Uncompressed,
2363 checkpoint_distance: 10,
2364 remove_file_options: Default::default(),
2365 manifest_cache: None,
2366 conditional_publication: false,
2367 },
2368 FormatType::PrimaryKey,
2369 false,
2370 &Default::default(),
2371 )
2372 .await
2373 .unwrap();
2374
2375 let manifest_ctx = Arc::new(ManifestContext::new(
2376 manager,
2377 RegionRoleState::Leader(RegionLeaderState::Writable),
2378 None,
2379 Default::default(),
2380 ));
2381
2382 MitoRegion {
2383 region_id: metadata.region_id,
2384 compaction_state: Default::default(),
2385 version_control,
2386 series_index_version_control: Default::default(),
2387 series_index_store: None,
2388 access_layer: env.access_layer.clone(),
2389 manifest_ctx,
2390 file_purger: crate::test_util::new_noop_file_purger(),
2391 provider: Provider::noop_provider(),
2392 last_flush_millis: Default::default(),
2393 last_schedule_compaction_millis: Default::default(),
2394 time_provider: Arc::new(StdTimeProvider),
2395 topic_latest_entry_id: Default::default(),
2396 region_stats: RegionStats::new(),
2397 stats: ManifestStats::default(),
2398 }
2399 }
2400
2401 #[tokio::test]
2402 async fn test_manifest_stream_does_not_pin_sst_handles() {
2403 use std::sync::atomic::{AtomicUsize, Ordering};
2404
2405 use common_recordbatch::filter::MetadataFilter;
2406 use datafusion_expr::{col, lit};
2407 use futures::StreamExt;
2408
2409 use super::ManifestSstProjection;
2410 use crate::sst::file::FileMeta;
2411 use crate::sst::file_purger::FilePurger;
2412
2413 #[derive(Debug, Default)]
2414 struct CountingPurger(AtomicUsize);
2415
2416 impl FilePurger for CountingPurger {
2417 fn purge_file(&self, _: FileMeta) {
2418 self.0.fetch_add(1, Ordering::SeqCst);
2419 }
2420 }
2421
2422 for include_visible in [false, true] {
2423 let env = SchedulerEnv::new().await;
2424 let region = build_test_region(&env).await;
2425 let purger = Arc::new(CountingPurger::default());
2426 let files = (0..2)
2427 .map(|_| FileMeta {
2428 region_id: region.region_id,
2429 file_id: FileId::random(),
2430 ..Default::default()
2431 })
2432 .collect::<Vec<_>>();
2433 let mut edit = empty_edit();
2434 edit.files_to_add = files.clone();
2435 region
2436 .manifest_ctx
2437 .manifest_manager
2438 .write()
2439 .await
2440 .update(
2441 RegionMetaActionList::with_action(RegionMetaAction::Edit(edit.clone())),
2442 false,
2443 )
2444 .await
2445 .unwrap();
2446 region
2447 .version_control
2448 .apply_edit(Some(edit), &[], purger.clone());
2449
2450 let schema = store_api::sst_entry::ManifestSstEntry::schema();
2451 let columns = if include_visible {
2452 vec!["file_id", "visible"]
2453 } else {
2454 vec!["file_id"]
2455 };
2456 let projection = columns
2457 .iter()
2458 .map(|name| {
2459 schema
2460 .column_schemas()
2461 .iter()
2462 .position(|column| column.name == *name)
2463 .unwrap()
2464 })
2465 .collect::<Vec<_>>();
2466 let projection = ManifestSstProjection::new(Some(&projection));
2467 let snapshot = region.manifest_visible_file_ids(projection);
2468 assert_eq!(snapshot.is_some(), include_visible);
2469 if let Some(snapshot) = snapshot {
2470 assert_eq!(snapshot, files.iter().map(|file| file.file_id).collect());
2471 }
2472 let filters = MetadataFilter::new(&[col("visible").eq(lit(true))]);
2475 let mut stream = region.manifest_sst_stream(filters, projection);
2476 let first = stream.next().await.unwrap();
2477 if include_visible {
2478 assert!(first.visible);
2479 }
2480 let mut removal = empty_edit();
2481 removal.files_to_remove = files;
2482 region
2483 .version_control
2484 .apply_edit(Some(removal), &[], purger.clone());
2485 assert_eq!(purger.0.load(Ordering::SeqCst), 2);
2488 let second = stream.next().await.unwrap();
2489 if include_visible {
2490 assert!(
2491 second.visible,
2492 "visibility must retain the original snapshot"
2493 );
2494 }
2495 assert!(stream.next().await.is_none());
2496 }
2497 }
2498
2499 fn empty_edit() -> RegionEdit {
2500 RegionEdit {
2501 files_to_add: Vec::new(),
2502 files_to_remove: Vec::new(),
2503 timestamp_ms: None,
2504 compaction_time_window: None,
2505 flushed_entry_id: None,
2506 flushed_sequence: None,
2507 committed_sequence: None,
2508 }
2509 }
2510
2511 #[tokio::test]
2512 async fn test_promoting_writable_leader_takes_no_lock() {
2513 let env = SchedulerEnv::new().await;
2514 let region = Arc::new(build_test_region(&env).await);
2515 let _downgrade_started_at = region.manifest_ctx.downgrade_started_at.lock().unwrap();
2516 let (tx, rx) = std::sync::mpsc::channel();
2517 let promoted = region.clone();
2518 std::thread::spawn(move || {
2519 let manifest_ctx = &promoted.manifest_ctx;
2520 let issued_at = std::time::Instant::now();
2521 tx.send(
2522 manifest_ctx.promote_unless_downgrade_started_after(issued_at, promoted.region_id),
2523 )
2524 .unwrap();
2525 });
2526 assert!(rx.recv_timeout(std::time::Duration::from_secs(10)).unwrap());
2527 }
2528
2529 #[tokio::test]
2530 async fn test_compaction_update_manifest_allows_editing_state() {
2531 let env = SchedulerEnv::new().await;
2532 let region = build_test_region(&env).await;
2533 region.set_editing(RegionLeaderState::Writable).unwrap();
2534
2535 let file_id = FileId::random();
2536 let action_list = RegionMetaActionList::with_action(RegionMetaAction::Edit(RegionEdit {
2537 files_to_add: vec![crate::sst::file::FileMeta {
2538 region_id: region.region_id,
2539 file_id,
2540 level: 1,
2541 ..Default::default()
2542 }],
2543 files_to_remove: Vec::new(),
2544 timestamp_ms: None,
2545 compaction_time_window: None,
2546 flushed_entry_id: None,
2547 flushed_sequence: None,
2548 committed_sequence: None,
2549 }));
2550
2551 region
2552 .manifest_ctx
2553 .update_manifest_for_compaction(action_list)
2554 .await
2555 .unwrap();
2556
2557 assert!(
2558 region
2559 .manifest_ctx
2560 .manifest()
2561 .await
2562 .files
2563 .contains_key(&file_id)
2564 );
2565 }
2566
2567 #[tokio::test]
2568 async fn test_index_publication_rejects_older_source_generation() {
2569 let env = SchedulerEnv::new().await;
2570 let region = build_test_region(&env).await;
2571 let source = crate::sst::file::FileMeta {
2572 region_id: region.region_id,
2573 file_id: FileId::random(),
2574 level: 1,
2575 file_size: 1024,
2576 ..Default::default()
2577 };
2578 region
2579 .manifest_ctx
2580 .update_manifest(
2581 RegionLeaderState::Writable,
2582 RegionMetaActionList::with_action(RegionMetaAction::Edit(RegionEdit {
2583 files_to_add: vec![source.clone()],
2584 ..empty_edit()
2585 })),
2586 false,
2587 )
2588 .await
2589 .unwrap();
2590
2591 let mut first_update = source.clone();
2592 first_update.index_version = 1;
2593 first_update.index_file_size = 128;
2594 let schema_version = region.version().metadata.schema_version;
2595 let initial_source = IndexBuildSource::new(source.clone(), schema_version);
2596 let first_committed = match region
2597 .manifest_ctx
2598 .update_manifest_for_index(&initial_source, first_update)
2599 .await
2600 .unwrap()
2601 {
2602 IndexPublication::Committed { file_meta, .. } => file_meta,
2603 IndexPublication::Stale(_) => panic!("first index publication should commit"),
2604 };
2605
2606 let (ready_tx, ready_rx) = tokio::sync::oneshot::channel();
2607 let (release_tx, release_rx) = tokio::sync::oneshot::channel();
2608 let delayed_manifest_ctx = region.manifest_ctx.clone();
2609 let delayed_source = IndexBuildSource::new(first_committed.clone(), schema_version);
2610 let delayed_publication = tokio::spawn(async move {
2611 let mut delayed_update = delayed_source.file_meta.clone();
2612 delayed_update.index_version = 2;
2613 delayed_update.index_file_size = 128;
2614 ready_tx.send(()).unwrap();
2615 release_rx.await.unwrap();
2616 delayed_manifest_ctx
2617 .update_manifest_for_index(&delayed_source, delayed_update)
2618 .await
2619 });
2620 ready_rx.await.unwrap();
2621
2622 let mut newer_update = first_committed.clone();
2623 newer_update.index_version = 3;
2624 newer_update.index_file_size = 256;
2625 let newer_source = IndexBuildSource::new(first_committed, schema_version);
2626 let newer_committed = match region
2627 .manifest_ctx
2628 .update_manifest_for_index(&newer_source, newer_update)
2629 .await
2630 .unwrap()
2631 {
2632 IndexPublication::Committed { file_meta, .. } => file_meta,
2633 IndexPublication::Stale(_) => panic!("newer index publication should commit"),
2634 };
2635
2636 release_tx.send(()).unwrap();
2637 assert!(matches!(
2638 delayed_publication.await.unwrap().unwrap(),
2639 IndexPublication::Stale(IndexPublicationStale::SourceChanged)
2640 ));
2641 assert_eq!(
2642 region
2643 .manifest_ctx
2644 .manifest()
2645 .await
2646 .files
2647 .get(&source.file_id),
2648 Some(&newer_committed)
2649 );
2650 }
2651
2652 #[tokio::test]
2653 async fn test_index_publication_rejects_older_schema_generation() {
2654 let env = SchedulerEnv::new().await;
2655 let region = build_test_region(&env).await;
2656 let source_meta = crate::sst::file::FileMeta {
2657 region_id: region.region_id,
2658 file_id: FileId::random(),
2659 level: 1,
2660 file_size: 1024,
2661 ..Default::default()
2662 };
2663 region
2664 .manifest_ctx
2665 .update_manifest(
2666 RegionLeaderState::Writable,
2667 RegionMetaActionList::with_action(RegionMetaAction::Edit(RegionEdit {
2668 files_to_add: vec![source_meta.clone()],
2669 ..empty_edit()
2670 })),
2671 false,
2672 )
2673 .await
2674 .unwrap();
2675
2676 let old_schema_version = region.version().metadata.schema_version;
2677 let source = IndexBuildSource::new(source_meta.clone(), old_schema_version);
2678 let mut new_metadata = region.version().metadata.as_ref().clone();
2679 new_metadata.schema_version += 1;
2680 region
2681 .manifest_ctx
2682 .update_manifest(
2683 RegionLeaderState::Writable,
2684 RegionMetaActionList::with_action(RegionMetaAction::Change(RegionChange {
2685 metadata: Arc::new(new_metadata),
2686 sst_format: FormatType::PrimaryKey,
2687 append_mode: None,
2688 })),
2689 false,
2690 )
2691 .await
2692 .unwrap();
2693
2694 let mut updated = source_meta.clone();
2695 updated.index_version = 1;
2696 updated.index_file_size = 128;
2697 assert!(matches!(
2698 region
2699 .manifest_ctx
2700 .update_manifest_for_index(&source, updated)
2701 .await
2702 .unwrap(),
2703 IndexPublication::Stale(IndexPublicationStale::SchemaChanged)
2704 ));
2705
2706 let manifest = region.manifest_ctx.manifest().await;
2707 assert_eq!(manifest.metadata.schema_version, old_schema_version + 1);
2708 assert_eq!(manifest.files.get(&source_meta.file_id), Some(&source_meta));
2709 }
2710
2711 #[tokio::test]
2712 async fn test_exit_staging_partition_expr_change_and_edit_success() {
2713 let env = SchedulerEnv::new().await;
2714 let region = build_test_region(&env).await;
2715
2716 let mut manager = region.manifest_ctx.manifest_manager.write().await;
2717 region.set_staging(&mut manager).await.unwrap();
2718 manager
2719 .update(
2720 RegionMetaActionList::new(vec![
2721 RegionMetaAction::PartitionExprChange(RegionPartitionExprChange {
2722 partition_expr: Some("expr_a".to_string()),
2723 }),
2724 RegionMetaAction::Edit(empty_edit()),
2725 ]),
2726 true,
2727 )
2728 .await
2729 .unwrap();
2730
2731 let _hook_payload = region.exit_staging_on_success(&mut manager).await.unwrap();
2732 drop(manager);
2733
2734 assert_eq!(
2735 region.version().metadata.partition_expr.as_deref(),
2736 Some("expr_a")
2737 );
2738 assert_eq!(
2739 region.state(),
2740 RegionRoleState::Leader(RegionLeaderState::Writable)
2741 );
2742 }
2743
2744 #[tokio::test]
2745 async fn test_exit_staging_change_with_same_columns_success() {
2746 let env = SchedulerEnv::new().await;
2747 let region = build_test_region(&env).await;
2748
2749 let mut manager = region.manifest_ctx.manifest_manager.write().await;
2750 region.set_staging(&mut manager).await.unwrap();
2751
2752 let mut changed_metadata = region.version().metadata.as_ref().clone();
2753 changed_metadata.set_partition_expr(Some("expr_b".to_string()));
2754
2755 manager
2756 .update(
2757 RegionMetaActionList::new(vec![
2758 RegionMetaAction::Change(RegionChange {
2759 metadata: Arc::new(changed_metadata),
2760 sst_format: FormatType::PrimaryKey,
2761 append_mode: None,
2762 }),
2763 RegionMetaAction::Edit(empty_edit()),
2764 ]),
2765 true,
2766 )
2767 .await
2768 .unwrap();
2769
2770 let _hook_payload = region.exit_staging_on_success(&mut manager).await.unwrap();
2771 drop(manager);
2772
2773 assert_eq!(
2774 region.version().metadata.partition_expr.as_deref(),
2775 Some("expr_b")
2776 );
2777 assert_eq!(
2778 region.state(),
2779 RegionRoleState::Leader(RegionLeaderState::Writable)
2780 );
2781 }
2782
2783 #[tokio::test]
2784 async fn test_exit_staging_change_with_different_columns_fails() {
2785 let env = SchedulerEnv::new().await;
2786 let region = build_test_region(&env).await;
2787
2788 let mut manager = region.manifest_ctx.manifest_manager.write().await;
2789 region.set_staging(&mut manager).await.unwrap();
2790
2791 let mut changed_metadata = region.version().metadata.as_ref().clone();
2792 changed_metadata.column_metadatas.rotate_left(1);
2793
2794 manager
2795 .update(
2796 RegionMetaActionList::new(vec![
2797 RegionMetaAction::Change(RegionChange {
2798 metadata: Arc::new(changed_metadata),
2799 sst_format: FormatType::PrimaryKey,
2800 append_mode: None,
2801 }),
2802 RegionMetaAction::Edit(empty_edit()),
2803 ]),
2804 true,
2805 )
2806 .await
2807 .unwrap();
2808
2809 let result = region.exit_staging_on_success(&mut manager).await;
2810 assert!(matches!(result, Err(Error::Unexpected { .. })));
2811 }
2812
2813 #[tokio::test]
2814 async fn test_exit_staging_partition_expr_change_and_change_conflict_fails() {
2815 let env = SchedulerEnv::new().await;
2816 let region = build_test_region(&env).await;
2817
2818 let mut manager = region.manifest_ctx.manifest_manager.write().await;
2819 region.set_staging(&mut manager).await.unwrap();
2820
2821 let mut changed_metadata = region.version().metadata.as_ref().clone();
2822 changed_metadata.set_partition_expr(Some("expr_c".to_string()));
2823
2824 manager
2825 .update(
2826 RegionMetaActionList::new(vec![
2827 RegionMetaAction::PartitionExprChange(RegionPartitionExprChange {
2828 partition_expr: Some("expr_c".to_string()),
2829 }),
2830 RegionMetaAction::Change(RegionChange {
2831 metadata: Arc::new(changed_metadata),
2832 sst_format: FormatType::PrimaryKey,
2833 append_mode: None,
2834 }),
2835 RegionMetaAction::Edit(empty_edit()),
2836 ]),
2837 true,
2838 )
2839 .await
2840 .unwrap();
2841
2842 let result = region.exit_staging_on_success(&mut manager).await;
2843 assert!(matches!(result, Err(Error::Unexpected { .. })));
2844 }
2845
2846 #[tokio::test]
2847 async fn test_set_region_state() {
2848 let env = SchedulerEnv::new().await;
2849 let builder = VersionControlBuilder::new();
2850 let version_control = Arc::new(builder.build());
2851 let manifest_ctx = env
2852 .mock_manifest_context(version_control.current().version.metadata.clone())
2853 .await;
2854
2855 let region_id = RegionId::new(1024, 0);
2856 manifest_ctx.set_role(RegionRole::Follower, region_id);
2858 assert_eq!(manifest_ctx.state.load(), RegionRoleState::Follower);
2859
2860 manifest_ctx.set_role(RegionRole::Leader, region_id);
2862 assert_eq!(
2863 manifest_ctx.state.load(),
2864 RegionRoleState::Leader(RegionLeaderState::Writable)
2865 );
2866
2867 manifest_ctx.set_role(RegionRole::StagingLeader, region_id);
2869 assert_eq!(
2870 manifest_ctx.state.load(),
2871 RegionRoleState::Leader(RegionLeaderState::Writable)
2872 );
2873
2874 manifest_ctx.set_role(RegionRole::DowngradingLeader, region_id);
2876 assert_eq!(
2877 manifest_ctx.state.load(),
2878 RegionRoleState::Leader(RegionLeaderState::Downgrading)
2879 );
2880
2881 manifest_ctx.set_role(RegionRole::Follower, region_id);
2883 assert_eq!(manifest_ctx.state.load(), RegionRoleState::Follower);
2884
2885 manifest_ctx.set_role(RegionRole::DowngradingLeader, region_id);
2887 assert_eq!(manifest_ctx.state.load(), RegionRoleState::Follower);
2888
2889 manifest_ctx.set_role(RegionRole::Leader, region_id);
2891 manifest_ctx.set_role(RegionRole::DowngradingLeader, region_id);
2892 assert_eq!(
2893 manifest_ctx.state.load(),
2894 RegionRoleState::Leader(RegionLeaderState::Downgrading)
2895 );
2896
2897 manifest_ctx.set_role(RegionRole::Leader, region_id);
2899 assert_eq!(
2900 manifest_ctx.state.load(),
2901 RegionRoleState::Leader(RegionLeaderState::Writable)
2902 );
2903 }
2904
2905 #[tokio::test]
2906 async fn test_staging_state_validation() {
2907 let env = SchedulerEnv::new().await;
2908 let builder = VersionControlBuilder::new();
2909 let version_control = Arc::new(builder.build());
2910
2911 let staging_ctx = {
2913 let manager = RegionManifestManager::new(
2914 version_control.current().version.metadata.clone(),
2915 0,
2916 RegionManifestOptions {
2917 manifest_dir: "".to_string(),
2918 object_store: env.access_layer.object_store().clone(),
2919 compress_type: CompressionType::Uncompressed,
2920 checkpoint_distance: 10,
2921 remove_file_options: Default::default(),
2922 manifest_cache: None,
2923 conditional_publication: false,
2924 },
2925 FormatType::PrimaryKey,
2926 false,
2927 &Default::default(),
2928 )
2929 .await
2930 .unwrap();
2931 Arc::new(ManifestContext::new(
2932 manager,
2933 RegionRoleState::Leader(RegionLeaderState::Staging),
2934 None,
2935 Default::default(),
2936 ))
2937 };
2938
2939 assert_eq!(
2941 staging_ctx.current_state(),
2942 RegionRoleState::Leader(RegionLeaderState::Staging)
2943 );
2944
2945 let writable_ctx = env
2947 .mock_manifest_context(version_control.current().version.metadata.clone())
2948 .await;
2949
2950 assert_eq!(
2951 writable_ctx.current_state(),
2952 RegionRoleState::Leader(RegionLeaderState::Writable)
2953 );
2954 }
2955
2956 #[tokio::test]
2957 async fn test_staging_state_transitions() {
2958 let builder = VersionControlBuilder::new();
2959 let version_control = Arc::new(builder.build());
2960 let metadata = version_control.current().version.metadata.clone();
2961
2962 let temp_dir = create_temp_dir("");
2964 let path_str = temp_dir.path().display().to_string();
2965 let fs_builder = Fs::default().root(&path_str);
2966 let object_store = ObjectStore::new(fs_builder).unwrap();
2967
2968 let index_aux_path = temp_dir.path().join("index_aux");
2969 let puffin_mgr = PuffinManagerFactory::new(&index_aux_path, 4096, None, None)
2970 .await
2971 .unwrap();
2972 let intm_mgr = IntermediateManager::init_fs(index_aux_path.to_str().unwrap())
2973 .await
2974 .unwrap();
2975
2976 let access_layer = Arc::new(AccessLayer::new(
2977 "",
2978 PathType::Bare,
2979 object_store,
2980 puffin_mgr,
2981 intm_mgr,
2982 ));
2983
2984 let manager = RegionManifestManager::new(
2985 metadata.clone(),
2986 0,
2987 RegionManifestOptions {
2988 manifest_dir: "".to_string(),
2989 object_store: access_layer.object_store().clone(),
2990 compress_type: CompressionType::Uncompressed,
2991 checkpoint_distance: 10,
2992 remove_file_options: Default::default(),
2993 manifest_cache: None,
2994 conditional_publication: false,
2995 },
2996 FormatType::PrimaryKey,
2997 false,
2998 &Default::default(),
2999 )
3000 .await
3001 .unwrap();
3002
3003 let manifest_ctx = Arc::new(ManifestContext::new(
3004 manager,
3005 RegionRoleState::Leader(RegionLeaderState::Writable),
3006 None,
3007 Default::default(),
3008 ));
3009
3010 let region = MitoRegion {
3011 region_id: metadata.region_id,
3012 compaction_state: Default::default(),
3013 version_control,
3014 series_index_version_control: Default::default(),
3015 series_index_store: None,
3016 access_layer,
3017 manifest_ctx: manifest_ctx.clone(),
3018 file_purger: crate::test_util::new_noop_file_purger(),
3019 provider: Provider::noop_provider(),
3020 last_flush_millis: Default::default(),
3021 last_schedule_compaction_millis: Default::default(),
3022 time_provider: Arc::new(StdTimeProvider),
3023 topic_latest_entry_id: Default::default(),
3024 region_stats: RegionStats::new(),
3025 stats: ManifestStats::default(),
3026 };
3027
3028 assert_eq!(
3030 region.state(),
3031 RegionRoleState::Leader(RegionLeaderState::Writable)
3032 );
3033 assert!(!region.is_staging());
3034
3035 let mut manager = manifest_ctx.manifest_manager.write().await;
3037 region.set_staging(&mut manager).await.unwrap();
3038 drop(manager);
3039 assert_eq!(
3040 region.state(),
3041 RegionRoleState::Leader(RegionLeaderState::Staging)
3042 );
3043 assert!(region.is_staging());
3044
3045 region.exit_staging().unwrap();
3047 assert_eq!(
3048 region.state(),
3049 RegionRoleState::Leader(RegionLeaderState::Writable)
3050 );
3051 assert!(!region.is_staging());
3052
3053 {
3055 let manager = manifest_ctx.manifest_manager.write().await;
3057 let dummy_actions = RegionMetaActionList::new(vec![]);
3058 let dummy_bytes = dummy_actions.encode().unwrap();
3059
3060 manager.store().save(100, &dummy_bytes, true).await.unwrap();
3062 manager.store().save(101, &dummy_bytes, true).await.unwrap();
3063 drop(manager);
3064
3065 let manager = manifest_ctx.manifest_manager.read().await;
3067 let dirty_manifests = manager.store().fetch_staging_manifests().await.unwrap();
3068 assert_eq!(
3069 dirty_manifests.len(),
3070 2,
3071 "Should have 2 dirty staging files"
3072 );
3073 drop(manager);
3074
3075 let mut manager = manifest_ctx.manifest_manager.write().await;
3077 region.set_staging(&mut manager).await.unwrap();
3078 drop(manager);
3079
3080 let manager = manifest_ctx.manifest_manager.read().await;
3082 let cleaned_manifests = manager.store().fetch_staging_manifests().await.unwrap();
3083 assert_eq!(
3084 cleaned_manifests.len(),
3085 0,
3086 "Dirty staging files should be cleaned up"
3087 );
3088 drop(manager);
3089
3090 region.exit_staging().unwrap();
3092 }
3093
3094 let mut manager = manifest_ctx.manifest_manager.write().await;
3096 assert!(region.set_staging(&mut manager).await.is_ok()); drop(manager);
3098 let mut manager = manifest_ctx.manifest_manager.write().await;
3099 assert!(region.set_staging(&mut manager).await.is_err()); drop(manager);
3101 assert!(region.exit_staging().is_ok()); assert!(region.exit_staging().is_err()); }
3104}