From 539c68778dfcde75e3136c7a19479f9a8c1559e5 Mon Sep 17 00:00:00 2001 From: Zhengchao An Date: Mon, 22 Jun 2026 17:35:58 +0800 Subject: [PATCH] refactor: remove standalone compat bridge modules (#3740) --- crates/e2e_test/src/lib.rs | 1 - .../e2e_test/src/reliant/grpc_lock_client.rs | 9 +- .../src/reliant/node_interact_test.rs | 22 +- .../src/replication_extension_test.rs | 2 +- crates/e2e_test/src/storage_compat.rs | 51 ---- crates/iam/src/store.rs | 1 - crates/iam/src/store/object.rs | 6 +- crates/iam/src/store/storage_compat.rs | 18 -- crates/notify/src/config_manager.rs | 67 +++- crates/notify/src/lib.rs | 1 - crates/notify/src/storage_compat.rs | 63 ---- crates/obs/src/lib.rs | 1 - crates/obs/src/metrics/scheduler.rs | 4 +- crates/obs/src/metrics/stats_collector.rs | 273 ++++++++++++++++- crates/obs/src/storage_compat.rs | 286 ------------------ crates/protocols/src/swift/account.rs | 2 +- crates/protocols/src/swift/container.rs | 2 +- crates/protocols/src/swift/mod.rs | 27 +- crates/protocols/src/swift/object.rs | 5 +- crates/protocols/src/swift/storage_compat.rs | 40 --- crates/protocols/src/swift/versioning.rs | 2 +- crates/s3select-api/src/lib.rs | 1 - crates/s3select-api/src/object_store.rs | 36 ++- crates/s3select-api/src/storage_compat.rs | 46 --- docs/architecture/migration-progress.md | 75 ++++- scripts/check_architecture_migration_rules.sh | 76 +++-- 26 files changed, 524 insertions(+), 593 deletions(-) delete mode 100644 crates/e2e_test/src/storage_compat.rs delete mode 100644 crates/iam/src/store/storage_compat.rs delete mode 100644 crates/notify/src/storage_compat.rs delete mode 100644 crates/obs/src/storage_compat.rs delete mode 100644 crates/protocols/src/swift/storage_compat.rs delete mode 100644 crates/s3select-api/src/storage_compat.rs diff --git a/crates/e2e_test/src/lib.rs b/crates/e2e_test/src/lib.rs index bbf09f87f..2259b63b7 100644 --- a/crates/e2e_test/src/lib.rs +++ b/crates/e2e_test/src/lib.rs @@ -13,7 +13,6 @@ // limitations under the License. mod reliant; -mod storage_compat; // Common utilities for all E2E tests #[cfg(test)] diff --git a/crates/e2e_test/src/reliant/grpc_lock_client.rs b/crates/e2e_test/src/reliant/grpc_lock_client.rs index fdb7b3b59..ea4cbe7b5 100644 --- a/crates/e2e_test/src/reliant/grpc_lock_client.rs +++ b/crates/e2e_test/src/reliant/grpc_lock_client.rs @@ -15,8 +15,8 @@ // Used by test_distributed_lock_4_nodes_grpc in lock.rs #![allow(dead_code)] -use super::super::storage_compat::node_service_time_out_client_no_auth; use async_trait::async_trait; +use rustfs_ecstore::api::rpc::{self as ecstore_rpc, TonicInterceptor}; use rustfs_lock::{ LockClient, LockError, LockId, LockInfo, LockRequest, LockResponse, LockStats, LockStatus, LockType, Result, types::{LockMetadata, LockPriority}, @@ -41,13 +41,10 @@ impl GrpcLockClient { &self, ) -> Result< rustfs_protos::proto_gen::node_service::node_service_client::NodeServiceClient< - tonic::service::interceptor::InterceptedService< - tonic::transport::Channel, - super::super::storage_compat::TonicInterceptor, - >, + tonic::service::interceptor::InterceptedService, >, > { - node_service_time_out_client_no_auth(&self.addr) + ecstore_rpc::node_service_time_out_client_no_auth(&self.addr) .await .map_err(|err| LockError::internal(format!("can not get client, err: {err}"))) } diff --git a/crates/e2e_test/src/reliant/node_interact_test.rs b/crates/e2e_test/src/reliant/node_interact_test.rs index c08920305..3186616d1 100644 --- a/crates/e2e_test/src/reliant/node_interact_test.rs +++ b/crates/e2e_test/src/reliant/node_interact_test.rs @@ -13,11 +13,13 @@ // See the License for the specific language governing permissions and // limitations under the License. -use super::super::storage_compat::{VolumeInfo, WalkDirOptions}; -use super::super::storage_compat::{gen_tonic_signature_interceptor, node_service_time_out_client}; use crate::common::workspace_root; use futures::future::join_all; use rmp_serde::{Deserializer, Serializer}; +use rustfs_ecstore::api::{ + disk::{VolumeInfo, WalkDirOptions}, + rpc::{self as ecstore_rpc, TonicInterceptor}, +}; use rustfs_filemeta::{MetaCacheEntry, MetacacheReader, MetacacheWriter}; use rustfs_protos::proto_gen::node_service::WalkDirRequest; use rustfs_protos::{ @@ -36,6 +38,10 @@ use tonic::codegen::tokio_stream::StreamExt; const CLUSTER_ADDR: &str = "http://localhost:9000"; +fn signature_interceptor() -> TonicInterceptor { + TonicInterceptor::Signature(ecstore_rpc::gen_tonic_signature_interceptor()) +} + #[tokio::test] #[ignore = "requires running RustFS server at localhost:9000"] async fn ping() -> Result<(), Box> { @@ -53,7 +59,7 @@ async fn ping() -> Result<(), Box> { assert!(decoded_payload.is_ok()); // Create client - let mut client = node_service_time_out_client(&CLUSTER_ADDR.to_string(), gen_tonic_signature_interceptor()).await?; + let mut client = ecstore_rpc::node_service_time_out_client(&CLUSTER_ADDR.to_string(), signature_interceptor()).await?; // Construct PingRequest let request = Request::new(PingRequest { @@ -78,7 +84,7 @@ async fn ping() -> Result<(), Box> { #[tokio::test] #[ignore = "requires running RustFS server at localhost:9000"] async fn make_volume() -> Result<(), Box> { - let mut client = node_service_time_out_client(&CLUSTER_ADDR.to_string(), gen_tonic_signature_interceptor()).await?; + let mut client = ecstore_rpc::node_service_time_out_client(&CLUSTER_ADDR.to_string(), signature_interceptor()).await?; let request = Request::new(MakeVolumeRequest { disk: "data".to_string(), volume: "dandan".to_string(), @@ -96,7 +102,7 @@ async fn make_volume() -> Result<(), Box> { #[tokio::test] #[ignore = "requires running RustFS server at localhost:9000"] async fn list_volumes() -> Result<(), Box> { - let mut client = node_service_time_out_client(&CLUSTER_ADDR.to_string(), gen_tonic_signature_interceptor()).await?; + let mut client = ecstore_rpc::node_service_time_out_client(&CLUSTER_ADDR.to_string(), signature_interceptor()).await?; let request = Request::new(ListVolumesRequest { disk: "data".to_string(), }); @@ -126,7 +132,7 @@ async fn walk_dir() -> Result<(), Box> { let (rd, mut wr) = tokio::io::duplex(1024); let mut buf = Vec::new(); opts.serialize(&mut Serializer::new(&mut buf))?; - let mut client = node_service_time_out_client(&CLUSTER_ADDR.to_string(), gen_tonic_signature_interceptor()).await?; + let mut client = ecstore_rpc::node_service_time_out_client(&CLUSTER_ADDR.to_string(), signature_interceptor()).await?; let disk_path = std::env::var_os("RUSTFS_DISK_PATH").map(PathBuf::from).unwrap_or_else(|| { let mut path = workspace_root(); path.push("target"); @@ -179,7 +185,7 @@ async fn walk_dir() -> Result<(), Box> { #[tokio::test] #[ignore = "requires running RustFS server at localhost:9000"] async fn read_all() -> Result<(), Box> { - let mut client = node_service_time_out_client(&CLUSTER_ADDR.to_string(), gen_tonic_signature_interceptor()).await?; + let mut client = ecstore_rpc::node_service_time_out_client(&CLUSTER_ADDR.to_string(), signature_interceptor()).await?; let request = Request::new(ReadAllRequest { disk: "data".to_string(), volume: "ff".to_string(), @@ -197,7 +203,7 @@ async fn read_all() -> Result<(), Box> { #[tokio::test] #[ignore = "requires running RustFS server at localhost:9000"] async fn storage_info() -> Result<(), Box> { - let mut client = node_service_time_out_client(&CLUSTER_ADDR.to_string(), gen_tonic_signature_interceptor()).await?; + let mut client = ecstore_rpc::node_service_time_out_client(&CLUSTER_ADDR.to_string(), signature_interceptor()).await?; let request = Request::new(LocalStorageInfoRequest { metrics: true }); let response = client.local_storage_info(request).await?.into_inner(); diff --git a/crates/e2e_test/src/replication_extension_test.rs b/crates/e2e_test/src/replication_extension_test.rs index e2d1c8219..480a8af3e 100644 --- a/crates/e2e_test/src/replication_extension_test.rs +++ b/crates/e2e_test/src/replication_extension_test.rs @@ -12,7 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -use super::storage_compat::BucketTargetSys; use crate::common::{ RustFSTestEnvironment, awscurl_available, awscurl_post_sts_form_urlencoded, init_logging, local_http_client, }; @@ -22,6 +21,7 @@ use aws_sdk_s3::types::{BucketVersioningStatus, VersioningConfiguration}; use aws_sdk_s3::{Client, Config}; use http::header::{CONTENT_TYPE, HOST}; use reqwest::StatusCode; +use rustfs_ecstore::api::bucket::bucket_target_sys::BucketTargetSys; use rustfs_madmin::{ AddServiceAccountReq, ListServiceAccountsResp, PeerInfo, PeerSite, ReplicateAddStatus, ReplicateEditStatus, ReplicateRemoveStatus, SRRemoveReq, SRResyncOpStatus, SRStatusInfo, SiteReplicationInfo, SyncStatus, diff --git a/crates/e2e_test/src/storage_compat.rs b/crates/e2e_test/src/storage_compat.rs deleted file mode 100644 index c37aeac47..000000000 --- a/crates/e2e_test/src/storage_compat.rs +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2024 RustFS Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#![allow(dead_code, unused_imports)] - -use rustfs_ecstore::api::bucket as ecstore_bucket; -use rustfs_ecstore::api::disk as ecstore_disk; -use rustfs_ecstore::api::rpc as ecstore_rpc; - -pub(crate) type BucketTargetSys = ecstore_bucket::bucket_target_sys::BucketTargetSys; -pub(crate) type TonicInterceptor = ecstore_rpc::TonicInterceptor; -pub(crate) type VolumeInfo = ecstore_disk::VolumeInfo; -pub(crate) type WalkDirOptions = ecstore_disk::WalkDirOptions; - -pub(crate) fn gen_tonic_signature_interceptor() -> TonicInterceptor { - TonicInterceptor::Signature(ecstore_rpc::gen_tonic_signature_interceptor()) -} - -pub(crate) async fn node_service_time_out_client( - addr: &String, - interceptor: TonicInterceptor, -) -> Result< - rustfs_protos::proto_gen::node_service::node_service_client::NodeServiceClient< - tonic::service::interceptor::InterceptedService, - >, - Box, -> { - ecstore_rpc::node_service_time_out_client(addr, interceptor).await -} - -pub(crate) async fn node_service_time_out_client_no_auth( - addr: &String, -) -> Result< - rustfs_protos::proto_gen::node_service::node_service_client::NodeServiceClient< - tonic::service::interceptor::InterceptedService, - >, - Box, -> { - ecstore_rpc::node_service_time_out_client_no_auth(addr).await -} diff --git a/crates/iam/src/store.rs b/crates/iam/src/store.rs index 7092543f4..9e189b99b 100644 --- a/crates/iam/src/store.rs +++ b/crates/iam/src/store.rs @@ -13,7 +13,6 @@ // limitations under the License. pub mod object; -mod storage_compat; use crate::cache::Cache; use crate::error::Result; diff --git a/crates/iam/src/store/object.rs b/crates/iam/src/store/object.rs index f665e0e9b..16f4355eb 100644 --- a/crates/iam/src/store/object.rs +++ b/crates/iam/src/store/object.rs @@ -28,7 +28,7 @@ use futures::future::join_all; use rustfs_credentials::get_global_action_cred; use rustfs_io_metrics::record_system_path_failure; use rustfs_policy::{auth::UserIdentity, policy::PolicyDoc}; -use rustfs_storage_api::{HTTPPreconditions, ListOperations as _, ObjectInfoOrErr as StorageObjectInfoOrErr}; +use rustfs_storage_api::{HTTPPreconditions, ListOperations as _, ObjectInfoOrErr as StorageObjectInfoOrErr, ObjectOperations}; use rustfs_utils::path::{SLASH_SEPARATOR, path_join_buf}; use serde::{Serialize, de::DeserializeOwned}; use std::sync::{LazyLock, Mutex}; @@ -38,8 +38,6 @@ use tokio::sync::mpsc::{self, Sender}; use tokio_util::sync::CancellationToken; use tracing::{debug, error, warn}; -use super::storage_compat::{IamObjectInfo, IamObjectOptions}; - pub static IAM_CONFIG_PREFIX: LazyLock = LazyLock::new(|| format!("{IAM_CONFIG_ROOT_PREFIX}/iam")); pub static IAM_CONFIG_USERS_PREFIX: LazyLock = LazyLock::new(|| format!("{IAM_CONFIG_ROOT_PREFIX}/iam/users/")); pub static IAM_CONFIG_SERVICE_ACCOUNTS_PREFIX: LazyLock = @@ -58,6 +56,8 @@ pub static IAM_CONFIG_POLICY_DB_GROUPS_PREFIX: LazyLock = LazyLock::new(|| format!("{IAM_CONFIG_ROOT_PREFIX}/iam/policydb/groups/")); type ObjectInfoOrErr = StorageObjectInfoOrErr; +type IamObjectInfo = ::ObjectInfo; +type IamObjectOptions = ::ObjectOptions; const IAM_IDENTITY_FILE: &str = "identity.json"; const IAM_POLICY_FILE: &str = "policy.json"; diff --git a/crates/iam/src/store/storage_compat.rs b/crates/iam/src/store/storage_compat.rs deleted file mode 100644 index bdef165ab..000000000 --- a/crates/iam/src/store/storage_compat.rs +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2024 RustFS Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use super::super::storage_compat::IamStore; - -pub(super) type IamObjectInfo = ::ObjectInfo; -pub(super) type IamObjectOptions = ::ObjectOptions; diff --git a/crates/notify/src/config_manager.rs b/crates/notify/src/config_manager.rs index 42e981c98..a5d6dc397 100644 --- a/crates/notify/src/config_manager.rs +++ b/crates/notify/src/config_manager.rs @@ -13,17 +13,14 @@ // limitations under the License. use crate::{ - Event, NotificationError, - registry::TargetRegistry, - rule_engine::NotifyRuleEngine, - runtime_facade::NotifyRuntimeFacade, - storage_compat::{self, NotifyConfigStoreError}, + Event, NotificationError, registry::TargetRegistry, rule_engine::NotifyRuleEngine, runtime_facade::NotifyRuntimeFacade, }; use rustfs_config::notify::{ NOTIFY_AMQP_SUB_SYS, NOTIFY_KAFKA_SUB_SYS, NOTIFY_MQTT_SUB_SYS, NOTIFY_MYSQL_SUB_SYS, NOTIFY_NATS_SUB_SYS, NOTIFY_POSTGRES_SUB_SYS, NOTIFY_PULSAR_SUB_SYS, NOTIFY_REDIS_SUB_SYS, NOTIFY_WEBHOOK_SUB_SYS, }; use rustfs_config::server_config::{Config, KVS}; +use rustfs_ecstore::api::{config as ecstore_config, global as ecstore_global, storage as ecstore_storage}; use rustfs_targets::{Target, arn::TargetID}; use std::sync::Arc; use tokio::sync::RwLock; @@ -34,6 +31,50 @@ const LOG_SUBSYSTEM_CONFIG: &str = "config"; const EVENT_NOTIFY_RUNTIME_LIFECYCLE: &str = "notify_runtime_lifecycle"; const EVENT_NOTIFY_CONFIG_UPDATE: &str = "notify_config_update"; +type NotifyStore = ecstore_storage::ECStore; + +#[derive(Debug)] +enum NotifyConfigStoreError { + StorageNotAvailable, + Read(String), + Save(String), +} + +async fn update_server_config(mut modifier: F) -> Result, NotifyConfigStoreError> +where + F: FnMut(&mut Config) -> bool, +{ + let Some(store) = resolve_notify_object_store_handle() else { + return Err(NotifyConfigStoreError::StorageNotAvailable); + }; + + let mut new_config = read_notify_server_config_without_migrate(store.clone()).await?; + + if !modifier(&mut new_config) { + return Ok(None); + } + + save_notify_server_config(store, &new_config).await?; + + Ok(Some(new_config)) +} + +fn resolve_notify_object_store_handle() -> Option> { + ecstore_global::resolve_object_store_handle() +} + +async fn read_notify_server_config_without_migrate(store: Arc) -> Result { + ecstore_config::com::read_config_without_migrate(store) + .await + .map_err(|err| NotifyConfigStoreError::Read(err.to_string())) +} + +async fn save_notify_server_config(store: Arc, config: &Config) -> Result<(), NotifyConfigStoreError> { + ecstore_config::com::save_server_config(store, config) + .await + .map_err(|err| NotifyConfigStoreError::Save(err.to_string())) +} + pub(crate) fn notify_configuration_hint() -> String { let webhook_enable_primary = format!("{}_PRIMARY", rustfs_config::notify::ENV_NOTIFY_WEBHOOK_ENABLE); let webhook_endpoint_primary = format!("{}_PRIMARY", rustfs_config::notify::ENV_NOTIFY_WEBHOOK_ENDPOINT); @@ -320,15 +361,13 @@ impl NotifyConfigManager { where F: FnMut(&mut Config) -> bool, { - let Some(new_config) = storage_compat::update_server_config(&mut modifier) - .await - .map_err(|err| match err { - NotifyConfigStoreError::StorageNotAvailable => NotificationError::StorageNotAvailable( - "Failed to save target configuration: server storage not initialized".to_string(), - ), - NotifyConfigStoreError::Read(err) => NotificationError::ReadConfig(err), - NotifyConfigStoreError::Save(err) => NotificationError::SaveConfig(err), - })? + let Some(new_config) = update_server_config(&mut modifier).await.map_err(|err| match err { + NotifyConfigStoreError::StorageNotAvailable => NotificationError::StorageNotAvailable( + "Failed to save target configuration: server storage not initialized".to_string(), + ), + NotifyConfigStoreError::Read(err) => NotificationError::ReadConfig(err), + NotifyConfigStoreError::Save(err) => NotificationError::SaveConfig(err), + })? else { debug!( event = EVENT_NOTIFY_CONFIG_UPDATE, diff --git a/crates/notify/src/lib.rs b/crates/notify/src/lib.rs index fb92c6576..adb72e4a4 100644 --- a/crates/notify/src/lib.rs +++ b/crates/notify/src/lib.rs @@ -36,7 +36,6 @@ mod runtime_facade; mod runtime_view; mod services; mod status_view; -mod storage_compat; pub use bucket_config_manager::NotifyBucketConfigManager; pub use config_manager::{NotifyConfigManager, runtime_target_id_for_subsystem}; diff --git a/crates/notify/src/storage_compat.rs b/crates/notify/src/storage_compat.rs deleted file mode 100644 index 93988566a..000000000 --- a/crates/notify/src/storage_compat.rs +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2024 RustFS Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use rustfs_config::server_config::Config; -use rustfs_ecstore::api::config as ecstore_config; -use rustfs_ecstore::api::global as ecstore_global; -use rustfs_ecstore::api::storage as ecstore_storage; -use std::sync::Arc; - -type NotifyStore = ecstore_storage::ECStore; - -#[derive(Debug)] -pub(crate) enum NotifyConfigStoreError { - StorageNotAvailable, - Read(String), - Save(String), -} - -pub(crate) async fn update_server_config(mut modifier: F) -> Result, NotifyConfigStoreError> -where - F: FnMut(&mut Config) -> bool, -{ - let Some(store) = resolve_notify_object_store_handle() else { - return Err(NotifyConfigStoreError::StorageNotAvailable); - }; - - let mut new_config = read_notify_server_config_without_migrate(store.clone()).await?; - - if !modifier(&mut new_config) { - return Ok(None); - } - - save_notify_server_config(store, &new_config).await?; - - Ok(Some(new_config)) -} - -fn resolve_notify_object_store_handle() -> Option> { - ecstore_global::resolve_object_store_handle() -} - -async fn read_notify_server_config_without_migrate(store: Arc) -> Result { - ecstore_config::com::read_config_without_migrate(store) - .await - .map_err(|err| NotifyConfigStoreError::Read(err.to_string())) -} - -async fn save_notify_server_config(store: Arc, config: &Config) -> Result<(), NotifyConfigStoreError> { - ecstore_config::com::save_server_config(store, config) - .await - .map_err(|err| NotifyConfigStoreError::Save(err.to_string())) -} diff --git a/crates/obs/src/lib.rs b/crates/obs/src/lib.rs index b2b42c43a..c41165726 100644 --- a/crates/obs/src/lib.rs +++ b/crates/obs/src/lib.rs @@ -64,7 +64,6 @@ mod error; mod global; mod logging; pub mod metrics; -mod storage_compat; mod telemetry; pub use cleaner::*; diff --git a/crates/obs/src/metrics/scheduler.rs b/crates/obs/src/metrics/scheduler.rs index f8d80f40b..dfacc4e48 100644 --- a/crates/obs/src/metrics/scheduler.rs +++ b/crates/obs/src/metrics/scheduler.rs @@ -23,7 +23,6 @@ //! - Process disk I/O metrics //! - Host network I/O metrics -use super::super::storage_compat::obs_bucket_monitor_available; use crate::metrics::collectors::{ AuditTargetStats, BucketReplicationBandwidthStats, @@ -83,6 +82,7 @@ use crate::metrics::stats_collector::{ collect_scanner_metric_stats, collect_system_cpu_and_memory_stats_with, }; use rustfs_audit::audit_target_metrics; +use rustfs_ecstore::api::global as ecstore_global; use rustfs_notify::{notification_metrics_snapshot, notification_target_metrics}; use rustfs_utils::get_env_opt_u64; use serde::Serialize; @@ -599,7 +599,7 @@ pub fn init_metrics_runtime(token: CancellationToken) { loop { tokio::select! { _ = interval.tick() => { - let monitor_available = obs_bucket_monitor_available(); + let monitor_available = ecstore_global::get_global_bucket_monitor().is_some(); let stats = collect_bucket_replication_bandwidth_stats(); let current_live_keys = repl_bw_live_keys(&stats); diff --git a/crates/obs/src/metrics/stats_collector.rs b/crates/obs/src/metrics/stats_collector.rs index 258739b40..0838e15c9 100644 --- a/crates/obs/src/metrics/stats_collector.rs +++ b/crates/obs/src/metrics/stats_collector.rs @@ -20,25 +20,24 @@ //! RustFS internal sources (storage layer, bucket monitor, system info) //! and convert them to the Stats structs used by collectors. -use super::super::storage_compat::{ - load_obs_data_usage_from_backend, obs_bucket_quota_limit_bytes, obs_bucket_replication_bandwidth_stats, - obs_bucket_replication_detail_stats, obs_ilm_runtime_snapshot, obs_site_replication_stats, obs_total_usable_capacity_bytes, - obs_total_usable_capacity_free_bytes, resolve_obs_object_store_handle, -}; use crate::metrics::collectors::{ - BucketReplicationBandwidthStats, BucketReplicationStats, BucketStats, BucketUsageStats, ClusterConfigStats, - ClusterHealthStats, ClusterStats, ClusterUsageStats, CpuStats, DiskStats, DriveCountStats, DriveDetailedStats, - ErasureSetStats, HostNetworkStats, IamStats, IlmStats, MemoryStats, NetworkStats, ProcessStats, ProcessStatusType, - ReplicationStats, ResourceStats, ScannerStats, + BucketReplicationBandwidthStats, BucketReplicationStats, BucketReplicationTargetStats, BucketStats, BucketUsageStats, + ClusterConfigStats, ClusterHealthStats, ClusterStats, ClusterUsageStats, CpuStats, DiskStats, DriveCountStats, + DriveDetailedStats, ErasureSetStats, HostNetworkStats, IamStats, IlmStats, MemoryStats, NetworkStats, ProcessStats, + ProcessStatusType, ReplicationStats, ResourceStats, ScannerStats, }; use chrono::Utc; use rustfs_common::heal_channel::HealScanMode; use rustfs_common::metrics::global_metrics; +use rustfs_ecstore::api::{ + bucket as ecstore_bucket, capacity as ecstore_capacity, data_usage as ecstore_data_usage, error as ecstore_error, + global as ecstore_global, storage as ecstore_storage, +}; use rustfs_iam::{get_global_iam_sys, oidc::oidc_plugin_authn_metrics_snapshot}; use rustfs_io_metrics::internode_metrics::global_internode_metrics; use rustfs_io_metrics::{ProcessStatusSnapshot, snapshot_process_resource_and_system}; use rustfs_storage_api::{BucketOperations, BucketOptions, StorageAdminApi}; -use std::collections::HashMap; +use std::{collections::HashMap, sync::Arc, time::Duration}; use sysinfo::{Networks, System}; use tracing::{instrument, warn}; @@ -46,6 +45,260 @@ const LOG_COMPONENT_OBS: &str = "obs"; const LOG_SUBSYSTEM_METRICS_COLLECTOR: &str = "metrics_collector"; const EVENT_METRICS_COLLECTOR_STATE: &str = "metrics_collector_state"; +type ObsStore = ecstore_storage::ECStore; +type ObsStorageInfo = ::StorageInfo; + +struct ObsDataUsageInfo { + buckets_count: u64, + objects_total_count: u64, + versions_total_count: u64, + delete_markers_total_count: u64, + objects_total_size: u64, + buckets_usage: HashMap, +} + +struct ObsBucketUsageInfo { + size: u64, + objects_count: u64, + object_size_histogram: HashMap, + object_versions_histogram: HashMap, + versions_count: u64, + delete_markers_count: u64, +} + +#[derive(Debug, Clone, PartialEq)] +struct ObsBucketReplicationBandwidthStats { + bucket: String, + target_arn: String, + limit_bytes_per_sec: i64, + current_bandwidth_bytes_per_sec: f64, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +struct ObsIlmRuntimeSnapshot { + expiry_pending_tasks: u64, + transition_active_tasks: u64, + transition_pending_tasks: u64, + transition_missed_immediate_tasks: u64, + transition_queue_full_tasks: u64, + transition_queue_send_timeout_tasks: u64, + transition_compensation_scheduled_tasks: u64, + transition_compensation_running_tasks: u64, +} + +fn usize_to_u64_saturating(value: usize) -> u64 { + u64::try_from(value).unwrap_or(u64::MAX) +} + +fn i64_to_u64_floor_zero(value: i64) -> u64 { + u64::try_from(value.max(0)).unwrap_or(0) +} + +fn i32_to_u64_floor_zero(value: i32) -> u64 { + u64::try_from(value.max(0)).unwrap_or(0) +} + +async fn load_obs_data_usage_from_backend(store: Arc) -> ecstore_error::Result { + let data_usage = ecstore_data_usage::load_data_usage_from_backend(store).await?; + + Ok(ObsDataUsageInfo { + buckets_count: data_usage.buckets_count, + objects_total_count: data_usage.objects_total_count, + versions_total_count: data_usage.versions_total_count, + delete_markers_total_count: data_usage.delete_markers_total_count, + objects_total_size: data_usage.objects_total_size, + buckets_usage: data_usage + .buckets_usage + .into_iter() + .map(|(bucket, usage)| { + ( + bucket, + ObsBucketUsageInfo { + size: usage.size, + objects_count: usage.objects_count, + object_size_histogram: usage.object_size_histogram, + object_versions_histogram: usage.object_versions_histogram, + versions_count: usage.versions_count, + delete_markers_count: usage.delete_markers_count, + }, + ) + }) + .collect(), + }) +} + +fn resolve_obs_object_store_handle() -> Option> { + ecstore_global::resolve_object_store_handle() +} + +fn obs_total_usable_capacity_bytes(storage_info: &ObsStorageInfo) -> u64 { + usize_to_u64_saturating(ecstore_capacity::get_total_usable_capacity(&storage_info.disks, storage_info)) +} + +fn obs_total_usable_capacity_free_bytes(storage_info: &ObsStorageInfo) -> u64 { + usize_to_u64_saturating(ecstore_capacity::get_total_usable_capacity_free(&storage_info.disks, storage_info)) +} + +async fn obs_bucket_quota_limit_bytes(bucket: &str) -> u64 { + ecstore_bucket::metadata_sys::get_quota_config(bucket) + .await + .ok() + .and_then(|(quota, _)| quota.get_quota_limit()) + .unwrap_or(0) +} + +fn obs_bucket_replication_bandwidth_stats() -> Option> { + let monitor = ecstore_global::get_global_bucket_monitor()?; + Some( + monitor + .get_report(|_| true) + .bucket_stats + .into_iter() + .map(|(opts, details)| ObsBucketReplicationBandwidthStats { + bucket: opts.name, + target_arn: opts.replication_arn, + limit_bytes_per_sec: details.limit_bytes_per_sec, + current_bandwidth_bytes_per_sec: details.current_bandwidth_bytes_per_sec, + }) + .collect(), + ) +} + +async fn obs_ilm_runtime_snapshot() -> ObsIlmRuntimeSnapshot { + let expiry_pending_tasks = { + let expiry = ecstore_bucket::lifecycle::bucket_lifecycle_ops::GLOBAL_ExpiryState + .read() + .await; + usize_to_u64_saturating(expiry.pending_tasks()) + }; + let transition = &ecstore_bucket::lifecycle::bucket_lifecycle_ops::GLOBAL_TransitionState; + + ObsIlmRuntimeSnapshot { + expiry_pending_tasks, + transition_active_tasks: i64_to_u64_floor_zero(transition.active_tasks()), + transition_pending_tasks: usize_to_u64_saturating(transition.pending_tasks()), + transition_missed_immediate_tasks: i64_to_u64_floor_zero(transition.missed_immediate_tasks()), + transition_queue_full_tasks: i64_to_u64_floor_zero(transition.queue_full_tasks()), + transition_queue_send_timeout_tasks: i64_to_u64_floor_zero(transition.queue_send_timeout_tasks()), + transition_compensation_scheduled_tasks: i64_to_u64_floor_zero(transition.compensation_scheduled_tasks()), + transition_compensation_running_tasks: i64_to_u64_floor_zero(transition.compensation_running_tasks()), + } +} + +async fn obs_bucket_replication_detail_stats() -> Vec { + let Some(stats) = ecstore_bucket::replication::GLOBAL_REPLICATION_STATS.get() else { + return Vec::new(); + }; + + let all_bucket_stats = stats.get_all().await; + let mut buckets = Vec::with_capacity(all_bucket_stats.len()); + + for (bucket, bucket_stats) in all_bucket_stats { + let proxy = stats.get_proxy_stats(&bucket).await; + let mut total_failed_bytes = 0u64; + let mut total_failed_count = 0u64; + let mut last_min_failed_bytes = 0u64; + let mut last_min_failed_count = 0u64; + let mut last_hour_failed_bytes = 0u64; + let mut last_hour_failed_count = 0u64; + let mut sent_bytes = 0u64; + let mut sent_count = 0u64; + let mut targets = Vec::with_capacity(bucket_stats.stats.len()); + + for (target_arn, target_stats) in bucket_stats.stats { + total_failed_bytes += i64_to_u64_floor_zero(target_stats.fail_stats.size); + total_failed_count += i64_to_u64_floor_zero(target_stats.fail_stats.count); + + let last_min = target_stats.fail_stats.recent_since(Duration::from_secs(60)); + last_min_failed_bytes += i64_to_u64_floor_zero(last_min.size); + last_min_failed_count += i64_to_u64_floor_zero(last_min.count); + + let last_hour = target_stats.fail_stats.recent_since(Duration::from_secs(60 * 60)); + last_hour_failed_bytes += i64_to_u64_floor_zero(last_hour.size); + last_hour_failed_count += i64_to_u64_floor_zero(last_hour.count); + + sent_bytes += i64_to_u64_floor_zero(target_stats.replicated_size); + sent_count += i64_to_u64_floor_zero(target_stats.replicated_count); + + targets.push(BucketReplicationTargetStats { + target_arn, + bandwidth_limit_bytes_per_sec: i64_to_u64_floor_zero(target_stats.bandwidth_limit_bytes_per_sec), + current_bandwidth_bytes_per_sec: target_stats.current_bandwidth_bytes_per_sec, + latency_ms: target_stats.latency.curr, + }); + } + + buckets.push(BucketReplicationStats { + bucket, + total_failed_bytes, + total_failed_count, + last_min_failed_bytes, + last_min_failed_count, + last_hour_failed_bytes, + last_hour_failed_count, + sent_bytes, + sent_count, + proxied_get_requests_total: i64_to_u64_floor_zero(proxy.get_total), + proxied_get_requests_failures: i64_to_u64_floor_zero(proxy.get_failed), + proxied_head_requests_total: i64_to_u64_floor_zero(proxy.head_total), + proxied_head_requests_failures: i64_to_u64_floor_zero(proxy.head_failed), + proxied_put_requests_total: i64_to_u64_floor_zero(proxy.put_total), + proxied_put_requests_failures: i64_to_u64_floor_zero(proxy.put_failed), + proxied_put_tagging_requests_total: i64_to_u64_floor_zero(proxy.put_tag_total), + proxied_put_tagging_requests_failures: i64_to_u64_floor_zero(proxy.put_tag_failed), + proxied_get_tagging_requests_total: i64_to_u64_floor_zero(proxy.get_tag_total), + proxied_get_tagging_requests_failures: i64_to_u64_floor_zero(proxy.get_tag_failed), + proxied_delete_tagging_requests_total: i64_to_u64_floor_zero(proxy.delete_tag_total), + proxied_delete_tagging_requests_failures: i64_to_u64_floor_zero(proxy.delete_tag_failed), + targets, + }); + } + + buckets +} + +async fn obs_site_replication_stats() -> ReplicationStats { + let Some(stats) = ecstore_bucket::replication::GLOBAL_REPLICATION_STATS.get() else { + return ReplicationStats::default(); + }; + + let site_metrics = stats.get_sr_metrics_for_node().await; + let current_data_transfer_rate = obs_bucket_replication_bandwidth_stats() + .into_iter() + .flatten() + .map(|stat| stat.current_bandwidth_bytes_per_sec) + .sum::(); + + let all_bucket_stats = stats.get_all().await; + let average_data_transfer_rate = all_bucket_stats + .values() + .flat_map(|bucket| bucket.stats.values()) + .map(|stat| stat.xfer_rate_lrg.avg + stat.xfer_rate_sml.avg) + .sum::(); + let max_data_transfer_rate = all_bucket_stats + .values() + .flat_map(|bucket| bucket.stats.values()) + .map(|stat| stat.xfer_rate_lrg.peak + stat.xfer_rate_sml.peak) + .sum::(); + let recent_backlog_count = stats.mrf_stats.values().copied().filter(|value| *value > 0).sum::(); + + ReplicationStats { + average_active_workers: site_metrics.active_workers.avg, + average_queued_bytes: site_metrics.queued.avg.bytes, + average_queued_count: site_metrics.queued.avg.count, + average_data_transfer_rate, + active_workers: i32_to_u64_floor_zero(site_metrics.active_workers.curr), + current_data_transfer_rate, + last_minute_queued_bytes: i64_to_u64_floor_zero(site_metrics.queued.last_minute.bytes), + last_minute_queued_count: i64_to_u64_floor_zero(site_metrics.queued.last_minute.count), + max_active_workers: i32_to_u64_floor_zero(site_metrics.active_workers.max), + max_queued_bytes: i64_to_u64_floor_zero(site_metrics.queued.max.bytes), + max_queued_count: i64_to_u64_floor_zero(site_metrics.queued.max.count), + max_data_transfer_rate, + recent_backlog_count: i64_to_u64_floor_zero(recent_backlog_count), + } +} + fn current_scanner_cycle_age_seconds( current_cycle: u64, current_started: chrono::DateTime, diff --git a/crates/obs/src/storage_compat.rs b/crates/obs/src/storage_compat.rs deleted file mode 100644 index 3e0f6285a..000000000 --- a/crates/obs/src/storage_compat.rs +++ /dev/null @@ -1,286 +0,0 @@ -// Copyright 2024 RustFS Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use crate::metrics::collectors::{ - BucketReplicationStats as MetricBucketReplicationStats, BucketReplicationTargetStats, - ReplicationStats as MetricReplicationStats, -}; -use rustfs_ecstore::api::bucket as ecstore_bucket; -use rustfs_ecstore::api::capacity as ecstore_capacity; -use rustfs_ecstore::api::data_usage as ecstore_data_usage; -use rustfs_ecstore::api::error as ecstore_error; -use rustfs_ecstore::api::global as ecstore_global; -use rustfs_ecstore::api::storage as ecstore_storage; -use rustfs_storage_api::StorageAdminApi; -use std::collections::HashMap; -use std::sync::Arc; -use std::time::Duration; - -pub(crate) type ObsStore = ecstore_storage::ECStore; -type ObsStorageInfo = ::StorageInfo; - -pub(crate) struct ObsDataUsageInfo { - pub(crate) buckets_count: u64, - pub(crate) objects_total_count: u64, - pub(crate) versions_total_count: u64, - pub(crate) delete_markers_total_count: u64, - pub(crate) objects_total_size: u64, - pub(crate) buckets_usage: HashMap, -} - -pub(crate) struct ObsBucketUsageInfo { - pub(crate) size: u64, - pub(crate) objects_count: u64, - pub(crate) object_size_histogram: HashMap, - pub(crate) object_versions_histogram: HashMap, - pub(crate) versions_count: u64, - pub(crate) delete_markers_count: u64, -} - -pub(crate) async fn load_obs_data_usage_from_backend(store: Arc) -> ecstore_error::Result { - let data_usage = ecstore_data_usage::load_data_usage_from_backend(store).await?; - - Ok(ObsDataUsageInfo { - buckets_count: data_usage.buckets_count, - objects_total_count: data_usage.objects_total_count, - versions_total_count: data_usage.versions_total_count, - delete_markers_total_count: data_usage.delete_markers_total_count, - objects_total_size: data_usage.objects_total_size, - buckets_usage: data_usage - .buckets_usage - .into_iter() - .map(|(bucket, usage)| { - ( - bucket, - ObsBucketUsageInfo { - size: usage.size, - objects_count: usage.objects_count, - object_size_histogram: usage.object_size_histogram, - object_versions_histogram: usage.object_versions_histogram, - versions_count: usage.versions_count, - delete_markers_count: usage.delete_markers_count, - }, - ) - }) - .collect(), - }) -} - -#[derive(Debug, Clone, PartialEq)] -pub(crate) struct ObsBucketReplicationBandwidthStats { - pub(crate) bucket: String, - pub(crate) target_arn: String, - pub(crate) limit_bytes_per_sec: i64, - pub(crate) current_bandwidth_bytes_per_sec: f64, -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub(crate) struct ObsIlmRuntimeSnapshot { - pub(crate) expiry_pending_tasks: u64, - pub(crate) transition_active_tasks: u64, - pub(crate) transition_pending_tasks: u64, - pub(crate) transition_missed_immediate_tasks: u64, - pub(crate) transition_queue_full_tasks: u64, - pub(crate) transition_queue_send_timeout_tasks: u64, - pub(crate) transition_compensation_scheduled_tasks: u64, - pub(crate) transition_compensation_running_tasks: u64, -} - -pub(crate) fn resolve_obs_object_store_handle() -> Option> { - ecstore_global::resolve_object_store_handle() -} - -pub(crate) fn obs_total_usable_capacity_bytes(storage_info: &ObsStorageInfo) -> u64 { - usize_to_u64_saturating(ecstore_capacity::get_total_usable_capacity(&storage_info.disks, storage_info)) -} - -pub(crate) fn obs_total_usable_capacity_free_bytes(storage_info: &ObsStorageInfo) -> u64 { - usize_to_u64_saturating(ecstore_capacity::get_total_usable_capacity_free(&storage_info.disks, storage_info)) -} - -pub(crate) async fn obs_bucket_quota_limit_bytes(bucket: &str) -> u64 { - ecstore_bucket::metadata_sys::get_quota_config(bucket) - .await - .ok() - .and_then(|(quota, _)| quota.get_quota_limit()) - .unwrap_or(0) -} - -pub(crate) fn obs_bucket_monitor_available() -> bool { - ecstore_global::get_global_bucket_monitor().is_some() -} - -pub(crate) fn obs_bucket_replication_bandwidth_stats() -> Option> { - let monitor = ecstore_global::get_global_bucket_monitor()?; - Some( - monitor - .get_report(|_| true) - .bucket_stats - .into_iter() - .map(|(opts, details)| ObsBucketReplicationBandwidthStats { - bucket: opts.name, - target_arn: opts.replication_arn, - limit_bytes_per_sec: details.limit_bytes_per_sec, - current_bandwidth_bytes_per_sec: details.current_bandwidth_bytes_per_sec, - }) - .collect(), - ) -} - -pub(crate) async fn obs_ilm_runtime_snapshot() -> ObsIlmRuntimeSnapshot { - let expiry_pending_tasks = { - let expiry = ecstore_bucket::lifecycle::bucket_lifecycle_ops::GLOBAL_ExpiryState - .read() - .await; - usize_to_u64_saturating(expiry.pending_tasks()) - }; - let transition = &ecstore_bucket::lifecycle::bucket_lifecycle_ops::GLOBAL_TransitionState; - - ObsIlmRuntimeSnapshot { - expiry_pending_tasks, - transition_active_tasks: i64_to_u64_floor_zero(transition.active_tasks()), - transition_pending_tasks: usize_to_u64_saturating(transition.pending_tasks()), - transition_missed_immediate_tasks: i64_to_u64_floor_zero(transition.missed_immediate_tasks()), - transition_queue_full_tasks: i64_to_u64_floor_zero(transition.queue_full_tasks()), - transition_queue_send_timeout_tasks: i64_to_u64_floor_zero(transition.queue_send_timeout_tasks()), - transition_compensation_scheduled_tasks: i64_to_u64_floor_zero(transition.compensation_scheduled_tasks()), - transition_compensation_running_tasks: i64_to_u64_floor_zero(transition.compensation_running_tasks()), - } -} - -pub(crate) async fn obs_bucket_replication_detail_stats() -> Vec { - let Some(stats) = ecstore_bucket::replication::GLOBAL_REPLICATION_STATS.get() else { - return Vec::new(); - }; - - let all_bucket_stats = stats.get_all().await; - let mut buckets = Vec::with_capacity(all_bucket_stats.len()); - - for (bucket, bucket_stats) in all_bucket_stats { - let proxy = stats.get_proxy_stats(&bucket).await; - let mut total_failed_bytes = 0u64; - let mut total_failed_count = 0u64; - let mut last_min_failed_bytes = 0u64; - let mut last_min_failed_count = 0u64; - let mut last_hour_failed_bytes = 0u64; - let mut last_hour_failed_count = 0u64; - let mut sent_bytes = 0u64; - let mut sent_count = 0u64; - let mut targets = Vec::with_capacity(bucket_stats.stats.len()); - - for (target_arn, target_stats) in bucket_stats.stats { - total_failed_bytes += i64_to_u64_floor_zero(target_stats.fail_stats.size); - total_failed_count += i64_to_u64_floor_zero(target_stats.fail_stats.count); - - let last_min = target_stats.fail_stats.recent_since(Duration::from_secs(60)); - last_min_failed_bytes += i64_to_u64_floor_zero(last_min.size); - last_min_failed_count += i64_to_u64_floor_zero(last_min.count); - - let last_hour = target_stats.fail_stats.recent_since(Duration::from_secs(60 * 60)); - last_hour_failed_bytes += i64_to_u64_floor_zero(last_hour.size); - last_hour_failed_count += i64_to_u64_floor_zero(last_hour.count); - - sent_bytes += i64_to_u64_floor_zero(target_stats.replicated_size); - sent_count += i64_to_u64_floor_zero(target_stats.replicated_count); - - targets.push(BucketReplicationTargetStats { - target_arn, - bandwidth_limit_bytes_per_sec: i64_to_u64_floor_zero(target_stats.bandwidth_limit_bytes_per_sec), - current_bandwidth_bytes_per_sec: target_stats.current_bandwidth_bytes_per_sec, - latency_ms: target_stats.latency.curr, - }); - } - - buckets.push(MetricBucketReplicationStats { - bucket, - total_failed_bytes, - total_failed_count, - last_min_failed_bytes, - last_min_failed_count, - last_hour_failed_bytes, - last_hour_failed_count, - sent_bytes, - sent_count, - proxied_get_requests_total: i64_to_u64_floor_zero(proxy.get_total), - proxied_get_requests_failures: i64_to_u64_floor_zero(proxy.get_failed), - proxied_head_requests_total: i64_to_u64_floor_zero(proxy.head_total), - proxied_head_requests_failures: i64_to_u64_floor_zero(proxy.head_failed), - proxied_put_requests_total: i64_to_u64_floor_zero(proxy.put_total), - proxied_put_requests_failures: i64_to_u64_floor_zero(proxy.put_failed), - proxied_put_tagging_requests_total: i64_to_u64_floor_zero(proxy.put_tag_total), - proxied_put_tagging_requests_failures: i64_to_u64_floor_zero(proxy.put_tag_failed), - proxied_get_tagging_requests_total: i64_to_u64_floor_zero(proxy.get_tag_total), - proxied_get_tagging_requests_failures: i64_to_u64_floor_zero(proxy.get_tag_failed), - proxied_delete_tagging_requests_total: i64_to_u64_floor_zero(proxy.delete_tag_total), - proxied_delete_tagging_requests_failures: i64_to_u64_floor_zero(proxy.delete_tag_failed), - targets, - }); - } - - buckets -} - -pub(crate) async fn obs_site_replication_stats() -> MetricReplicationStats { - let Some(stats) = ecstore_bucket::replication::GLOBAL_REPLICATION_STATS.get() else { - return MetricReplicationStats::default(); - }; - - let site_metrics = stats.get_sr_metrics_for_node().await; - let current_data_transfer_rate = obs_bucket_replication_bandwidth_stats() - .into_iter() - .flatten() - .map(|stat| stat.current_bandwidth_bytes_per_sec) - .sum::(); - - let all_bucket_stats = stats.get_all().await; - let average_data_transfer_rate = all_bucket_stats - .values() - .flat_map(|bucket| bucket.stats.values()) - .map(|stat| stat.xfer_rate_lrg.avg + stat.xfer_rate_sml.avg) - .sum::(); - let max_data_transfer_rate = all_bucket_stats - .values() - .flat_map(|bucket| bucket.stats.values()) - .map(|stat| stat.xfer_rate_lrg.peak + stat.xfer_rate_sml.peak) - .sum::(); - let recent_backlog_count = stats.mrf_stats.values().copied().filter(|value| *value > 0).sum::(); - - MetricReplicationStats { - average_active_workers: site_metrics.active_workers.avg, - average_queued_bytes: site_metrics.queued.avg.bytes, - average_queued_count: site_metrics.queued.avg.count, - average_data_transfer_rate, - active_workers: i32_to_u64_floor_zero(site_metrics.active_workers.curr), - current_data_transfer_rate, - last_minute_queued_bytes: i64_to_u64_floor_zero(site_metrics.queued.last_minute.bytes), - last_minute_queued_count: i64_to_u64_floor_zero(site_metrics.queued.last_minute.count), - max_active_workers: i32_to_u64_floor_zero(site_metrics.active_workers.max), - max_queued_bytes: i64_to_u64_floor_zero(site_metrics.queued.max.bytes), - max_queued_count: i64_to_u64_floor_zero(site_metrics.queued.max.count), - max_data_transfer_rate, - recent_backlog_count: i64_to_u64_floor_zero(recent_backlog_count), - } -} - -fn usize_to_u64_saturating(value: usize) -> u64 { - u64::try_from(value).unwrap_or(u64::MAX) -} - -fn i64_to_u64_floor_zero(value: i64) -> u64 { - u64::try_from(value.max(0)).unwrap_or(0) -} - -fn i32_to_u64_floor_zero(value: i32) -> u64 { - u64::try_from(value.max(0)).unwrap_or(0) -} diff --git a/crates/protocols/src/swift/account.rs b/crates/protocols/src/swift/account.rs index 5ea1cf0d6..7110d80ac 100644 --- a/crates/protocols/src/swift/account.rs +++ b/crates/protocols/src/swift/account.rs @@ -14,8 +14,8 @@ //! Swift account operations and validation -use super::storage_compat::{get_swift_bucket_metadata, resolve_swift_object_store_handle, set_swift_bucket_metadata}; use super::{SwiftError, SwiftResult}; +use super::{get_swift_bucket_metadata, resolve_swift_object_store_handle, set_swift_bucket_metadata}; use rustfs_credentials::Credentials; use rustfs_storage_api::{BucketOperations, MakeBucketOptions}; use s3s::dto::{Tag, Tagging}; diff --git a/crates/protocols/src/swift/container.rs b/crates/protocols/src/swift/container.rs index becbafd8e..727788a8e 100644 --- a/crates/protocols/src/swift/container.rs +++ b/crates/protocols/src/swift/container.rs @@ -17,9 +17,9 @@ //! This module implements Swift container CRUD operations and container-bucket translation. use super::account::validate_account_access; -use super::storage_compat::{get_swift_bucket_metadata, resolve_swift_object_store_handle, set_swift_bucket_metadata}; use super::types::Container; use super::{SwiftError, SwiftResult}; +use super::{get_swift_bucket_metadata, resolve_swift_object_store_handle, set_swift_bucket_metadata}; use rustfs_credentials::Credentials; use rustfs_storage_api::{ BucketInfo, BucketOperations, BucketOptions, DeleteBucketOptions, ListOperations as _, MakeBucketOptions, diff --git a/crates/protocols/src/swift/mod.rs b/crates/protocols/src/swift/mod.rs index 4fb818843..96c60241a 100644 --- a/crates/protocols/src/swift/mod.rs +++ b/crates/protocols/src/swift/mod.rs @@ -33,6 +33,12 @@ //! and stores credentials in task-local storage, which Swift handlers access //! to enforce tenant isolation. +use std::sync::Arc; + +use rustfs_ecstore::api::{ + bucket as ecstore_bucket, error as ecstore_error, global as ecstore_global, storage as ecstore_storage, +}; + pub mod account; pub mod acl; pub mod bulk; @@ -51,7 +57,6 @@ pub mod ratelimit; pub mod router; pub mod slo; pub mod staticweb; -pub mod storage_compat; pub mod symlink; pub mod sync; pub mod tempurl; @@ -63,3 +68,23 @@ pub use router::{SwiftRoute, SwiftRouter}; // Note: Container, Object, and SwiftMetadata types used by Swift implementation #[allow(unused_imports)] pub use types::{Container, Object, SwiftMetadata}; + +type SwiftBucketMetadata = ecstore_bucket::metadata::BucketMetadata; +type SwiftStorageResult = ecstore_error::Result; +type SwiftStore = ecstore_storage::ECStore; +pub type SwiftGetObjectReader = ::GetObjectReader; +pub type SwiftObjectInfo = ::ObjectInfo; +pub type SwiftObjectOptions = ::ObjectOptions; +pub type SwiftPutObjReader = ::PutObjectReader; + +fn resolve_swift_object_store_handle() -> Option> { + ecstore_global::resolve_object_store_handle() +} + +async fn get_swift_bucket_metadata(bucket: &str) -> SwiftStorageResult> { + ecstore_bucket::metadata_sys::get(bucket).await +} + +async fn set_swift_bucket_metadata(bucket: String, metadata: SwiftBucketMetadata) -> SwiftStorageResult<()> { + ecstore_bucket::metadata_sys::set_bucket_metadata(bucket, metadata).await +} diff --git a/crates/protocols/src/swift/object.rs b/crates/protocols/src/swift/object.rs index e268e19de..5133fe635 100644 --- a/crates/protocols/src/swift/object.rs +++ b/crates/protocols/src/swift/object.rs @@ -51,8 +51,7 @@ use super::account::validate_account_access; use super::container::ContainerMapper; -use super::storage_compat::resolve_swift_object_store_handle; -use super::{SwiftError, SwiftResult}; +use super::{SwiftError, SwiftResult, resolve_swift_object_store_handle}; use axum::http::HeaderMap; use rustfs_credentials::Credentials; use rustfs_rio::HashReader; @@ -61,7 +60,7 @@ use std::collections::HashMap; use tracing::debug; use tracing::error; -pub use super::storage_compat::{SwiftGetObjectReader, SwiftObjectInfo, SwiftObjectOptions, SwiftPutObjReader}; +pub use super::{SwiftGetObjectReader, SwiftObjectInfo, SwiftObjectOptions, SwiftPutObjReader}; const LOG_COMPONENT_PROTOCOLS: &str = "protocols"; const LOG_SUBSYSTEM_SWIFT_OBJECT: &str = "swift_object"; diff --git a/crates/protocols/src/swift/storage_compat.rs b/crates/protocols/src/swift/storage_compat.rs deleted file mode 100644 index 6886236b8..000000000 --- a/crates/protocols/src/swift/storage_compat.rs +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2024 RustFS Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use std::sync::Arc; - -use rustfs_ecstore::api::bucket as ecstore_bucket; -use rustfs_ecstore::api::error as ecstore_error; -use rustfs_ecstore::api::global as ecstore_global; -use rustfs_ecstore::api::storage as ecstore_storage; - -pub(super) type SwiftBucketMetadata = ecstore_bucket::metadata::BucketMetadata; -pub(super) type SwiftStorageResult = ecstore_error::Result; -pub(super) type SwiftStore = ecstore_storage::ECStore; -pub type SwiftGetObjectReader = ::GetObjectReader; -pub type SwiftObjectInfo = ::ObjectInfo; -pub type SwiftObjectOptions = ::ObjectOptions; -pub type SwiftPutObjReader = ::PutObjectReader; - -pub fn resolve_swift_object_store_handle() -> Option> { - ecstore_global::resolve_object_store_handle() -} - -pub async fn get_swift_bucket_metadata(bucket: &str) -> SwiftStorageResult> { - ecstore_bucket::metadata_sys::get(bucket).await -} - -pub async fn set_swift_bucket_metadata(bucket: String, metadata: SwiftBucketMetadata) -> SwiftStorageResult<()> { - ecstore_bucket::metadata_sys::set_bucket_metadata(bucket, metadata).await -} diff --git a/crates/protocols/src/swift/versioning.rs b/crates/protocols/src/swift/versioning.rs index 6b4ad57f5..c9d7b13f1 100644 --- a/crates/protocols/src/swift/versioning.rs +++ b/crates/protocols/src/swift/versioning.rs @@ -54,7 +54,7 @@ use super::account::validate_account_access; use super::container::ContainerMapper; use super::object::{ObjectKeyMapper, SwiftObjectOptions as ObjectOptions, head_object}; -use super::storage_compat::resolve_swift_object_store_handle; +use super::resolve_swift_object_store_handle; use super::{SwiftError, SwiftResult}; use rustfs_credentials::Credentials; use rustfs_storage_api::{ListOperations as _, ObjectOperations as _}; diff --git a/crates/s3select-api/src/lib.rs b/crates/s3select-api/src/lib.rs index be937e73f..56688ea91 100644 --- a/crates/s3select-api/src/lib.rs +++ b/crates/s3select-api/src/lib.rs @@ -19,7 +19,6 @@ use std::fmt::Display; pub mod object_store; pub mod query; pub mod server; -mod storage_compat; #[cfg(test)] mod test; diff --git a/crates/s3select-api/src/object_store.rs b/crates/s3select-api/src/object_store.rs index b8aaaf3e2..86f753400 100644 --- a/crates/s3select-api/src/object_store.rs +++ b/crates/s3select-api/src/object_store.rs @@ -12,11 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -use super::storage_compat::{ - SELECT_DEFAULT_READ_BUFFER_SIZE, SelectGetObjectReader, SelectObjectInfo, SelectObjectOptions, SelectStorageError, - SelectStore, resolve_select_object_store_handle, select_default_read_buffer_size_u64, select_is_err_bucket_not_found, - select_is_err_object_not_found, select_is_err_version_not_found, -}; use async_trait::async_trait; use bytes::Bytes; use chrono::Utc; @@ -30,6 +25,9 @@ use object_store::{ }; use pin_project_lite::pin_project; use rustfs_common::DEFAULT_DELIMITER; +use rustfs_ecstore::api::{ + error as ecstore_error, global as ecstore_global, set_disk as ecstore_set_disk, storage as ecstore_storage, +}; use rustfs_storage_api::{HTTPRangeSpec, ObjectIO as _, ObjectOperations as _}; use s3s::S3Result; use s3s::dto::SelectObjectContentInput; @@ -49,6 +47,34 @@ use tokio::io::{AsyncRead, ReadBuf}; use tokio_util::io::ReaderStream; use transform_stream::AsyncTryStream; +type SelectStorageError = ecstore_error::StorageError; +type SelectStore = ecstore_storage::ECStore; +type SelectGetObjectReader = ::GetObjectReader; +type SelectObjectInfo = ::ObjectInfo; +type SelectObjectOptions = ::ObjectOptions; + +const SELECT_DEFAULT_READ_BUFFER_SIZE: usize = ecstore_set_disk::DEFAULT_READ_BUFFER_SIZE; + +fn select_default_read_buffer_size_u64() -> u64 { + u64::try_from(SELECT_DEFAULT_READ_BUFFER_SIZE).unwrap_or(u64::MAX) +} + +fn resolve_select_object_store_handle() -> Option> { + ecstore_global::resolve_object_store_handle() +} + +fn select_is_err_bucket_not_found(err: &SelectStorageError) -> bool { + ecstore_error::is_err_bucket_not_found(err) +} + +fn select_is_err_object_not_found(err: &SelectStorageError) -> bool { + ecstore_error::is_err_object_not_found(err) +} + +fn select_is_err_version_not_found(err: &SelectStorageError) -> bool { + ecstore_error::is_err_version_not_found(err) +} + /// Maximum allowed object size for JSON DOCUMENT mode. /// /// JSON DOCUMENT format requires loading the entire file into memory for DOM diff --git a/crates/s3select-api/src/storage_compat.rs b/crates/s3select-api/src/storage_compat.rs deleted file mode 100644 index 0e4c75c77..000000000 --- a/crates/s3select-api/src/storage_compat.rs +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright 2024 RustFS Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use rustfs_ecstore::api::error as ecstore_error; -use rustfs_ecstore::api::global as ecstore_global; -use rustfs_ecstore::api::set_disk as ecstore_set_disk; -use rustfs_ecstore::api::storage as ecstore_storage; - -pub(crate) type SelectStorageError = ecstore_error::StorageError; -pub(crate) type SelectStore = ecstore_storage::ECStore; -pub(crate) type SelectGetObjectReader = ::GetObjectReader; -pub(crate) type SelectObjectInfo = ::ObjectInfo; -pub(crate) type SelectObjectOptions = ::ObjectOptions; - -pub(crate) const SELECT_DEFAULT_READ_BUFFER_SIZE: usize = ecstore_set_disk::DEFAULT_READ_BUFFER_SIZE; - -pub(crate) fn select_default_read_buffer_size_u64() -> u64 { - u64::try_from(SELECT_DEFAULT_READ_BUFFER_SIZE).unwrap_or(u64::MAX) -} - -pub(crate) fn resolve_select_object_store_handle() -> Option> { - ecstore_global::resolve_object_store_handle() -} - -pub(crate) fn select_is_err_bucket_not_found(err: &SelectStorageError) -> bool { - ecstore_error::is_err_bucket_not_found(err) -} - -pub(crate) fn select_is_err_object_not_found(err: &SelectStorageError) -> bool { - ecstore_error::is_err_object_not_found(err) -} - -pub(crate) fn select_is_err_version_not_found(err: &SelectStorageError) -> bool { - ecstore_error::is_err_version_not_found(err) -} diff --git a/docs/architecture/migration-progress.md b/docs/architecture/migration-progress.md index 6fb8701b4..6bb04a588 100644 --- a/docs/architecture/migration-progress.md +++ b/docs/architecture/migration-progress.md @@ -5,16 +5,17 @@ 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-test-fuzz-compat-bridge-cleanup` -- 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`. -- Based on: API-123 slice. +- Branch: `overtrue/arch-standalone-thin-compat-cleanup` +- 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`. +- Based on: API-124 slice. - PR type for this branch: `pure-move` - Runtime behavior changes: none. -- Rust code changes: remove heal/scanner test and fuzz storage compatibility - bridges, then route their consumers directly to ECStore API owner modules. -- CI/script changes: allow the migrated test/fuzz targets to import owner APIs - directly and reject reintroduced test/fuzz bridge modules. -- Docs changes: record the API-124 test/fuzz bridge cleanup. +- Rust code changes: remove standalone thin storage compatibility bridges from + e2e, IAM store, notify, OBS, Swift, and S3 Select, then route their consumers + directly to ECStore API owner modules. +- CI/script changes: allow migrated standalone consumers to import owner APIs + directly and reject reintroduced bridge modules. +- Docs changes: record the API-125/API-126 standalone bridge cleanup. ## Phase 0 Tasks @@ -751,6 +752,32 @@ Status values: `[ ]` not started, `[~]` in progress, `[x]` complete, `[!]` block - Verification: heal/scanner test compile coverage, fuzz target compile coverage, test/fuzz bridge residual scan, migration and layer guards, formatting, diff hygiene, Rust risk scan, and three-expert review. +- [x] `API-125` Remove standalone thin compatibility bridges. + - Completed slice: replace e2e tests, IAM store object access, and notify + config persistence consumers with direct owner APIs, then delete their local + `storage_compat.rs` bridge modules. + - Acceptance: e2e, IAM store, and notify no longer route through local thin + storage compatibility bridges; migration rules reject deleted files, module + declarations, or bridge consumers. + - Must preserve: e2e RPC client behavior, site-replication target contracts, + IAM object associated types, notify server-config read/modify/save behavior, + and reload-if-changed semantics. + - Verification: affected crate compile coverage, standalone thin bridge + residual scan, migration and layer guards, formatting, diff hygiene, Rust + risk scan, and three-expert review. +- [x] `API-126` Remove remaining standalone owner compatibility bridges. + - Completed slice: replace OBS metrics, Swift object/container/account, and + S3 Select object-store consumers with direct owner APIs, then delete their + local `storage_compat.rs` bridge modules. + - Acceptance: OBS, Swift, and S3 Select no longer route through local thin + storage compatibility bridges; migration rules reject deleted files, module + declarations, or bridge consumers. + - Must preserve: OBS capacity, bucket usage, replication, and ILM metrics; + Swift bucket metadata and object IO contracts; S3 Select object reader, + error mapping, and default read-buffer behavior. + - Verification: affected crate compile coverage, remaining standalone bridge + residual scan, migration and layer guards, formatting, diff hygiene, Rust + risk scan, and three-expert review. - [x] `G-012` Inventory placement and repair invariants. - Acceptance: [`placement-repair-invariants.md`](placement-repair-invariants.md) records @@ -3784,14 +3811,40 @@ Status values: `[ ]` not started, `[~]` in progress, `[x]` complete, `[!]` block | Expert | Status | Notes | |---|---|---| -| Quality/architecture | pass | API-124 removes only test/fuzz bridge modules and keeps call sites on explicit ECStore owner APIs. | -| Migration preservation | pass | The migration guard now rejects the deleted test/fuzz bridge files, module declarations, and bridge consumers. | -| Testing/verification | pass | Heal/scanner test compile, fuzz target compile, residual scan, migration guard, layer guard, formatting, and diff hygiene passed. | +| Quality/architecture | pass | API-125/API-126 remove only standalone thin bridge modules and keep call sites on explicit owner APIs. | +| Migration preservation | pass | The migration guard now rejects the deleted e2e, IAM-store, notify, OBS, Swift, and S3 Select bridge files, module declarations, and bridge consumers. | +| Testing/verification | pass | Affected crate compile, residual scan, migration guard, layer guard, formatting, diff hygiene, and diff-only Rust risk scan passed. | ## Verification Notes Passed before push: +- Issue #660 API-126 current slice: + - `cargo check --tests -p e2e_test -p rustfs-iam -p rustfs-notify -p rustfs-obs -p rustfs-protocols -p rustfs-s3select-api`: 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. + - Remaining standalone compatibility bridge residual scan: passed. + - Rust risk scan: diff-only scan found no new unwrap/expect, numeric casts, + string-error public APIs, boxed public errors, println/eprintln, or relaxed + ordering. + +- Issue #660 API-125 current slice: + - `cargo check --tests -p e2e_test -p rustfs-iam -p rustfs-notify`: 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. + - Standalone thin compatibility bridge residual scan: passed. + - Rust risk scan: diff-only scan found no new unwrap/expect, numeric casts, + string-error public APIs, boxed public errors, println/eprintln, or relaxed + ordering. + - Issue #660 API-124 current slice: - `cargo check --tests -p rustfs-heal -p rustfs-scanner`: passed. - `cargo check --manifest-path fuzz/Cargo.toml --bins`: passed; transient `fuzz/Cargo.lock` refresh was restored to avoid dependency churn. diff --git a/scripts/check_architecture_migration_rules.sh b/scripts/check_architecture_migration_rules.sh index cb1a1a250..d36c51dcb 100755 --- a/scripts/check_architecture_migration_rules.sh +++ b/scripts/check_architecture_migration_rules.sh @@ -119,6 +119,7 @@ OBS_STORAGE_COMPAT_PASSTHROUGH_HITS_FILE="${TMP_DIR}/obs_storage_compat_passthro E2E_STORAGE_COMPAT_RPC_PASSTHROUGH_HITS_FILE="${TMP_DIR}/e2e_storage_compat_rpc_passthrough_hits.txt" TEST_STORAGE_COMPAT_PASSTHROUGH_HITS_FILE="${TMP_DIR}/test_storage_compat_passthrough_hits.txt" TEST_FUZZ_COMPAT_BRIDGE_HITS_FILE="${TMP_DIR}/test_fuzz_compat_bridge_hits.txt" +STANDALONE_THIN_COMPAT_BRIDGE_HITS_FILE="${TMP_DIR}/standalone_thin_compat_bridge_hits.txt" PRODUCTION_UNUSED_COMPAT_ALLOW_HITS_FILE="${TMP_DIR}/production_unused_compat_allow_hits.txt" BROAD_STORE_API_COMPAT_REEXPORT_HITS_FILE="${TMP_DIR}/broad_store_api_compat_reexport_hits.txt" NESTED_STORE_API_COMPAT_MODULE_HITS_FILE="${TMP_DIR}/nested_store_api_compat_module_hits.txt" @@ -713,7 +714,7 @@ fi --glob '!**/storage_compat.rs' \ --glob '!**/*storage_compat.rs' \ --glob '!target/**' \ - | rg -v '^(rustfs/src/(capacity/service|config/config_test|error|init|runtime_capabilities|startup_(background|bucket_metadata|fs_guard|iam|lifecycle|notification|server|services|shutdown|storage)|table_catalog|workload_admission)\.rs|rustfs/src/server/(event|http|module_switch|readiness)\.rs|rustfs/src/storage/s3_api/(bucket|multipart)\.rs|crates/heal/tests/(endpoint_index_test|heal_bug_fixes_test|heal_integration_test)\.rs|crates/scanner/tests/lifecycle_integration_test\.rs|fuzz/fuzz_targets/(bucket_validation|path_containment)\.rs):' || true + | rg -v '^(rustfs/src/(capacity/service|config/config_test|error|init|runtime_capabilities|startup_(background|bucket_metadata|fs_guard|iam|lifecycle|notification|server|services|shutdown|storage)|table_catalog|workload_admission)\.rs|rustfs/src/server/(event|http|module_switch|readiness)\.rs|rustfs/src/storage/s3_api/(bucket|multipart)\.rs|crates/e2e_test/src/(replication_extension_test|reliant/(grpc_lock_client|node_interact_test))\.rs|crates/heal/tests/(endpoint_index_test|heal_bug_fixes_test|heal_integration_test)\.rs|crates/notify/src/config_manager\.rs|crates/obs/src/metrics/(scheduler|stats_collector)\.rs|crates/protocols/src/swift/mod\.rs|crates/s3select-api/src/object_store\.rs|crates/scanner/tests/lifecycle_integration_test\.rs|fuzz/fuzz_targets/(bucket_validation|path_containment)\.rs):' || true ) | cat >"$DIRECT_ECSTORE_IMPORT_HITS_FILE" @@ -984,14 +985,18 @@ fi ( cd "$ROOT_DIR" - rg -n --no-heading 'rustfs_ecstore::api::' \ - crates/iam/src/storage_compat.rs \ - crates/heal/src/heal/storage_compat.rs \ - crates/obs/src/storage_compat.rs \ - crates/notify/src/storage_compat.rs \ - crates/protocols/src/swift/storage_compat.rs \ - crates/s3select-api/src/storage_compat.rs \ - crates/scanner/src/storage_compat.rs \ + { + for file in \ + crates/iam/src/storage_compat.rs \ + crates/heal/src/heal/storage_compat.rs \ + crates/obs/src/storage_compat.rs \ + crates/notify/src/storage_compat.rs \ + crates/protocols/src/swift/storage_compat.rs \ + crates/s3select-api/src/storage_compat.rs \ + crates/scanner/src/storage_compat.rs; do + [[ -f "$file" ]] && rg -n --with-filename --no-heading 'rustfs_ecstore::api::' "$file" + done + } \ | rg -v '^[^:]+:[0-9]+:use rustfs_ecstore::api::[a-z_]+ as ecstore_[a-z_]+;' || true ) >"$OUTER_CONSUMER_COMPAT_RAW_FACADE_PATH_HITS_FILE" @@ -1001,8 +1006,10 @@ fi ( cd "$ROOT_DIR" - rg -n --with-filename --no-heading 'rustfs_ecstore::api::' crates/e2e_test/src/storage_compat.rs \ - | rg -v '^[^:]+:[0-9]+:use rustfs_ecstore::api::[a-z_]+ as ecstore_[a-z_]+;' || true + if [[ -f crates/e2e_test/src/storage_compat.rs ]]; then + rg -n --with-filename --no-heading 'rustfs_ecstore::api::' crates/e2e_test/src/storage_compat.rs \ + | rg -v '^[^:]+:[0-9]+:use rustfs_ecstore::api::[a-z_]+ as ecstore_[a-z_]+;' || true + fi ) >"$RUSTFS_ROOT_E2E_COMPAT_RAW_FACADE_PATH_HITS_FILE" if [[ -s "$RUSTFS_ROOT_E2E_COMPAT_RAW_FACADE_PATH_HITS_FILE" ]]; then @@ -1303,6 +1310,35 @@ if [[ -s "$TEST_FUZZ_COMPAT_BRIDGE_HITS_FILE" ]]; then report_failure "heal/scanner test and fuzz targets must import ECStore owner APIs directly instead of local storage compatibility bridges: $(paste -sd '; ' "$TEST_FUZZ_COMPAT_BRIDGE_HITS_FILE")" fi +( + cd "$ROOT_DIR" + { + for file in \ + crates/e2e_test/src/storage_compat.rs \ + crates/iam/src/store/storage_compat.rs \ + crates/notify/src/storage_compat.rs \ + crates/obs/src/storage_compat.rs \ + crates/protocols/src/swift/storage_compat.rs \ + crates/s3select-api/src/storage_compat.rs; do + [[ -e "$file" ]] && printf '%s:1:standalone thin bridge file exists\n' "$file" + done + rg -n --with-filename 'storage_compat' \ + crates/e2e_test/src \ + crates/notify/src \ + crates/obs/src \ + crates/protocols/src/swift \ + crates/s3select-api/src \ + -g '*.rs' || true + rg -n --with-filename '^\s*use\s+super::storage_compat|store::storage_compat|\bmod\s+storage_compat' \ + crates/iam/src/store.rs \ + crates/iam/src/store/object.rs || true + } +) >"$STANDALONE_THIN_COMPAT_BRIDGE_HITS_FILE" + +if [[ -s "$STANDALONE_THIN_COMPAT_BRIDGE_HITS_FILE" ]]; then + report_failure "standalone e2e/IAM-store/notify consumers must import owner APIs directly instead of local storage compatibility bridges: $(paste -sd '; ' "$STANDALONE_THIN_COMPAT_BRIDGE_HITS_FILE")" +fi + ( cd "$ROOT_DIR" rg -n --with-filename 'crate::storage_compat' \ @@ -1331,8 +1367,10 @@ fi ( cd "$ROOT_DIR" - rg -n --no-heading 'use rustfs_ecstore::api::\{[^}]*\b(?:config|global)\b[^}]*\}\s*;' \ - crates/notify/src/storage_compat.rs || true + if [[ -f crates/notify/src/storage_compat.rs ]]; then + rg -n --no-heading 'use rustfs_ecstore::api::\{[^}]*\b(?:config|global)\b[^}]*\}\s*;' \ + crates/notify/src/storage_compat.rs || true + fi ) >"$NOTIFY_STORAGE_COMPAT_MODULE_HITS_FILE" if [[ -s "$NOTIFY_STORAGE_COMPAT_MODULE_HITS_FILE" ]]; then @@ -1341,8 +1379,10 @@ fi ( cd "$ROOT_DIR" - rg -n --no-heading 'pub\(crate\)\s+use rustfs_ecstore::api::data_usage::load_data_usage_from_backend' \ - crates/obs/src/storage_compat.rs || true + if [[ -f crates/obs/src/storage_compat.rs ]]; then + rg -n --no-heading 'pub\(crate\)\s+use rustfs_ecstore::api::data_usage::load_data_usage_from_backend' \ + crates/obs/src/storage_compat.rs || true + fi ) >"$OBS_STORAGE_COMPAT_PASSTHROUGH_HITS_FILE" if [[ -s "$OBS_STORAGE_COMPAT_PASSTHROUGH_HITS_FILE" ]]; then @@ -1351,8 +1391,10 @@ fi ( cd "$ROOT_DIR" - rg -n --no-heading 'pub\(crate\)\s+use rustfs_ecstore::api::rpc::\{[^}]*\b(?:gen_tonic_signature_interceptor|node_service_time_out_client|node_service_time_out_client_no_auth)\b[^}]*\}\s*;' \ - crates/e2e_test/src/storage_compat.rs || true + if [[ -f crates/e2e_test/src/storage_compat.rs ]]; then + rg -n --no-heading 'pub\(crate\)\s+use rustfs_ecstore::api::rpc::\{[^}]*\b(?:gen_tonic_signature_interceptor|node_service_time_out_client|node_service_time_out_client_no_auth)\b[^}]*\}\s*;' \ + crates/e2e_test/src/storage_compat.rs || true + fi ) >"$E2E_STORAGE_COMPAT_RPC_PASSTHROUGH_HITS_FILE" if [[ -s "$E2E_STORAGE_COMPAT_RPC_PASSTHROUGH_HITS_FILE" ]]; then