From d8a53d090fb0fe699b7e4913304d03fdd615af8d Mon Sep 17 00:00:00 2001 From: Zhengchao An Date: Tue, 23 Jun 2026 13:14:00 +0800 Subject: [PATCH] refactor: route admin status metrics through app context (#3780) --- docs/architecture/migration-progress.md | 47 +++++++-- rustfs/src/admin/handlers/scanner.rs | 5 +- rustfs/src/admin/handlers/tier.rs | 7 +- rustfs/src/admin/mod.rs | 29 ------ rustfs/src/admin/router.rs | 11 +- rustfs/src/app/context.rs | 130 +++++++++++++++++++++++- rustfs/src/app/context/global.rs | 38 +++++-- rustfs/src/app/context/handles.rs | 60 +++++++++-- rustfs/src/app/context/interfaces.rs | 20 +++- rustfs/src/app/mod.rs | 16 +++ rustfs/src/storage/mod.rs | 9 ++ 11 files changed, 306 insertions(+), 66 deletions(-) diff --git a/docs/architecture/migration-progress.md b/docs/architecture/migration-progress.md index ddfdaaaa2..35768119f 100644 --- a/docs/architecture/migration-progress.md +++ b/docs/architecture/migration-progress.md @@ -5,13 +5,14 @@ Status values: `[ ]` not started, `[~]` in progress, `[x]` complete, `[!]` block ## Current Context - Issue: [`rustfs/backlog#660`](https://github.com/rustfs/backlog/issues/660) -- Branch: `overtrue/arch-admin-replication-stats-context` -- Baseline: completed `C-011/C-012/C-013/API-055/API-059/API-079/API-080/API-081/API-082/API-083/API-084/API-085/API-086/API-087/API-088/API-089/API-090/API-091/API-092/API-093/API-094/API-095/API-096/API-097/API-098/API-099/API-100/API-101/API-102/API-103/API-104/API-105/API-106/API-107/API-108/API-109/API-110/API-111/API-112/API-113/API-114/API-115/API-116/API-117/API-118/API-119/API-120/API-121/API-122/API-123/API-124/API-125/API-126/API-127/API-128/API-129/API-130/API-131/API-132/API-133/API-134/API-135/API-136/API-137/API-138/API-139/API-140/API-141/API-142/API-143/API-144/API-145/API-146/API-147/API-148/API-149/API-150/API-151/API-152/API-153/API-154/API-155/API-156/API-157/API-158/API-159/API-160/API-161/API-162/API-163/API-164/API-165`. -- Based on: API-164/API-165 local branch stacked on API-163 PR #3777. +- Branch: `overtrue/arch-admin-status-metrics-context` +- Baseline: completed `C-011/C-012/C-013/API-055/API-059/API-079/API-080/API-081/API-082/API-083/API-084/API-085/API-086/API-087/API-088/API-089/API-090/API-091/API-092/API-093/API-094/API-095/API-096/API-097/API-098/API-099/API-100/API-101/API-102/API-103/API-104/API-105/API-106/API-107/API-108/API-109/API-110/API-111/API-112/API-113/API-114/API-115/API-116/API-117/API-118/API-119/API-120/API-121/API-122/API-123/API-124/API-125/API-126/API-127/API-128/API-129/API-130/API-131/API-132/API-133/API-134/API-135/API-136/API-137/API-138/API-139/API-140/API-141/API-142/API-143/API-144/API-145/API-146/API-147/API-148/API-149/API-150/API-151/API-152/API-153/API-154/API-155/API-156/API-157/API-158/API-159/API-160/API-161/API-162/API-163/API-164/API-165/API-166`. +- Based on: API-166 local branch stacked on API-164/API-165 local branch after API-163 PR #3777 merged. - PR type for this branch: `consumer-migration` - Runtime behavior changes: none. -- Rust code changes: route admin replication stats reads through AppContext - resolvers with legacy global fallback. +- Rust code changes: route admin status and metrics reads for boot time, tier + transition stats, and scanner metrics through AppContext resolvers with + legacy global fallback. - CI/script changes: lock completed owner and test/fuzz boundaries against bare/glob imports, scattered raw ECStore facade subpaths, and startup runtime/root-server/table/S3/app shared/app bucket/app ECStore/admin facade @@ -20,7 +21,7 @@ Status values: `[ ]` not started, `[~]` in progress, `[x]` complete, `[!]` block and storage owner thin bridge regressions, plus app context and notify event-bridge thin module regressions; accept the reviewed AppContext resolver reverse dependencies in the layer baseline. -- Docs changes: record the API-136/API-137/API-138/API-139/API-140/API-141/API-142/API-143/API-144/API-145/API-146/API-147/API-148/API-149/API-150/API-151/API-152/API-153/API-154/API-155/API-156/API-157/API-158/API-159/API-160/API-161/API-162/API-163/API-164/API-165/API-166 owner facade cleanup. +- Docs changes: record the API-136/API-137/API-138/API-139/API-140/API-141/API-142/API-143/API-144/API-145/API-146/API-147/API-148/API-149/API-150/API-151/API-152/API-153/API-154/API-155/API-156/API-157/API-158/API-159/API-160/API-161/API-162/API-163/API-164/API-165/API-166/API-167 owner facade cleanup. ## Phase 0 Tasks @@ -4401,6 +4402,21 @@ Status values: `[ ]` not started, `[~]` in progress, `[x]` complete, `[!]` block stats global-read scan, Rust risk scan, branch freshness check, and three-expert review. +- [x] `API-167` Route admin status and metrics reads through AppContext. + - Do: add AppContext interfaces for boot time, daily tier transition stats, + and scanner metrics report reads, then route admin replication uptime, tier + stats, and scanner status through those resolvers. + - Acceptance: admin production handlers no longer directly read + `GLOBAL_BOOT_TIME`, `GLOBAL_TransitionState`, or scanner `global_metrics`, + while AppContext default adapters keep the existing global fallback. + - Must preserve: replication metrics uptime defaults, tier stats filtering, + scanner status payload shape, scanner runtime-config reporting, and existing + storage owner global initialization. + - Verification: RustFS compile coverage, targeted context resolver tests, + migration guard, layer guard, formatting, diff hygiene, residual admin + status global-read scan, Rust risk scan, branch freshness check, and + three-expert review. + ## Next PRs 1. `consumer-migration`: continue reducing direct global reads behind AppContext resolver boundaries. @@ -4455,6 +4471,9 @@ Status values: `[ ]` not started, `[~]` in progress, `[x]` complete, `[!]` block | Quality/architecture | pass | API-166 keeps admin replication stats reads behind AppContext resolver boundaries with a storage-owner fallback wrapper. | | Migration preservation | pass | Admin replication metrics, site-replication summaries, bandwidth enrichment, and missing-stats defaults are preserved. | | Testing/verification | pass | RustFS focused compile, targeted context tests, formatting, migration/layer guards, diff hygiene, residual stats scan, and Rust risk scan passed for API-166. | +| Quality/architecture | pass | API-167 keeps admin boot-time, tier-transition, and scanner metrics reads behind AppContext resolver boundaries. | +| Migration preservation | pass | Replication uptime enrichment, tier stats filtering, scanner metrics JSON, and scanner runtime-config reporting are preserved. | +| Testing/verification | pass | RustFS focused compile, targeted context tests, formatting, migration/layer guards, diff hygiene, residual admin status scan, and Rust risk scan passed for API-167. | ## Verification Notes @@ -4505,6 +4524,22 @@ Passed before push: - Rust risk scan: no new production unwrap/expect, panic/todo/unsafe, or cast risks added. +- Issue #660 API-167 current slice: + - `cargo check --tests -p rustfs`: passed. + - `cargo test -p rustfs resolver_helpers_are_context_first_and_fallback_when_context_is_absent --lib`: + passed. + - `cargo fmt --all`: passed. + - `cargo fmt --all --check`: passed. + - `git diff --check`: passed. + - `bash -n scripts/check_architecture_migration_rules.sh`: passed. + - `./scripts/check_architecture_migration_rules.sh`: passed. + - `./scripts/check_layer_dependencies.sh`: passed. + - AppContext admin status resolver scan: passed; direct admin production + `GLOBAL_BOOT_TIME`, `GLOBAL_TransitionState`, and scanner `global_metrics` + reads are removed. + - Rust risk scan: no new production unwrap/expect, panic/todo/unsafe, or cast + risks added. + - Issue #660 API-163 current slice: - `cargo check --tests -p rustfs`: passed. - `cargo test -p rustfs resolver_helpers_are_context_first_and_fallback_when_context_is_absent --lib`: diff --git a/rustfs/src/admin/handlers/scanner.rs b/rustfs/src/admin/handlers/scanner.rs index b6b8d149c..bcead3793 100644 --- a/rustfs/src/admin/handlers/scanner.rs +++ b/rustfs/src/admin/handlers/scanner.rs @@ -14,12 +14,13 @@ use crate::admin::auth::validate_admin_request; use crate::admin::router::{AdminOperation, Operation, S3Router}; +use crate::app::context::resolve_scanner_metrics_report; use crate::auth::{check_key_valid, get_session_token}; use crate::server::{ADMIN_PREFIX, RemoteAddr}; use http::{HeaderMap, HeaderValue}; use hyper::{Method, StatusCode}; use matchit::Params; -use rustfs_common::metrics::{ScannerMetricsReport, global_metrics}; +use rustfs_common::metrics::ScannerMetricsReport; use rustfs_credentials::Credentials; use rustfs_policy::policy::action::{Action, AdminAction}; use s3s::header::CONTENT_TYPE; @@ -84,7 +85,7 @@ impl Operation for ScannerStatusHandler { async fn call(&self, req: S3Request, _params: Params<'_, '_>) -> S3Result> { let _cred = validate_scanner_status_request(&req).await?; let response = ScannerStatusResponse { - metrics: global_metrics().report().await, + metrics: resolve_scanner_metrics_report().await, runtime_config: rustfs_scanner::scanner_runtime_config_status(), }; let body = serde_json::to_vec(&response).map_err(|err| { diff --git a/rustfs/src/admin/handlers/tier.rs b/rustfs/src/admin/handlers/tier.rs index 3298f8197..0b06d7edd 100644 --- a/rustfs/src/admin/handlers/tier.rs +++ b/rustfs/src/admin/handlers/tier.rs @@ -13,7 +13,6 @@ // limitations under the License. #![allow(unused_variables, unused_mut, unused_must_use)] -use super::super::lifecycle::bucket_lifecycle_ops::GLOBAL_TransitionState; use super::super::{ AdminError, DailyAllTierStats, ERR_TIER_ALREADY_EXISTS, ERR_TIER_BACKEND_IN_USE, ERR_TIER_BACKEND_NOT_EMPTY, ERR_TIER_CONNECT_ERR, ERR_TIER_INVALID_CREDENTIALS, ERR_TIER_MISSING_CREDENTIALS, ERR_TIER_NAME_NOT_UPPERCASE, @@ -24,7 +23,9 @@ use crate::{ auth::validate_admin_request, router::{AdminOperation, Operation, S3Router}, }, - app::context::{resolve_notification_system, resolve_object_store_handle, resolve_tier_config_handle}, + app::context::{ + resolve_daily_tier_stats, resolve_notification_system, resolve_object_store_handle, resolve_tier_config_handle, + }, auth::{check_key_valid, get_session_token}, server::{ADMIN_PREFIX, RemoteAddr}, storage::request_context::spawn_traced, @@ -748,7 +749,7 @@ impl Operation for GetTierInfo { } else { None }; - let info = filter_tier_stats(GLOBAL_TransitionState.get_daily_all_tier_stats(), tier_name); + let info = filter_tier_stats(resolve_daily_tier_stats(), tier_name); let data = serde_json::to_vec(&info) .map_err(|e| S3Error::with_message(S3ErrorCode::InternalError, format!("marshal tier err {e}")))?; diff --git a/rustfs/src/admin/mod.rs b/rustfs/src/admin/mod.rs index a62873c6d..ae7b6a2ab 100644 --- a/rustfs/src/admin/mod.rs +++ b/rustfs/src/admin/mod.rs @@ -135,10 +135,6 @@ mod ecstore_error { pub(crate) use crate::storage::ecstore_error::StorageError; } -mod ecstore_global { - pub(crate) use crate::storage::ecstore_global::GLOBAL_BOOT_TIME; -} - #[allow(unused_imports)] mod ecstore_layout { pub(crate) use crate::storage::ecstore_layout::{EndpointServerPools, Endpoints, PoolEndpoints}; @@ -264,21 +260,6 @@ pub(crate) mod bucket_target_sys { } pub(crate) mod lifecycle { - pub(crate) mod bucket_lifecycle_ops { - use super::super::DailyAllTierStats; - - pub(crate) struct GlobalTransitionStateCompat; - - #[allow(non_upper_case_globals)] - pub(crate) static GLOBAL_TransitionState: GlobalTransitionStateCompat = GlobalTransitionStateCompat; - - impl GlobalTransitionStateCompat { - pub(crate) fn get_daily_all_tier_stats(&self) -> DailyAllTierStats { - super::super::ecstore_bucket::lifecycle::bucket_lifecycle_ops::GLOBAL_TransitionState.get_daily_all_tier_stats() - } - } - } - pub(crate) mod tier_last_day_stats { #[cfg(test)] pub(crate) type LastDayTierStats = super::super::ecstore_bucket::lifecycle::tier_last_day_stats::LastDayTierStats; @@ -498,16 +479,6 @@ pub(crate) async fn collect_local_metrics( ecstore_metrics::collect_local_metrics(types, opts).await } -pub(crate) struct BootTimeCompat; - -pub(crate) static GLOBAL_BOOT_TIME: BootTimeCompat = BootTimeCompat; - -impl BootTimeCompat { - pub(crate) fn get(&self) -> Option<&'static std::time::SystemTime> { - ecstore_global::GLOBAL_BOOT_TIME.get() - } -} - pub(crate) struct AdminErrorRef(fn() -> &'static AdminError); impl Deref for AdminErrorRef { diff --git a/rustfs/src/admin/router.rs b/rustfs/src/admin/router.rs index 2d338689f..f8d240dc5 100644 --- a/rustfs/src/admin/router.rs +++ b/rustfs/src/admin/router.rs @@ -12,7 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -use super::GLOBAL_BOOT_TIME; use super::PeerRestClient; use super::bandwidth::monitor::BandwidthDetails; use super::bucket_target_sys::{BucketTargetSys, PutObjectOptions, RemoveObjectOptions, S3ClientError, TargetClient}; @@ -26,8 +25,9 @@ use super::{AdminReplicationConfigExt as _, AdminVersioningConfigExt as _}; use crate::admin::console::{is_console_path, make_console_server}; use crate::admin::handlers::oidc::is_oidc_path; use crate::app::context::{ - resolve_bucket_monitor_handle, resolve_deployment_id, resolve_notification_system, resolve_object_store_handle, - resolve_region, resolve_replication_pool_handle, resolve_replication_stats_handle, resolve_server_config, + resolve_boot_time, resolve_bucket_monitor_handle, resolve_deployment_id, resolve_notification_system, + resolve_object_store_handle, resolve_region, resolve_replication_pool_handle, resolve_replication_stats_handle, + resolve_server_config, }; use crate::app::object_usecase::DefaultObjectUsecase; use crate::auth::{check_key_valid, get_session_token}; @@ -1437,9 +1437,8 @@ async fn build_replication_metrics_response(bucket: &str, route: ReplicationExtR } fn replication_metrics_uptime_seconds() -> i64 { - GLOBAL_BOOT_TIME - .get() - .and_then(|boot_time| SystemTime::now().duration_since(*boot_time).ok()) + resolve_boot_time() + .and_then(|boot_time| SystemTime::now().duration_since(boot_time).ok()) .map(|uptime| uptime.as_secs() as i64) .unwrap_or_default() } diff --git a/rustfs/src/app/context.rs b/rustfs/src/app/context.rs index b41ec7653..b6d16ee65 100644 --- a/rustfs/src/app/context.rs +++ b/rustfs/src/app/context.rs @@ -25,8 +25,10 @@ pub use global::*; pub use handles::*; pub use interfaces::*; +use super::DailyAllTierStats; use super::ECStore; use super::EndpointServerPools; +use super::ScannerMetricsReport; use super::TierConfigMgr; use super::metadata_sys::BucketMetadataSys; use super::new_object_layer_fn; @@ -38,7 +40,7 @@ use rustfs_iam::{store::object::ObjectStore, sys::IamSys}; use rustfs_kms::KmsServiceManager; use rustfs_lock::LockClient; use rustfs_tls_runtime::{GlobalPublishedOutboundTlsState, TlsGeneration}; -use std::{future::Future, sync::Arc}; +use std::{future::Future, sync::Arc, time::SystemTime}; use tokio::sync::RwLock; /// Resolve KMS runtime service manager using AppContext-first precedence. @@ -116,6 +118,22 @@ pub fn resolve_replication_stats_handle() -> Option> { resolve_replication_stats_handle_with(get_global_app_context(), || default_replication_stats_interface().handle()) } +/// Resolve boot time using AppContext-first precedence. +pub fn resolve_boot_time() -> Option { + resolve_boot_time_with(get_global_app_context(), || default_boot_time_interface().get()) +} + +/// Resolve daily tier transition statistics using AppContext-first precedence. +pub fn resolve_daily_tier_stats() -> DailyAllTierStats { + resolve_daily_tier_stats_with(get_global_app_context(), || default_tier_stats_interface().daily_all()) +} + +/// Resolve scanner metrics report using AppContext-first precedence. +pub async fn resolve_scanner_metrics_report() -> ScannerMetricsReport { + resolve_scanner_metrics_report_with(get_global_app_context(), || async { default_scanner_metrics_interface().report().await }) + .await +} + /// Resolve deployment identity using AppContext-first precedence. pub fn resolve_deployment_id() -> Option { resolve_deployment_id_with(get_global_app_context(), || default_deployment_id_interface().get()) @@ -241,6 +259,29 @@ fn resolve_replication_stats_handle_with( .or_else(fallback) } +fn resolve_boot_time_with(context: Option>, fallback: impl FnOnce() -> Option) -> Option { + context.and_then(|context| context.boot_time().get()).or_else(fallback) +} + +fn resolve_daily_tier_stats_with( + context: Option>, + fallback: impl FnOnce() -> DailyAllTierStats, +) -> DailyAllTierStats { + context.map_or_else(fallback, |context| context.tier_stats().daily_all()) +} + +async fn resolve_scanner_metrics_report_with(context: Option>, fallback: F) -> ScannerMetricsReport +where + F: FnOnce() -> Fut, + Fut: Future, +{ + if let Some(context) = context { + return context.scanner_metrics().report().await; + } + + fallback().await +} + #[cfg(test)] fn resolve_object_store_handle_with( context: Option>, @@ -330,16 +371,17 @@ mod tests { default_replication_pool_interface, }; use crate::app::context::interfaces::{ - ActionCredentialInterface, BucketMetadataInterface, BufferConfigInterface, DeploymentIdInterface, EndpointsInterface, - IamInterface, KmsInterface, KmsRuntimeInterface, LocalNodeNameInterface, LockClientInterface, - OutboundTlsRuntimeInterface, RegionInterface, ReplicationStatsInterface, RuntimePortInterface, ServerConfigInterface, - TierConfigInterface, + ActionCredentialInterface, BootTimeInterface, BucketMetadataInterface, BufferConfigInterface, DeploymentIdInterface, + EndpointsInterface, IamInterface, KmsInterface, KmsRuntimeInterface, LocalNodeNameInterface, LockClientInterface, + OutboundTlsRuntimeInterface, RegionInterface, ReplicationStatsInterface, RuntimePortInterface, ScannerMetricsInterface, + ServerConfigInterface, TierConfigInterface, TierStatsInterface, }; use crate::config::{RustFSBufferConfig, WorkloadProfile}; use async_trait::async_trait; use rustfs_iam::{store::object::ObjectStore, sys::IamSys}; use rustfs_lock::{LocalClient, LockClient}; use std::path::PathBuf; + use std::time::{Duration, SystemTime}; use tempfile::TempDir; use tokio_util::sync::CancellationToken; @@ -412,6 +454,37 @@ mod tests { } } + struct TestBootTimeInterface { + boot_time: Option, + } + + impl BootTimeInterface for TestBootTimeInterface { + fn get(&self) -> Option { + self.boot_time.clone() + } + } + + struct TestTierStatsInterface { + daily_stats: DailyAllTierStats, + } + + impl TierStatsInterface for TestTierStatsInterface { + fn daily_all(&self) -> DailyAllTierStats { + self.daily_stats.clone() + } + } + + struct TestScannerMetricsInterface { + report: ScannerMetricsReport, + } + + #[async_trait] + impl ScannerMetricsInterface for TestScannerMetricsInterface { + async fn report(&self) -> ScannerMetricsReport { + self.report.clone() + } + } + struct TestEndpointsInterface { endpoints: Option, } @@ -571,6 +644,20 @@ mod tests { let bucket_metadata = Arc::new(RwLock::new(BucketMetadataSys::new(object_store.clone()))); let context_replication_stats = Arc::new(ReplicationStats::new()); let fallback_replication_stats = Arc::new(ReplicationStats::new()); + let context_boot_time = SystemTime::UNIX_EPOCH + Duration::from_secs(10); + let fallback_boot_time = SystemTime::UNIX_EPOCH + Duration::from_secs(20); + let mut context_daily_tier_stats = DailyAllTierStats::new(); + context_daily_tier_stats.insert("CONTEXT".to_string(), Default::default()); + let mut fallback_daily_tier_stats = DailyAllTierStats::new(); + fallback_daily_tier_stats.insert("FALLBACK".to_string(), Default::default()); + let context_scanner_metrics = ScannerMetricsReport { + current_cycle: 7, + ..Default::default() + }; + let fallback_scanner_metrics = ScannerMetricsReport { + current_cycle: 13, + ..Default::default() + }; let tier_config = TierConfigMgr::new(); let server_config = Config::new(); let buffer_config = RustFSBufferConfig::new(WorkloadProfile::AiTraining); @@ -621,6 +708,15 @@ mod tests { replication_stats: Arc::new(TestReplicationStatsInterface { stats: Some(context_replication_stats.clone()), }), + boot_time: Arc::new(TestBootTimeInterface { + boot_time: Some(context_boot_time), + }), + tier_stats: Arc::new(TestTierStatsInterface { + daily_stats: context_daily_tier_stats.clone(), + }), + scanner_metrics: Arc::new(TestScannerMetricsInterface { + report: context_scanner_metrics.clone(), + }), endpoints: Arc::new(TestEndpointsInterface { endpoints: Some(endpoints.clone()), }), @@ -678,6 +774,19 @@ mod tests { &resolve_replication_stats_handle_with(Some(context.clone()), || None).expect("context replication stats"), &context_replication_stats )); + assert_eq!( + resolve_boot_time_with(Some(context.clone()), || Some(fallback_boot_time)).expect("context boot time"), + context_boot_time + ); + assert!( + resolve_daily_tier_stats_with(Some(context.clone()), || fallback_daily_tier_stats.clone()).contains_key("CONTEXT") + ); + assert_eq!( + resolve_scanner_metrics_report_with(Some(context.clone()), || async { fallback_scanner_metrics.clone() }) + .await + .current_cycle, + context_scanner_metrics.current_cycle + ); assert_eq!( resolve_endpoints_handle_with(Some(context.clone()), || None) .expect("context endpoints") @@ -745,6 +854,17 @@ mod tests { .expect("fallback replication stats"), &fallback_replication_stats )); + assert_eq!( + resolve_boot_time_with(None, || Some(fallback_boot_time)).expect("fallback boot time"), + fallback_boot_time + ); + assert!(resolve_daily_tier_stats_with(None, || fallback_daily_tier_stats.clone()).contains_key("FALLBACK")); + assert_eq!( + resolve_scanner_metrics_report_with(None, || async { fallback_scanner_metrics.clone() }) + .await + .current_cycle, + fallback_scanner_metrics.current_cycle + ); assert_eq!( resolve_endpoints_handle_with(None, || Some(endpoints.clone())) .expect("fallback endpoints") diff --git a/rustfs/src/app/context/global.rs b/rustfs/src/app/context/global.rs index 7b992dc5b..fb54a26ae 100644 --- a/rustfs/src/app/context/global.rs +++ b/rustfs/src/app/context/global.rs @@ -14,18 +14,20 @@ use super::super::{ECStore, set_object_store_resolver}; use super::handles::{ - IamHandle, KmsHandle, default_action_credential_interface, default_bucket_metadata_interface, + IamHandle, KmsHandle, default_action_credential_interface, default_boot_time_interface, default_bucket_metadata_interface, default_bucket_monitor_interface, default_buffer_config_interface, default_deployment_id_interface, default_endpoints_interface, default_kms_runtime_interface, default_local_node_name_interface, default_lock_client_interface, default_notification_system_interface, default_notify_interface, default_outbound_tls_runtime_interface, default_region_interface, default_replication_pool_interface, default_replication_stats_interface, - default_runtime_port_interface, default_server_config_interface, default_tier_config_interface, + default_runtime_port_interface, default_scanner_metrics_interface, default_server_config_interface, + default_tier_config_interface, default_tier_stats_interface, }; use super::interfaces::{ - ActionCredentialInterface, BucketMetadataInterface, BucketMonitorInterface, BufferConfigInterface, DeploymentIdInterface, - EndpointsInterface, IamInterface, KmsInterface, KmsRuntimeInterface, LocalNodeNameInterface, LockClientInterface, - NotificationSystemInterface, NotifyInterface, OutboundTlsRuntimeInterface, RegionInterface, ReplicationPoolInterface, - ReplicationStatsInterface, RuntimePortInterface, ServerConfigInterface, TierConfigInterface, + ActionCredentialInterface, BootTimeInterface, BucketMetadataInterface, BucketMonitorInterface, BufferConfigInterface, + DeploymentIdInterface, EndpointsInterface, IamInterface, KmsInterface, KmsRuntimeInterface, LocalNodeNameInterface, + LockClientInterface, NotificationSystemInterface, NotifyInterface, OutboundTlsRuntimeInterface, RegionInterface, + ReplicationPoolInterface, ReplicationStatsInterface, RuntimePortInterface, ScannerMetricsInterface, ServerConfigInterface, + TierConfigInterface, TierStatsInterface, }; use rustfs_iam::{store::object::ObjectStore, sys::IamSys}; use rustfs_kms::KmsServiceManager; @@ -46,6 +48,9 @@ pub struct AppContext { bucket_monitor: Arc, replication_pool: Arc, replication_stats: Arc, + boot_time: Arc, + tier_stats: Arc, + scanner_metrics: Arc, endpoints: Arc, deployment_id: Arc, runtime_port: Arc, @@ -72,6 +77,9 @@ impl AppContext { bucket_monitor: default_bucket_monitor_interface(), replication_pool: default_replication_pool_interface(), replication_stats: default_replication_stats_interface(), + boot_time: default_boot_time_interface(), + tier_stats: default_tier_stats_interface(), + scanner_metrics: default_scanner_metrics_interface(), endpoints: default_endpoints_interface(), deployment_id: default_deployment_id_interface(), runtime_port: default_runtime_port_interface(), @@ -138,6 +146,18 @@ impl AppContext { self.replication_stats.clone() } + pub fn boot_time(&self) -> Arc { + self.boot_time.clone() + } + + pub fn tier_stats(&self) -> Arc { + self.tier_stats.clone() + } + + pub fn scanner_metrics(&self) -> Arc { + self.scanner_metrics.clone() + } + pub fn endpoints(&self) -> Arc { self.endpoints.clone() } @@ -191,6 +211,9 @@ pub(super) struct AppContextTestInterfaces { pub(super) bucket_monitor: Arc, pub(super) replication_pool: Arc, pub(super) replication_stats: Arc, + pub(super) boot_time: Arc, + pub(super) tier_stats: Arc, + pub(super) scanner_metrics: Arc, pub(super) endpoints: Arc, pub(super) deployment_id: Arc, pub(super) runtime_port: Arc, @@ -218,6 +241,9 @@ impl AppContext { bucket_monitor: interfaces.bucket_monitor, replication_pool: interfaces.replication_pool, replication_stats: interfaces.replication_stats, + boot_time: interfaces.boot_time, + tier_stats: interfaces.tier_stats, + scanner_metrics: interfaces.scanner_metrics, endpoints: interfaces.endpoints, deployment_id: interfaces.deployment_id, runtime_port: interfaces.runtime_port, diff --git a/rustfs/src/app/context/handles.rs b/rustfs/src/app/context/handles.rs index 137dead97..7f5d532e2 100644 --- a/rustfs/src/app/context/handles.rs +++ b/rustfs/src/app/context/handles.rs @@ -16,15 +16,16 @@ use super::super::EndpointServerPools; use super::super::TierConfigMgr; use super::super::metadata_sys::{BucketMetadataSys, get_global_bucket_metadata_sys}; use super::super::{ - get_global_bucket_monitor, get_global_deployment_id, get_global_endpoints_opt, get_global_lock_client, - get_global_notification_sys, get_global_region, get_global_replication_pool, get_global_replication_stats, - get_global_tier_config_mgr, global_rustfs_port, + 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_lock_client, get_global_notification_sys, get_global_region, + get_global_replication_pool, get_global_replication_stats, get_global_tier_config_mgr, global_rustfs_port, }; use super::interfaces::{ - ActionCredentialInterface, BucketMetadataInterface, BucketMonitorInterface, BufferConfigInterface, DeploymentIdInterface, - EndpointsInterface, IamInterface, KmsInterface, KmsRuntimeInterface, LocalNodeNameInterface, LockClientInterface, - NotificationSystemInterface, NotifyInterface, OutboundTlsRuntimeInterface, RegionInterface, ReplicationPoolInterface, - ReplicationStatsInterface, RuntimePortInterface, ServerConfigInterface, TierConfigInterface, + ActionCredentialInterface, BootTimeInterface, BucketMetadataInterface, BucketMonitorInterface, BufferConfigInterface, + DeploymentIdInterface, EndpointsInterface, IamInterface, KmsInterface, KmsRuntimeInterface, LocalNodeNameInterface, + LockClientInterface, NotificationSystemInterface, NotifyInterface, OutboundTlsRuntimeInterface, RegionInterface, + ReplicationPoolInterface, ReplicationStatsInterface, RuntimePortInterface, ScannerMetricsInterface, ServerConfigInterface, + TierConfigInterface, TierStatsInterface, }; use crate::config::{RustFSBufferConfig, get_global_buffer_config}; use async_trait::async_trait; @@ -40,7 +41,7 @@ use rustfs_targets::{EventName, arn::TargetID}; use rustfs_tls_runtime::{ GlobalPublishedOutboundTlsState, TlsGeneration, load_global_outbound_tls_generation, load_global_outbound_tls_state, }; -use std::sync::Arc; +use std::{sync::Arc, time::SystemTime}; use tokio::sync::RwLock; /// Default IAM interface adapter. @@ -182,6 +183,37 @@ impl ReplicationStatsInterface for ReplicationStatsHandle { } } +/// Default boot time interface adapter. +#[derive(Default)] +pub struct BootTimeHandle; + +impl BootTimeInterface for BootTimeHandle { + fn get(&self) -> Option { + get_global_boot_time() + } +} + +/// Default tier transition statistics interface adapter. +#[derive(Default)] +pub struct TierStatsHandle; + +impl TierStatsInterface for TierStatsHandle { + fn daily_all(&self) -> super::super::DailyAllTierStats { + get_daily_all_tier_stats() + } +} + +/// Default scanner metrics report interface adapter. +#[derive(Default)] +pub struct ScannerMetricsHandle; + +#[async_trait] +impl ScannerMetricsInterface for ScannerMetricsHandle { + async fn report(&self) -> super::super::ScannerMetricsReport { + collect_scanner_metrics_report().await + } +} + /// Default endpoints interface adapter. #[derive(Default)] pub struct EndpointsHandle; @@ -315,6 +347,18 @@ pub fn default_replication_stats_interface() -> Arc Arc { + Arc::new(BootTimeHandle) +} + +pub fn default_tier_stats_interface() -> Arc { + Arc::new(TierStatsHandle) +} + +pub fn default_scanner_metrics_interface() -> Arc { + Arc::new(ScannerMetricsHandle) +} + pub fn default_endpoints_interface() -> Arc { Arc::new(EndpointsHandle) } diff --git a/rustfs/src/app/context/interfaces.rs b/rustfs/src/app/context/interfaces.rs index 2216ed377..d53eeae9c 100644 --- a/rustfs/src/app/context/interfaces.rs +++ b/rustfs/src/app/context/interfaces.rs @@ -12,7 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. +use super::super::DailyAllTierStats; use super::super::EndpointServerPools; +use super::super::ScannerMetricsReport; use super::super::TierConfigMgr; use super::super::metadata_sys::BucketMetadataSys; use super::super::{BucketBandwidthMonitor, DynReplicationPool, NotificationSys, ReplicationStats}; @@ -26,7 +28,7 @@ use rustfs_lock::LockClient; use rustfs_notify::{EventArgs, NotificationError}; use rustfs_targets::{EventName, arn::TargetID}; use rustfs_tls_runtime::{GlobalPublishedOutboundTlsState, TlsGeneration}; -use std::sync::Arc; +use std::{sync::Arc, time::SystemTime}; use tokio::sync::RwLock; /// IAM interface for application-layer use-cases. @@ -94,6 +96,22 @@ pub trait ReplicationStatsInterface: Send + Sync { fn handle(&self) -> Option>; } +/// Boot time interface for admin metric integration. +pub trait BootTimeInterface: Send + Sync { + fn get(&self) -> Option; +} + +/// Tier transition statistics interface for admin metric integration. +pub trait TierStatsInterface: Send + Sync { + fn daily_all(&self) -> DailyAllTierStats; +} + +/// Scanner metrics report interface for admin status integration. +#[async_trait] +pub trait ScannerMetricsInterface: Send + Sync { + async fn report(&self) -> ScannerMetricsReport; +} + /// Endpoints interface for application-layer use-cases. pub trait EndpointsInterface: Send + Sync { fn handle(&self) -> Option; diff --git a/rustfs/src/app/mod.rs b/rustfs/src/app/mod.rs index e128bc0b9..8040f5d95 100644 --- a/rustfs/src/app/mod.rs +++ b/rustfs/src/app/mod.rs @@ -678,6 +678,22 @@ pub(crate) fn get_global_replication_stats() -> Option> { crate::storage::get_global_replication_stats() } +pub(crate) type DailyAllTierStats = crate::storage::DailyAllTierStats; + +pub(crate) fn get_global_boot_time() -> Option { + crate::storage::get_global_boot_time() +} + +pub(crate) fn get_daily_all_tier_stats() -> DailyAllTierStats { + crate::storage::get_daily_all_tier_stats() +} + +pub(crate) type ScannerMetricsReport = rustfs_common::metrics::ScannerMetricsReport; + +pub(crate) async fn collect_scanner_metrics_report() -> ScannerMetricsReport { + rustfs_common::metrics::global_metrics().report().await +} + #[cfg(test)] pub(crate) fn boxed_reader(reader: R) -> DynReader where diff --git a/rustfs/src/storage/mod.rs b/rustfs/src/storage/mod.rs index 28b3eafff..2ea723e01 100644 --- a/rustfs/src/storage/mod.rs +++ b/rustfs/src/storage/mod.rs @@ -213,6 +213,7 @@ pub(crate) type BucketVersioningSys = ecstore_bucket::versioning_sys::BucketVers pub(crate) type BucketBandwidthMonitor = ecstore_bucket::bandwidth::monitor::Monitor; pub(crate) type CheckPartsResp = ecstore_disk::CheckPartsResp; pub(crate) type CollectMetricsOpts = ecstore_metrics::CollectMetricsOpts; +pub(crate) type DailyAllTierStats = ecstore_bucket::lifecycle::tier_last_day_stats::DailyAllTierStats; pub(crate) type DeleteOptions = ecstore_disk::DeleteOptions; pub(crate) type DiskError = ecstore_disk::error::DiskError; pub(crate) type DiskInfo = ecstore_disk::DiskInfo; @@ -305,6 +306,14 @@ pub(crate) fn get_global_replication_stats() -> Option> { ecstore_bucket::replication::GLOBAL_REPLICATION_STATS.get().cloned() } +pub(crate) fn get_global_boot_time() -> Option { + ecstore_global::GLOBAL_BOOT_TIME.get().cloned() +} + +pub(crate) fn get_daily_all_tier_stats() -> DailyAllTierStats { + ecstore_bucket::lifecycle::bucket_lifecycle_ops::GLOBAL_TransitionState.get_daily_all_tier_stats() +} + pub(crate) async fn try_migrate_bucket_metadata(store: Arc) { ecstore_bucket::migration::try_migrate_bucket_metadata(store).await; }