refactor(app): add lifecycle state context boundary (#4099)

This commit is contained in:
Zhengchao An
2026-06-30 06:26:06 +08:00
committed by GitHub
parent 055d3ad825
commit 46edc118f0
8 changed files with 83 additions and 12 deletions
+1 -1
View File
@@ -42,7 +42,7 @@ migration PR removes or replaces each item.
| `GLOBAL_OBJECT_API`, `GLOBAL_OBJECT_STORE_RESOLVER` | `crates/ecstore/src/runtime/global.rs` | Runtime migration target | Do not migrate first; it is tied to storage startup, IAM-after-storage AppContext publication, and data-plane resolver compatibility. |
| `GLOBAL_ENDPOINTS`, `GLOBAL_IS_ERASURE`, `GLOBAL_IS_DIST_ERASURE`, `GLOBAL_IS_ERASURE_SD`, `GLOBAL_ROOT_DISK_THRESHOLD` | `crates/ecstore/src/runtime/global.rs` and `crates/ecstore/src/runtime/sources.rs` | Runtime migration target | Endpoint, setup-type, and root-disk-threshold access now stays behind ECStore runtime helpers; move endpoint ownership only after readiness and quorum behavior have explicit coverage. |
| `GLOBAL_LOCAL_DISK_MAP`, `GLOBAL_LOCAL_DISK_ID_MAP`, `GLOBAL_LOCAL_DISK_SET_DRIVES` | `crates/ecstore/src/runtime/global.rs` and `crates/ecstore/src/runtime/sources.rs` | Runtime migration target | Local disk map, disk-id cache, and set-drive access now stay behind ECStore runtime-source helpers instead of direct global access; preserve disk lookup, remote/local classification, and test reset hooks in later ownership changes. |
| `GLOBAL_EXPIRY_STATE`, `GLOBAL_TRANSITION_STATE`, `GLOBAL_LIFECYCLE_SYS` | `crates/ecstore/src/bucket/lifecycle/*`, `crates/ecstore/src/runtime/global.rs`, and `crates/ecstore/src/runtime/sources.rs` | Runtime migration target | Lifecycle state globals now stay behind ECStore lifecycle owner helpers and ECStore runtime-source helpers; scanner expiry-state access now uses the ECStore runtime `expiry_state_handle` boundary; `GLOBAL_EXPIRY_STATE` remains the first code-bearing ownership candidate because AppContext already has an `ExpiryStateInterface`, resolver, and tests. |
| `GLOBAL_EXPIRY_STATE`, `GLOBAL_TRANSITION_STATE`, `GLOBAL_LIFECYCLE_SYS` | `crates/ecstore/src/bucket/lifecycle/*`, `crates/ecstore/src/runtime/global.rs`, and `crates/ecstore/src/runtime/sources.rs` | Runtime migration target | Lifecycle state globals now stay behind ECStore lifecycle owner helpers and ECStore runtime-source helpers; RustFS AppContext has expiry/transition state interfaces and resolver coverage; scanner expiry-state access still uses the ECStore runtime `expiry_state_handle` boundary until scanner gets an injected provider. |
| `GLOBAL_REPLICATION_POOL`, `GLOBAL_REPLICATION_STATS`, `GLOBAL_BUCKET_MONITOR` | `crates/ecstore/src/bucket/replication/*`, `crates/ecstore/src/runtime/global.rs` | Runtime migration target | Replication pool/stat access now stays behind replication owner and ECStore runtime-source helpers; bucket-monitor direct access now stays behind ECStore runtime helpers while AppContext/runtime-source resolvers remain the caller boundary. |
| `GLOBAL_TIER_CONFIG_MGR`, `GLOBAL_STORAGE_CLASS`, `GLOBAL_CONFIG_SYS`, `GLOBAL_SERVER_CONFIG` | `crates/ecstore/src/config`, `crates/config`, `rustfs/src/app/context/runtime_sources.rs` | Runtime migration target | Tier config manager reads and reloads now use the ECStore runtime-source helper; move remaining config state through config/runtime-source owners only, without combining storage-class behavior or persistence changes. |
| `GLOBAL_EVENT_NOTIFIER`, `GLOBAL_NOTIFICATION_SYS` | `crates/ecstore/src/runtime/global.rs`, `crates/ecstore/src/runtime/sources.rs`, and `crates/ecstore/src/services/*` | Runtime migration target | `GLOBAL_EVENT_NOTIFIER` and `GLOBAL_NOTIFICATION_SYS` access now stay behind ECStore runtime-source and notification owner helpers; move remaining notification ownership only through notify/runtime-source boundaries. |
+30 -2
View File
@@ -29,7 +29,7 @@ pub use interfaces::*;
use super::storage_api::context::bucket::metadata_sys::BucketMetadataSys;
use super::storage_api::context::runtime::{
BucketBandwidthMonitor, DailyAllTierStats, DynReplicationPool, ExpiryState, NotificationSys, ReplicationStats,
ScannerMetricsReport, StorageClassConfig, TierConfigMgr,
ScannerMetricsReport, StorageClassConfig, TierConfigMgr, TransitionState,
};
use super::storage_api::context::{ECStore, EndpointServerPools};
use crate::config::RustFSBufferConfig;
@@ -248,6 +248,11 @@ pub fn resolve_expiry_state_handle() -> Option<Arc<RwLock<ExpiryState>>> {
resolve_expiry_state_handle_with(get_global_app_context())
}
/// Resolve lifecycle transition state using AppContext-first precedence.
pub fn resolve_transition_state_handle() -> Option<Arc<TransitionState>> {
resolve_transition_state_handle_with(get_global_app_context())
}
/// Resolve server config using AppContext-first precedence.
pub fn resolve_server_config() -> Option<Config> {
resolve_server_config_with(get_global_app_context())
@@ -444,6 +449,10 @@ fn resolve_expiry_state_handle_with(context: Option<Arc<AppContext>>) -> Option<
context.map(|context| context.expiry_state().handle())
}
fn resolve_transition_state_handle_with(context: Option<Arc<AppContext>>) -> Option<Arc<TransitionState>> {
context.map(|context| context.transition_state().handle())
}
fn resolve_server_config_with(context: Option<Arc<AppContext>>) -> Option<Config> {
context.and_then(|context| context.server_config().get())
}
@@ -485,7 +494,7 @@ mod tests {
EndpointsInterface, IamInterface, InternodeMetricsInterface, KmsInterface, KmsRuntimeInterface, LocalNodeNameInterface,
LockClientInterface, LockClientsInterface, OidcInterface, OutboundTlsRuntimeInterface, PerformanceMetricsInterface,
RegionInterface, ReplicationStatsInterface, RuntimePortInterface, S3SelectDbInterface, ScannerMetricsInterface,
ServerConfigInterface, StorageClassInterface, TierConfigInterface, TierStatsInterface,
ServerConfigInterface, StorageClassInterface, TierConfigInterface, TierStatsInterface, TransitionStateInterface,
};
use crate::config::{RustFSBufferConfig, WorkloadProfile};
use async_trait::async_trait;
@@ -798,6 +807,16 @@ mod tests {
}
}
struct TestTransitionStateInterface {
transition_state: Arc<TransitionState>,
}
impl TransitionStateInterface for TestTransitionStateInterface {
fn handle(&self) -> Arc<TransitionState> {
self.transition_state.clone()
}
}
struct TestServerConfigInterface {
config: Option<Config>,
published: Arc<AtomicUsize>,
@@ -937,6 +956,7 @@ mod tests {
};
let tier_config = TierConfigMgr::new();
let context_expiry_state = ExpiryState::new();
let context_transition_state = TransitionState::new();
let server_config = Config::new();
let context_server_config_published = Arc::new(AtomicUsize::new(0));
let context_storage_class_published = Arc::new(AtomicUsize::new(0));
@@ -1047,6 +1067,9 @@ mod tests {
expiry_state: Arc::new(TestExpiryStateInterface {
expiry_state: context_expiry_state.clone(),
}),
transition_state: Arc::new(TestTransitionStateInterface {
transition_state: context_transition_state.clone(),
}),
server_config: Arc::new(TestServerConfigInterface {
config: Some(server_config.clone()),
published: context_server_config_published.clone(),
@@ -1176,6 +1199,10 @@ mod tests {
&resolve_expiry_state_handle_with(Some(context.clone())).expect("context expiry state"),
&context_expiry_state
));
assert!(Arc::ptr_eq(
&resolve_transition_state_handle_with(Some(context.clone())).expect("context transition state"),
&context_transition_state
));
assert_eq!(
resolve_server_config_with(Some(context.clone())).expect("context server config"),
server_config
@@ -1221,6 +1248,7 @@ mod tests {
assert!(resolve_region_with(None).is_none());
assert!(resolve_tier_config_handle_with(None).is_none());
assert!(resolve_expiry_state_handle_with(None).is_none());
assert!(resolve_transition_state_handle_with(None).is_none());
assert!(resolve_server_config_with(None).is_none());
assert!(!publish_server_config_with(None, Config::new()));
assert!(!publish_storage_class_config_with(None, StorageClassConfig::default()));
+11 -2
View File
@@ -23,7 +23,8 @@ use super::handles::{
default_outbound_tls_runtime_interface, default_performance_metrics_interface, default_region_interface,
default_replication_pool_interface, default_replication_stats_interface, default_runtime_port_interface,
default_s3select_db_interface, default_scanner_metrics_interface, default_server_config_interface,
default_storage_class_interface, default_tier_config_interface, default_tier_stats_interface, oidc_interface,
default_storage_class_interface, default_tier_config_interface, default_tier_stats_interface,
default_transition_state_interface, oidc_interface,
};
use super::interfaces::{
ActionCredentialInterface, BootTimeInterface, BucketMetadataInterface, BucketMonitorInterface, BufferConfigInterface,
@@ -31,7 +32,7 @@ use super::interfaces::{
KmsRuntimeInterface, LocalNodeNameInterface, LockClientInterface, LockClientsInterface, NotificationSystemInterface,
NotifyInterface, OidcInterface, OutboundTlsRuntimeInterface, PerformanceMetricsInterface, RegionInterface,
ReplicationPoolInterface, ReplicationStatsInterface, RuntimePortInterface, S3SelectDbInterface, ScannerMetricsInterface,
ServerConfigInterface, StorageClassInterface, TierConfigInterface, TierStatsInterface,
ServerConfigInterface, StorageClassInterface, TierConfigInterface, TierStatsInterface, TransitionStateInterface,
};
use rustfs_iam::{oidc::OidcSys, store::object::ObjectStore, sys::IamSys};
use rustfs_kms::KmsServiceManager;
@@ -69,6 +70,7 @@ pub struct AppContext {
region: Arc<dyn RegionInterface>,
tier_config: Arc<dyn TierConfigInterface>,
expiry_state: Arc<dyn ExpiryStateInterface>,
transition_state: Arc<dyn TransitionStateInterface>,
server_config: Arc<dyn ServerConfigInterface>,
storage_class: Arc<dyn StorageClassInterface>,
buffer_config: Arc<dyn BufferConfigInterface>,
@@ -105,6 +107,7 @@ impl AppContext {
region: default_region_interface(),
tier_config: default_tier_config_interface(),
expiry_state: default_expiry_state_interface(),
transition_state: default_transition_state_interface(),
server_config: default_server_config_interface(),
storage_class: default_storage_class_interface(),
buffer_config: default_buffer_config_interface(),
@@ -238,6 +241,10 @@ impl AppContext {
self.expiry_state.clone()
}
pub fn transition_state(&self) -> Arc<dyn TransitionStateInterface> {
self.transition_state.clone()
}
pub fn server_config(&self) -> Arc<dyn ServerConfigInterface> {
self.server_config.clone()
}
@@ -280,6 +287,7 @@ pub(super) struct AppContextTestInterfaces {
pub(super) region: Arc<dyn RegionInterface>,
pub(super) tier_config: Arc<dyn TierConfigInterface>,
pub(super) expiry_state: Arc<dyn ExpiryStateInterface>,
pub(super) transition_state: Arc<dyn TransitionStateInterface>,
pub(super) server_config: Arc<dyn ServerConfigInterface>,
pub(super) storage_class: Arc<dyn StorageClassInterface>,
pub(super) buffer_config: Arc<dyn BufferConfigInterface>,
@@ -317,6 +325,7 @@ impl AppContext {
region: interfaces.region,
tier_config: interfaces.tier_config,
expiry_state: interfaces.expiry_state,
transition_state: interfaces.transition_state,
server_config: interfaces.server_config,
storage_class: interfaces.storage_class,
buffer_config: interfaces.buffer_config,
+16 -2
View File
@@ -16,7 +16,7 @@ use super::super::storage_api::context::EndpointServerPools;
use super::super::storage_api::context::bucket::metadata_sys::BucketMetadataSys;
use super::super::storage_api::context::runtime::{
BucketBandwidthMonitor, DailyAllTierStats, DynReplicationPool, ExpiryState, NotificationSys, ReplicationStats,
ScannerMetricsReport, StorageClassConfig, TierConfigMgr,
ScannerMetricsReport, StorageClassConfig, TierConfigMgr, TransitionState,
};
use super::interfaces::{
ActionCredentialInterface, BootTimeInterface, BucketMetadataInterface, BucketMonitorInterface, BufferConfigInterface,
@@ -24,7 +24,7 @@ use super::interfaces::{
KmsRuntimeInterface, LocalNodeNameInterface, LockClientInterface, LockClientsInterface, NotificationSystemInterface,
NotifyInterface, OidcInterface, OutboundTlsRuntimeInterface, PerformanceMetricsInterface, RegionInterface,
ReplicationPoolInterface, ReplicationStatsInterface, RuntimePortInterface, S3SelectDbInterface, ScannerMetricsInterface,
ServerConfigInterface, StorageClassInterface, TierConfigInterface, TierStatsInterface,
ServerConfigInterface, StorageClassInterface, TierConfigInterface, TierStatsInterface, TransitionStateInterface,
};
use super::runtime_sources;
use crate::config::RustFSBufferConfig;
@@ -390,6 +390,16 @@ impl ExpiryStateInterface for ExpiryStateHandle {
}
}
/// Default lifecycle transition state interface adapter.
#[derive(Default)]
pub struct TransitionStateHandle;
impl TransitionStateInterface for TransitionStateHandle {
fn handle(&self) -> Arc<TransitionState> {
runtime_sources::transition_state()
}
}
/// Default server config interface adapter.
#[derive(Default)]
pub struct ServerConfigHandle;
@@ -528,6 +538,10 @@ pub fn default_expiry_state_interface() -> Arc<dyn ExpiryStateInterface> {
Arc::new(ExpiryStateHandle)
}
pub fn default_transition_state_interface() -> Arc<dyn TransitionStateInterface> {
Arc::new(TransitionStateHandle)
}
pub fn default_server_config_interface() -> Arc<dyn ServerConfigInterface> {
Arc::new(ServerConfigHandle)
}
+6 -1
View File
@@ -16,7 +16,7 @@ use super::super::storage_api::context::EndpointServerPools;
use super::super::storage_api::context::bucket::metadata_sys::BucketMetadataSys;
use super::super::storage_api::context::runtime::{
BucketBandwidthMonitor, DailyAllTierStats, DynReplicationPool, ExpiryState, NotificationSys, ReplicationStats,
ScannerMetricsReport, StorageClassConfig, TierConfigMgr,
ScannerMetricsReport, StorageClassConfig, TierConfigMgr, TransitionState,
};
use crate::config::RustFSBufferConfig;
use async_trait::async_trait;
@@ -197,6 +197,11 @@ pub trait ExpiryStateInterface: Send + Sync {
fn handle(&self) -> Arc<RwLock<ExpiryState>>;
}
/// Lifecycle transition state interface for transition queues and tier stats.
pub trait TransitionStateInterface: Send + Sync {
fn handle(&self) -> Arc<TransitionState>;
}
/// Server config interface for application-layer and server modules.
pub trait ServerConfigInterface: Send + Sync {
fn get(&self) -> Option<Config>;
+9 -4
View File
@@ -18,10 +18,11 @@ use super::super::storage_api::context::EndpointServerPools;
use super::super::storage_api::context::bucket::metadata_sys::BucketMetadataSys;
use super::super::storage_api::context::runtime::{
BucketBandwidthMonitor, DailyAllTierStats, DynReplicationPool, ExpiryState, NotificationSys, ReplicationStats,
ScannerMetricsReport, StorageClassConfig, TierConfigMgr, collect_scanner_metrics_report, get_daily_all_tier_stats,
get_global_boot_time, get_global_bucket_monitor, get_global_deployment_id, get_global_endpoints_opt, get_global_expiry_state,
get_global_lock_client, get_global_lock_clients, get_global_notification_sys, get_global_region, get_global_replication_pool,
get_global_replication_stats, get_global_tier_config_mgr, global_rustfs_port, set_global_storage_class,
ScannerMetricsReport, StorageClassConfig, TierConfigMgr, TransitionState, collect_scanner_metrics_report,
get_daily_all_tier_stats, get_global_boot_time, get_global_bucket_monitor, get_global_deployment_id,
get_global_endpoints_opt, get_global_expiry_state, get_global_lock_client, get_global_lock_clients,
get_global_notification_sys, get_global_region, get_global_replication_pool, get_global_replication_stats,
get_global_tier_config_mgr, get_global_transition_state, global_rustfs_port, set_global_storage_class,
};
use crate::config::{RustFSBufferConfig, get_global_buffer_config};
use rustfs_config::server_config::{Config, get_global_server_config, set_global_server_config};
@@ -180,6 +181,10 @@ pub fn expiry_state() -> Arc<RwLock<ExpiryState>> {
get_global_expiry_state()
}
pub fn transition_state() -> Arc<TransitionState> {
get_global_transition_state()
}
#[cfg(test)]
pub fn object_store() -> Option<Arc<ECStore>> {
super::super::storage_api::context::runtime::new_object_layer_fn()
+5
View File
@@ -125,6 +125,7 @@ pub(crate) mod runtime {
pub(crate) type ScannerMetricsReport = rustfs_common::metrics::ScannerMetricsReport;
pub(crate) type StorageClassConfig = crate::storage::storage_api::ecstore_config::storageclass::Config;
pub(crate) type TierConfigMgr = crate::storage::storage_api::TierConfigMgr;
pub(crate) type TransitionState = crate::storage::storage_api::TransitionState;
#[cfg(test)]
pub(crate) type TierConfig = crate::storage::storage_api::ecstore_tier::tier_config::TierConfig;
@@ -171,6 +172,10 @@ pub(crate) mod runtime {
crate::storage::storage_api::get_global_expiry_state()
}
pub(crate) fn get_global_transition_state() -> Arc<TransitionState> {
crate::storage::storage_api::get_global_transition_state()
}
#[cfg(test)]
pub(crate) fn new_object_layer_fn() -> Option<Arc<crate::storage::storage_api::ECStore>> {
crate::storage::storage_api::ecstore_global::new_object_layer_fn()
+5
View File
@@ -515,6 +515,7 @@ pub(crate) type ReplicationStats = ecstore_bucket::replication::ReplicationStats
pub(crate) type SetupType = ecstore_layout::SetupType;
pub(crate) type StorageError = ecstore_error::StorageError;
pub(crate) type TierConfigMgr = ecstore_tier::TierConfigMgr;
pub(crate) type TransitionState = ecstore_bucket::lifecycle::bucket_lifecycle_ops::TransitionState;
pub(crate) type Error = ecstore_error::Error;
pub(crate) type Result<T> = ecstore_error::Result<T>;
pub(crate) type UpdateMetadataOpts = ecstore_disk::UpdateMetadataOpts;
@@ -582,6 +583,10 @@ pub(crate) fn get_global_expiry_state() -> Arc<tokio::sync::RwLock<ExpiryState>>
ecstore_bucket::lifecycle::bucket_lifecycle_ops::get_global_expiry_state()
}
pub(crate) fn get_global_transition_state() -> Arc<TransitionState> {
ecstore_bucket::lifecycle::bucket_lifecycle_ops::get_global_transition_state()
}
pub(crate) async fn try_migrate_bucket_metadata(store: Arc<ECStore>) {
ecstore_bucket::migration::try_migrate_bucket_metadata(store).await;
}