diff --git a/src/mito2/src/error.rs b/src/mito2/src/error.rs index 74ea47b653..86b310e1ac 100644 --- a/src/mito2/src/error.rs +++ b/src/mito2/src/error.rs @@ -490,6 +490,18 @@ pub enum Error { location: Location, }, + #[snafu(display( + "Region {} is in {:?} state, which does not permit manifest updates.", + region_id, + state + ))] + UpdateManifest { + region_id: RegionId, + state: RegionRoleState, + #[snafu(implicit)] + location: Location, + }, + #[snafu(display("Region {} is in {:?} state, expect: {:?}", region_id, state, expect))] RegionLeaderState { region_id: RegionId, @@ -1055,7 +1067,7 @@ impl ErrorExt for Error { CompactRegion { source, .. } => source.status_code(), CompatReader { .. } => StatusCode::Unexpected, InvalidRegionRequest { source, .. } => source.status_code(), - RegionLeaderState { .. } => StatusCode::RegionNotReady, + RegionLeaderState { .. } | UpdateManifest { .. } => StatusCode::RegionNotReady, &FlushableRegionState { .. } => StatusCode::RegionNotReady, JsonOptions { .. } => StatusCode::InvalidArguments, EmptyRegionDir { .. } | EmptyManifestDir { .. } => StatusCode::RegionNotFound, diff --git a/src/mito2/src/region.rs b/src/mito2/src/region.rs index 90d2f55e5e..4a5beb0cb5 100644 --- a/src/mito2/src/region.rs +++ b/src/mito2/src/region.rs @@ -36,7 +36,7 @@ use store_api::storage::RegionId; use crate::access_layer::AccessLayerRef; use crate::error::{ FlushableRegionStateSnafu, RegionLeaderStateSnafu, RegionNotFoundSnafu, RegionTruncatedSnafu, - Result, + Result, UpdateManifestSnafu, }; use crate::manifest::action::{RegionMetaAction, RegionMetaActionList}; use crate::manifest::manager::RegionManifestManager; @@ -371,14 +371,36 @@ impl ManifestContext { // Checks state inside the lock. This is to ensure that we won't update the manifest // after `set_readonly_gracefully()` is called. let current_state = self.state.load(); - ensure!( - current_state == RegionRoleState::Leader(expect_state), - RegionLeaderStateSnafu { - region_id: manifest.metadata.region_id, - state: current_state, - expect: expect_state, + + // If expect_state is not downgrading, the current state must be either `expect_state` or downgrading. + // + // A downgrading leader rejects user writes but still allows + // flushing the memtable and updating the manifest. + if expect_state != RegionLeaderState::Downgrading { + if current_state == RegionRoleState::Leader(RegionLeaderState::Downgrading) { + info!( + "Region {} is in downgrading leader state, updating manifest. state is {:?}", + manifest.metadata.region_id, expect_state + ); } - ); + ensure!( + current_state == RegionRoleState::Leader(expect_state) + || current_state == RegionRoleState::Leader(RegionLeaderState::Downgrading), + UpdateManifestSnafu { + region_id: manifest.metadata.region_id, + state: current_state, + } + ); + } else { + ensure!( + current_state == RegionRoleState::Leader(expect_state), + RegionLeaderStateSnafu { + region_id: manifest.metadata.region_id, + state: current_state, + expect: expect_state, + } + ); + } for action in &action_list.actions { // Checks whether the edit is still applicable.