diff --git a/docs/architecture/migration-progress.md b/docs/architecture/migration-progress.md index 3b3cb458a..ddfdaaaa2 100644 --- a/docs/architecture/migration-progress.md +++ b/docs/architecture/migration-progress.md @@ -5,14 +5,13 @@ 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-site-replication-tls-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`. -- Based on: API-163 local branch stacked on API-162 PR #3776. +- 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. - PR type for this branch: `consumer-migration` - Runtime behavior changes: none. -- Rust code changes: route admin site-replication and TLS debug outbound TLS - generation/state reads through AppContext resolvers with legacy global - fallback. +- Rust code changes: route admin replication stats reads 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 @@ -21,7 +20,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 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 owner facade cleanup. ## Phase 0 Tasks @@ -4386,6 +4385,22 @@ Status values: `[ ]` not started, `[~]` in progress, `[x]` complete, `[!]` block TLS global-read scan, Rust risk scan, branch freshness check, and three-expert review. +- [x] `API-166` Route admin replication stats reads through AppContext. + - Do: add a replication stats AppContext interface and storage-owner wrapper, + then route admin replication metrics, extended replication metrics, and + site-replication metrics summary reads through the resolver. + - Acceptance: admin production handlers no longer directly read + `GLOBAL_REPLICATION_STATS`, while AppContext default adapters keep the + existing global fallback. + - Must preserve: replication metrics defaults when stats are absent, bucket + latest-stat lookup, site-replication node metric mapping, bandwidth report + enrichment, runtime-field enrichment, and existing storage owner global + initialization. + - Verification: RustFS compile coverage, targeted context resolver tests, + migration guard, layer guard, formatting, diff hygiene, residual replication + stats 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. @@ -4437,6 +4452,9 @@ Status values: `[ ]` not started, `[~]` in progress, `[x]` complete, `[!]` block | Quality/architecture | pass | API-165 keeps admin TLS debug outbound TLS status reads behind the AppContext resolver boundary. | | Migration preservation | pass | TLS debug status JSON fields, consumer labels, reload/env reporting, and legacy fallback behavior are preserved. | | Testing/verification | pass | RustFS focused compile, targeted context tests, formatting, migration/layer guards, diff hygiene, residual TLS scan, and Rust risk scan passed for API-165. | +| 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. | ## Verification Notes @@ -4472,6 +4490,21 @@ Passed before push: - Rust risk scan: no new production unwrap/expect, panic/todo/unsafe, or cast risks added. +- Issue #660 API-166 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 replication stats resolver scan: passed; direct admin production + `GLOBAL_REPLICATION_STATS` 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/replication.rs b/rustfs/src/admin/handlers/replication.rs index 140ee3a0b..a3eed56a9 100644 --- a/rustfs/src/admin/handlers/replication.rs +++ b/rustfs/src/admin/handlers/replication.rs @@ -18,13 +18,12 @@ use super::super::metadata::BUCKET_TARGETS_FILE; use super::super::metadata_sys; use super::super::metadata_sys::get_replication_config; use super::super::replication::BucketStats; -use super::super::replication::GLOBAL_REPLICATION_STATS; use super::super::target::BucketTarget; use crate::admin::auth::validate_admin_request; use crate::admin::handlers::site_replication::site_replication_peer_deployment_id_for_endpoint; use crate::admin::router::{AdminOperation, Operation, S3Router}; use crate::admin::utils::read_compatible_admin_body; -use crate::app::context::{resolve_object_store_handle, resolve_runtime_port}; +use crate::app::context::{resolve_object_store_handle, resolve_replication_stats_handle, resolve_runtime_port}; use crate::auth::{check_key_valid, get_session_token}; use crate::error::ApiError; use crate::server::{ADMIN_PREFIX, RemoteAddr}; @@ -158,7 +157,7 @@ impl Operation for GetReplicationMetricsHandler { // TODO cluster cache // In actual implementation, statistics would be obtained from cluster // This is simplified to get from local cache - let bucket_stats = match GLOBAL_REPLICATION_STATS.get() { + let bucket_stats = match resolve_replication_stats_handle() { Some(s) => s.get_latest_replication_stats(bucket).await, None => BucketStats::default(), }; diff --git a/rustfs/src/admin/handlers/site_replication.rs b/rustfs/src/admin/handlers/site_replication.rs index 0e34249d5..ca43c475a 100644 --- a/rustfs/src/admin/handlers/site_replication.rs +++ b/rustfs/src/admin/handlers/site_replication.rs @@ -20,7 +20,7 @@ use super::super::metadata::{ BUCKET_SSECONFIG, BUCKET_TAGGING_CONFIG, BUCKET_TARGETS_FILE, BUCKET_VERSIONING_CONFIG, OBJECT_LOCK_CONFIG, }; use super::super::metadata_sys; -use super::super::replication::{GLOBAL_REPLICATION_STATS, ResyncOpts}; +use super::super::replication::ResyncOpts; use super::super::target::{ARN, BucketTarget, BucketTargetType, BucketTargets, Credentials}; use super::super::{AdminReplicationConfigExt as _, AdminVersioningConfigExt as _}; use super::super::{delete_admin_config, read_admin_config, save_admin_config}; @@ -34,7 +34,7 @@ use crate::admin::utils::{encode_compatible_admin_payload, read_compatible_admin use crate::app::context::{ resolve_deployment_id, resolve_endpoints_handle, resolve_iam_handle, resolve_object_store_handle, resolve_outbound_tls_generation, resolve_outbound_tls_state, resolve_region, resolve_replication_pool_handle, - resolve_runtime_port, resolve_server_config, + resolve_replication_stats_handle, resolve_runtime_port, resolve_server_config, }; use crate::auth::{check_key_valid, get_session_token}; use crate::config::get_config_snapshot; @@ -1740,7 +1740,7 @@ fn filter_sr_info(mut info: SRInfo, opts: &SRStatusOptions) -> SRInfo { } async fn build_metrics_summary(local_peer: &PeerInfo) -> SRMetricsSummary { - let Some(stats) = GLOBAL_REPLICATION_STATS.get() else { + let Some(stats) = resolve_replication_stats_handle() else { return SRMetricsSummary::default(); }; diff --git a/rustfs/src/admin/mod.rs b/rustfs/src/admin/mod.rs index fc692d96a..a62873c6d 100644 --- a/rustfs/src/admin/mod.rs +++ b/rustfs/src/admin/mod.rs @@ -394,27 +394,14 @@ pub(crate) mod quota { } pub(crate) mod replication { - use std::sync::Arc; - pub(crate) type BucketReplicationResyncStatus = super::ecstore_bucket::replication::BucketReplicationResyncStatus; pub(crate) type BucketStats = super::ecstore_bucket::replication::BucketStats; pub(crate) type ObjectOpts = super::ecstore_bucket::replication::ObjectOpts; - pub(crate) type ReplicationStats = super::ecstore_bucket::replication::ReplicationStats; pub(crate) type ResyncOpts = super::ecstore_bucket::replication::ResyncOpts; #[cfg(test)] pub(crate) type ResyncStatusType = super::ecstore_bucket::replication::ResyncStatusType; #[cfg(test)] pub(crate) type TargetReplicationResyncStatus = super::ecstore_bucket::replication::TargetReplicationResyncStatus; - - pub(crate) struct GlobalReplicationStatsCompat; - - pub(crate) static GLOBAL_REPLICATION_STATS: GlobalReplicationStatsCompat = GlobalReplicationStatsCompat; - - impl GlobalReplicationStatsCompat { - pub(crate) fn get(&self) -> Option<&'static Arc> { - super::ecstore_bucket::replication::GLOBAL_REPLICATION_STATS.get() - } - } } pub(crate) mod target { diff --git a/rustfs/src/admin/router.rs b/rustfs/src/admin/router.rs index 6144efdc9..2d338689f 100644 --- a/rustfs/src/admin/router.rs +++ b/rustfs/src/admin/router.rs @@ -19,7 +19,7 @@ use super::bucket_target_sys::{BucketTargetSys, PutObjectOptions, RemoveObjectOp use super::metadata::BUCKET_TARGETS_FILE; use super::metadata_sys; use super::read_admin_config_without_migrate; -use super::replication::{BucketReplicationResyncStatus, BucketStats, GLOBAL_REPLICATION_STATS, ObjectOpts, ResyncOpts}; +use super::replication::{BucketReplicationResyncStatus, BucketStats, ObjectOpts, ResyncOpts}; use super::target::{BucketTarget, BucketTargetType, BucketTargets}; use super::versioning_sys::BucketVersioningSys; use super::{AdminReplicationConfigExt as _, AdminVersioningConfigExt as _}; @@ -27,7 +27,7 @@ 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_server_config, + 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}; @@ -1421,7 +1421,7 @@ async fn ensure_replication_config_exists(bucket: &str) -> S3Result<()> { } async fn build_replication_metrics_response(bucket: &str, route: ReplicationExtRoute) -> S3Result> { - let bucket_stats = match GLOBAL_REPLICATION_STATS.get() { + let bucket_stats = match resolve_replication_stats_handle() { Some(stats) => stats.get_latest_replication_stats(bucket).await, None => BucketStats::default(), }; diff --git a/rustfs/src/app/context.rs b/rustfs/src/app/context.rs index dbb40fd7a..b41ec7653 100644 --- a/rustfs/src/app/context.rs +++ b/rustfs/src/app/context.rs @@ -30,7 +30,7 @@ use super::EndpointServerPools; use super::TierConfigMgr; use super::metadata_sys::BucketMetadataSys; use super::new_object_layer_fn; -use super::{BucketBandwidthMonitor, DynReplicationPool, NotificationSys}; +use super::{BucketBandwidthMonitor, DynReplicationPool, NotificationSys, ReplicationStats}; use crate::config::RustFSBufferConfig; use rustfs_config::server_config::Config; use rustfs_credentials::Credentials; @@ -111,6 +111,11 @@ pub fn resolve_replication_pool_handle() -> Option> { resolve_replication_pool_handle_with(get_global_app_context(), || default_replication_pool_interface().handle()) } +/// Resolve replication statistics handle using AppContext-first precedence. +pub fn resolve_replication_stats_handle() -> Option> { + resolve_replication_stats_handle_with(get_global_app_context(), || default_replication_stats_interface().handle()) +} + /// 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()) @@ -227,6 +232,15 @@ fn resolve_replication_pool_handle_with( .or_else(fallback) } +fn resolve_replication_stats_handle_with( + context: Option>, + fallback: impl FnOnce() -> Option>, +) -> Option> { + context + .and_then(|context| context.replication_stats().handle()) + .or_else(fallback) +} + #[cfg(test)] fn resolve_object_store_handle_with( context: Option>, @@ -318,7 +332,8 @@ mod tests { use crate::app::context::interfaces::{ ActionCredentialInterface, BucketMetadataInterface, BufferConfigInterface, DeploymentIdInterface, EndpointsInterface, IamInterface, KmsInterface, KmsRuntimeInterface, LocalNodeNameInterface, LockClientInterface, - OutboundTlsRuntimeInterface, RegionInterface, RuntimePortInterface, ServerConfigInterface, TierConfigInterface, + OutboundTlsRuntimeInterface, RegionInterface, ReplicationStatsInterface, RuntimePortInterface, ServerConfigInterface, + TierConfigInterface, }; use crate::config::{RustFSBufferConfig, WorkloadProfile}; use async_trait::async_trait; @@ -387,6 +402,16 @@ mod tests { } } + struct TestReplicationStatsInterface { + stats: Option>, + } + + impl ReplicationStatsInterface for TestReplicationStatsInterface { + fn handle(&self) -> Option> { + self.stats.clone() + } + } + struct TestEndpointsInterface { endpoints: Option, } @@ -544,6 +569,8 @@ mod tests { let context_kms = Arc::new(KmsServiceManager::new()); let fallback_kms = Arc::new(KmsServiceManager::new()); 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 tier_config = TierConfigMgr::new(); let server_config = Config::new(); let buffer_config = RustFSBufferConfig::new(WorkloadProfile::AiTraining); @@ -591,6 +618,9 @@ mod tests { }), bucket_monitor: default_bucket_monitor_interface(), replication_pool: default_replication_pool_interface(), + replication_stats: Arc::new(TestReplicationStatsInterface { + stats: Some(context_replication_stats.clone()), + }), endpoints: Arc::new(TestEndpointsInterface { endpoints: Some(endpoints.clone()), }), @@ -644,6 +674,10 @@ mod tests { &resolve_object_store_handle_with(Some(context.clone()), || None).expect("context object store"), &object_store )); + assert!(Arc::ptr_eq( + &resolve_replication_stats_handle_with(Some(context.clone()), || None).expect("context replication stats"), + &context_replication_stats + )); assert_eq!( resolve_endpoints_handle_with(Some(context.clone()), || None) .expect("context endpoints") @@ -706,6 +740,11 @@ mod tests { &resolve_object_store_handle_with(None, || Some(object_store.clone())).expect("fallback object store"), &object_store )); + assert!(Arc::ptr_eq( + &resolve_replication_stats_handle_with(None, || Some(fallback_replication_stats.clone())) + .expect("fallback replication stats"), + &fallback_replication_stats + )); 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 eb28e5e2c..7b992dc5b 100644 --- a/rustfs/src/app/context/global.rs +++ b/rustfs/src/app/context/global.rs @@ -18,14 +18,14 @@ use super::handles::{ 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_runtime_port_interface, - default_server_config_interface, default_tier_config_interface, + default_region_interface, default_replication_pool_interface, default_replication_stats_interface, + default_runtime_port_interface, default_server_config_interface, default_tier_config_interface, }; use super::interfaces::{ ActionCredentialInterface, BucketMetadataInterface, BucketMonitorInterface, BufferConfigInterface, DeploymentIdInterface, EndpointsInterface, IamInterface, KmsInterface, KmsRuntimeInterface, LocalNodeNameInterface, LockClientInterface, NotificationSystemInterface, NotifyInterface, OutboundTlsRuntimeInterface, RegionInterface, ReplicationPoolInterface, - RuntimePortInterface, ServerConfigInterface, TierConfigInterface, + ReplicationStatsInterface, RuntimePortInterface, ServerConfigInterface, TierConfigInterface, }; use rustfs_iam::{store::object::ObjectStore, sys::IamSys}; use rustfs_kms::KmsServiceManager; @@ -45,6 +45,7 @@ pub struct AppContext { bucket_metadata: Arc, bucket_monitor: Arc, replication_pool: Arc, + replication_stats: Arc, endpoints: Arc, deployment_id: Arc, runtime_port: Arc, @@ -70,6 +71,7 @@ impl AppContext { bucket_metadata: default_bucket_metadata_interface(), bucket_monitor: default_bucket_monitor_interface(), replication_pool: default_replication_pool_interface(), + replication_stats: default_replication_stats_interface(), endpoints: default_endpoints_interface(), deployment_id: default_deployment_id_interface(), runtime_port: default_runtime_port_interface(), @@ -132,6 +134,10 @@ impl AppContext { self.replication_pool.clone() } + pub fn replication_stats(&self) -> Arc { + self.replication_stats.clone() + } + pub fn endpoints(&self) -> Arc { self.endpoints.clone() } @@ -184,6 +190,7 @@ pub(super) struct AppContextTestInterfaces { pub(super) bucket_metadata: Arc, pub(super) bucket_monitor: Arc, pub(super) replication_pool: Arc, + pub(super) replication_stats: Arc, pub(super) endpoints: Arc, pub(super) deployment_id: Arc, pub(super) runtime_port: Arc, @@ -210,6 +217,7 @@ impl AppContext { bucket_metadata: interfaces.bucket_metadata, bucket_monitor: interfaces.bucket_monitor, replication_pool: interfaces.replication_pool, + replication_stats: interfaces.replication_stats, 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 5c12eb386..137dead97 100644 --- a/rustfs/src/app/context/handles.rs +++ b/rustfs/src/app/context/handles.rs @@ -17,13 +17,14 @@ 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_tier_config_mgr, global_rustfs_port, + 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, - RuntimePortInterface, ServerConfigInterface, TierConfigInterface, + ReplicationStatsInterface, RuntimePortInterface, ServerConfigInterface, TierConfigInterface, }; use crate::config::{RustFSBufferConfig, get_global_buffer_config}; use async_trait::async_trait; @@ -171,6 +172,16 @@ impl ReplicationPoolInterface for ReplicationPoolHandle { } } +/// Default replication statistics interface adapter. +#[derive(Default)] +pub struct ReplicationStatsHandle; + +impl ReplicationStatsInterface for ReplicationStatsHandle { + fn handle(&self) -> Option> { + get_global_replication_stats() + } +} + /// Default endpoints interface adapter. #[derive(Default)] pub struct EndpointsHandle; @@ -300,6 +311,10 @@ pub fn default_replication_pool_interface() -> Arc Arc::new(ReplicationPoolHandle) } +pub fn default_replication_stats_interface() -> Arc { + Arc::new(ReplicationStatsHandle) +} + 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 ee5ed54a8..2216ed377 100644 --- a/rustfs/src/app/context/interfaces.rs +++ b/rustfs/src/app/context/interfaces.rs @@ -15,7 +15,7 @@ use super::super::EndpointServerPools; use super::super::TierConfigMgr; use super::super::metadata_sys::BucketMetadataSys; -use super::super::{BucketBandwidthMonitor, DynReplicationPool, NotificationSys}; +use super::super::{BucketBandwidthMonitor, DynReplicationPool, NotificationSys, ReplicationStats}; use crate::config::RustFSBufferConfig; use async_trait::async_trait; use rustfs_config::server_config::Config; @@ -89,6 +89,11 @@ pub trait ReplicationPoolInterface: Send + Sync { fn handle(&self) -> Option>; } +/// Replication statistics interface for admin metrics integration. +pub trait ReplicationStatsInterface: Send + Sync { + fn handle(&self) -> Option>; +} + /// 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 4a0c1ba0b..e128bc0b9 100644 --- a/rustfs/src/app/mod.rs +++ b/rustfs/src/app/mod.rs @@ -672,6 +672,12 @@ pub(crate) fn get_global_replication_pool() -> Option> { crate::storage::get_global_replication_pool() } +pub(crate) type ReplicationStats = crate::storage::ReplicationStats; + +pub(crate) fn get_global_replication_stats() -> Option> { + crate::storage::get_global_replication_stats() +} + #[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 891f6d1da..28b3eafff 100644 --- a/rustfs/src/storage/mod.rs +++ b/rustfs/src/storage/mod.rs @@ -246,6 +246,7 @@ pub(crate) type ReadMultipleReq = ecstore_disk::ReadMultipleReq; pub(crate) type ReadMultipleResp = ecstore_disk::ReadMultipleResp; pub(crate) type ReadOptions = ecstore_disk::ReadOptions; pub(crate) type RenameDataResp = ecstore_disk::RenameDataResp; +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; @@ -300,6 +301,10 @@ pub(crate) fn get_global_replication_pool() -> Option> { ecstore_bucket::replication::get_global_replication_pool() } +pub(crate) fn get_global_replication_stats() -> Option> { + ecstore_bucket::replication::GLOBAL_REPLICATION_STATS.get().cloned() +} + pub(crate) async fn try_migrate_bucket_metadata(store: Arc) { ecstore_bucket::migration::try_migrate_bucket_metadata(store).await; }