refactor: remove standalone compat bridge modules (#3740)

This commit is contained in:
Zhengchao An
2026-06-22 17:35:58 +08:00
committed by GitHub
parent b63b076275
commit 539c68778d
26 changed files with 524 additions and 593 deletions
-1
View File
@@ -13,7 +13,6 @@
// limitations under the License.
mod reliant;
mod storage_compat;
// Common utilities for all E2E tests
#[cfg(test)]
@@ -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<tonic::transport::Channel, TonicInterceptor>,
>,
> {
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}")))
}
@@ -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<dyn Error>> {
@@ -53,7 +59,7 @@ async fn ping() -> Result<(), Box<dyn Error>> {
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<dyn Error>> {
#[tokio::test]
#[ignore = "requires running RustFS server at localhost:9000"]
async fn make_volume() -> Result<(), Box<dyn Error>> {
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<dyn Error>> {
#[tokio::test]
#[ignore = "requires running RustFS server at localhost:9000"]
async fn list_volumes() -> Result<(), Box<dyn Error>> {
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<dyn Error>> {
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<dyn Error>> {
#[tokio::test]
#[ignore = "requires running RustFS server at localhost:9000"]
async fn read_all() -> Result<(), Box<dyn Error>> {
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<dyn Error>> {
#[tokio::test]
#[ignore = "requires running RustFS server at localhost:9000"]
async fn storage_info() -> Result<(), Box<dyn Error>> {
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();
@@ -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,
-51
View File
@@ -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<tonic::transport::Channel, TonicInterceptor>,
>,
Box<dyn std::error::Error>,
> {
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<tonic::transport::Channel, TonicInterceptor>,
>,
Box<dyn std::error::Error>,
> {
ecstore_rpc::node_service_time_out_client_no_auth(addr).await
}
-1
View File
@@ -13,7 +13,6 @@
// limitations under the License.
pub mod object;
mod storage_compat;
use crate::cache::Cache;
use crate::error::Result;
+3 -3
View File
@@ -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<String> = LazyLock::new(|| format!("{IAM_CONFIG_ROOT_PREFIX}/iam"));
pub static IAM_CONFIG_USERS_PREFIX: LazyLock<String> = LazyLock::new(|| format!("{IAM_CONFIG_ROOT_PREFIX}/iam/users/"));
pub static IAM_CONFIG_SERVICE_ACCOUNTS_PREFIX: LazyLock<String> =
@@ -58,6 +56,8 @@ pub static IAM_CONFIG_POLICY_DB_GROUPS_PREFIX: LazyLock<String> =
LazyLock::new(|| format!("{IAM_CONFIG_ROOT_PREFIX}/iam/policydb/groups/"));
type ObjectInfoOrErr = StorageObjectInfoOrErr<IamObjectInfo, IamStorageError>;
type IamObjectInfo = <IamStore as ObjectOperations>::ObjectInfo;
type IamObjectOptions = <IamStore as ObjectOperations>::ObjectOptions;
const IAM_IDENTITY_FILE: &str = "identity.json";
const IAM_POLICY_FILE: &str = "policy.json";
-18
View File
@@ -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 = <IamStore as rustfs_storage_api::ObjectOperations>::ObjectInfo;
pub(super) type IamObjectOptions = <IamStore as rustfs_storage_api::ObjectOperations>::ObjectOptions;
+53 -14
View File
@@ -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<F>(mut modifier: F) -> Result<Option<Config>, 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<Arc<NotifyStore>> {
ecstore_global::resolve_object_store_handle()
}
async fn read_notify_server_config_without_migrate(store: Arc<NotifyStore>) -> Result<Config, NotifyConfigStoreError> {
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<NotifyStore>, 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,
-1
View File
@@ -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};
-63
View File
@@ -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<F>(mut modifier: F) -> Result<Option<Config>, 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<Arc<NotifyStore>> {
ecstore_global::resolve_object_store_handle()
}
async fn read_notify_server_config_without_migrate(store: Arc<NotifyStore>) -> Result<Config, NotifyConfigStoreError> {
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<NotifyStore>, config: &Config) -> Result<(), NotifyConfigStoreError> {
ecstore_config::com::save_server_config(store, config)
.await
.map_err(|err| NotifyConfigStoreError::Save(err.to_string()))
}
-1
View File
@@ -64,7 +64,6 @@ mod error;
mod global;
mod logging;
pub mod metrics;
mod storage_compat;
mod telemetry;
pub use cleaner::*;
+2 -2
View File
@@ -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);
+263 -10
View File
@@ -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 = <ObsStore as StorageAdminApi>::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<String, ObsBucketUsageInfo>,
}
struct ObsBucketUsageInfo {
size: u64,
objects_count: u64,
object_size_histogram: HashMap<String, u64>,
object_versions_histogram: HashMap<String, u64>,
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<ObsStore>) -> ecstore_error::Result<ObsDataUsageInfo> {
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<Arc<ObsStore>> {
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<Vec<ObsBucketReplicationBandwidthStats>> {
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<BucketReplicationStats> {
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::<f64>();
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::<f64>();
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::<f64>();
let recent_backlog_count = stats.mrf_stats.values().copied().filter(|value| *value > 0).sum::<i64>();
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<Utc>,
-286
View File
@@ -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 = <ObsStore as StorageAdminApi>::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<String, ObsBucketUsageInfo>,
}
pub(crate) struct ObsBucketUsageInfo {
pub(crate) size: u64,
pub(crate) objects_count: u64,
pub(crate) object_size_histogram: HashMap<String, u64>,
pub(crate) object_versions_histogram: HashMap<String, u64>,
pub(crate) versions_count: u64,
pub(crate) delete_markers_count: u64,
}
pub(crate) async fn load_obs_data_usage_from_backend(store: Arc<ObsStore>) -> ecstore_error::Result<ObsDataUsageInfo> {
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<Arc<ObsStore>> {
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<Vec<ObsBucketReplicationBandwidthStats>> {
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<MetricBucketReplicationStats> {
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::<f64>();
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::<f64>();
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::<f64>();
let recent_backlog_count = stats.mrf_stats.values().copied().filter(|value| *value > 0).sum::<i64>();
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)
}
+1 -1
View File
@@ -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};
+1 -1
View File
@@ -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,
+26 -1
View File
@@ -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<T> = ecstore_error::Result<T>;
type SwiftStore = ecstore_storage::ECStore;
pub type SwiftGetObjectReader = <SwiftStore as rustfs_storage_api::ObjectIO>::GetObjectReader;
pub type SwiftObjectInfo = <SwiftStore as rustfs_storage_api::ObjectOperations>::ObjectInfo;
pub type SwiftObjectOptions = <SwiftStore as rustfs_storage_api::ObjectOperations>::ObjectOptions;
pub type SwiftPutObjReader = <SwiftStore as rustfs_storage_api::ObjectIO>::PutObjectReader;
fn resolve_swift_object_store_handle() -> Option<Arc<SwiftStore>> {
ecstore_global::resolve_object_store_handle()
}
async fn get_swift_bucket_metadata(bucket: &str) -> SwiftStorageResult<Arc<SwiftBucketMetadata>> {
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
}
+2 -3
View File
@@ -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";
@@ -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<T> = ecstore_error::Result<T>;
pub(super) type SwiftStore = ecstore_storage::ECStore;
pub type SwiftGetObjectReader = <SwiftStore as rustfs_storage_api::ObjectIO>::GetObjectReader;
pub type SwiftObjectInfo = <SwiftStore as rustfs_storage_api::ObjectOperations>::ObjectInfo;
pub type SwiftObjectOptions = <SwiftStore as rustfs_storage_api::ObjectOperations>::ObjectOptions;
pub type SwiftPutObjReader = <SwiftStore as rustfs_storage_api::ObjectIO>::PutObjectReader;
pub fn resolve_swift_object_store_handle() -> Option<Arc<SwiftStore>> {
ecstore_global::resolve_object_store_handle()
}
pub async fn get_swift_bucket_metadata(bucket: &str) -> SwiftStorageResult<Arc<SwiftBucketMetadata>> {
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
}
+1 -1
View File
@@ -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 _};
-1
View File
@@ -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;
+31 -5
View File
@@ -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 = <SelectStore as rustfs_storage_api::ObjectIO>::GetObjectReader;
type SelectObjectInfo = <SelectStore as rustfs_storage_api::ObjectOperations>::ObjectInfo;
type SelectObjectOptions = <SelectStore as rustfs_storage_api::ObjectOperations>::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<Arc<SelectStore>> {
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
-46
View File
@@ -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 = <SelectStore as rustfs_storage_api::ObjectIO>::GetObjectReader;
pub(crate) type SelectObjectInfo = <SelectStore as rustfs_storage_api::ObjectOperations>::ObjectInfo;
pub(crate) type SelectObjectOptions = <SelectStore as rustfs_storage_api::ObjectOperations>::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<std::sync::Arc<SelectStore>> {
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)
}
+64 -11
View File
@@ -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.
+59 -17
View File
@@ -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