refactor(ecstore): route runtime getters through facade (#4109)

This commit is contained in:
Zhengchao An
2026-06-30 14:32:01 +08:00
committed by GitHub
parent 143833ec45
commit 4538919efa
7 changed files with 66 additions and 34 deletions
+5 -6
View File
@@ -112,17 +112,16 @@ pub mod event {
pub mod global {
pub use crate::runtime::global::{
get_global_bucket_monitor, get_global_deployment_id, get_global_endpoints_opt, get_global_lock_client,
get_global_lock_clients, get_global_region, get_global_tier_config_mgr, global_rustfs_port, is_dist_erasure, is_erasure,
is_erasure_sd, is_first_cluster_node_local, new_object_layer_fn, resolve_object_store_handle, set_global_endpoints,
set_global_region, set_global_rustfs_port, set_object_store_resolver, shutdown_background_services, update_erasure_type,
set_global_endpoints, set_global_region, set_global_rustfs_port, set_object_store_resolver, shutdown_background_services,
update_erasure_type,
};
}
pub mod runtime {
pub use crate::runtime::sources::{
boot_time, bucket_monitor, expiry_state_handle, first_cluster_node_is_local, global_tier_config_mgr, local_disk_map_read,
object_store_handle, setup_is_erasure, setup_is_erasure_sd, transition_state_handle,
boot_time, bucket_monitor, deployment_id, endpoint_pools, expiry_state_handle, first_cluster_node_is_local,
global_lock_client, global_lock_clients, global_tier_config_mgr, local_disk_map_read, object_store_handle, region,
rustfs_port, setup_is_dist_erasure, setup_is_erasure, setup_is_erasure_sd, transition_state_handle,
};
}
+14 -10
View File
@@ -32,10 +32,10 @@ use crate::{
GLOBAL_BOOT_TIME, GLOBAL_EVENT_NOTIFIER, GLOBAL_IS_ERASURE_SD, GLOBAL_LIFECYCLE_SYS, GLOBAL_LOCAL_DISK_ID_MAP,
GLOBAL_LOCAL_DISK_MAP, GLOBAL_LOCAL_DISK_SET_DRIVES, GLOBAL_LOCAL_NODE_NAME_FALLBACK, GLOBAL_ROOT_DISK_THRESHOLD,
GLOBAL_TIER_CONFIG_MGR, TypeLocalDiskSetDrives, get_background_services_cancel_token, get_global_bucket_monitor,
get_global_deployment_id, get_global_endpoints, get_global_endpoints_opt, get_global_lock_clients, get_global_region,
get_global_tier_config_mgr, global_rustfs_port, init_global_bucket_monitor, is_dist_erasure, is_erasure,
is_first_cluster_node_local, resolve_object_store_handle, set_global_deployment_id, set_global_lock_client,
set_global_lock_clients, set_object_layer, update_erasure_type,
get_global_deployment_id, get_global_endpoints, get_global_endpoints_opt, get_global_lock_client,
get_global_lock_clients, get_global_region, get_global_tier_config_mgr, global_rustfs_port, init_global_bucket_monitor,
is_dist_erasure, is_erasure, is_first_cluster_node_local, resolve_object_store_handle, set_global_deployment_id,
set_global_lock_client, set_global_lock_clients, set_object_layer, update_erasure_type,
},
services::batch_processor::{GlobalBatchProcessors, get_global_processors},
services::event_notification::EventNotifier,
@@ -113,7 +113,7 @@ pub fn object_store_handle() -> Option<Arc<ECStore>> {
resolve_object_store_handle()
}
pub(crate) fn endpoint_pools() -> Option<EndpointServerPools> {
pub fn endpoint_pools() -> Option<EndpointServerPools> {
get_global_endpoints_opt()
}
@@ -140,7 +140,7 @@ pub async fn setup_is_erasure() -> bool {
is_erasure().await
}
pub(crate) async fn setup_is_dist_erasure() -> bool {
pub async fn setup_is_dist_erasure() -> bool {
is_dist_erasure().await
}
@@ -176,7 +176,7 @@ pub(crate) fn default_local_node_name() -> String {
GLOBAL_LOCAL_NODE_NAME_FALLBACK.to_string()
}
pub(crate) fn rustfs_port() -> u16 {
pub fn rustfs_port() -> u16 {
global_rustfs_port()
}
@@ -263,7 +263,7 @@ pub(crate) fn deployment_upload_id(upload_id: &str) -> String {
.encode_to_string(format!("{}.{}", get_global_deployment_id().unwrap_or_default(), upload_id).as_bytes())
}
pub(crate) fn deployment_id() -> Option<String> {
pub fn deployment_id() -> Option<String> {
get_global_deployment_id()
}
@@ -289,7 +289,11 @@ pub(crate) fn global_lock_manager() -> Arc<rustfs_lock::GlobalLockManager> {
rustfs_lock::get_global_lock_manager()
}
pub(crate) fn global_lock_clients() -> Option<&'static HashMap<String, Arc<dyn LockClient>>> {
pub fn global_lock_client() -> Option<Arc<dyn LockClient>> {
get_global_lock_client()
}
pub fn global_lock_clients() -> Option<&'static HashMap<String, Arc<dyn LockClient>>> {
get_global_lock_clients()
}
@@ -320,7 +324,7 @@ pub(crate) fn bucket_metadata_sys() -> Option<Arc<RwLock<BucketMetadataSys>>> {
get_global_bucket_metadata_sys()
}
pub(crate) fn region() -> Option<Region> {
pub fn region() -> Option<Region> {
get_global_region()
}
@@ -12,7 +12,7 @@ replication, or `SetDisks` runtime behavior.
| `storage`, `disk`, `layout`, `error`, `runtime`, `cluster`, `rpc` | Compatibility spine for storage, disk topology, runtime handles, cluster control, and internode calls. | Keep until replacement contracts compile in downstream boundary files. |
| `bucket`, `config`, `tier`, `data_usage`, `capacity`, `notification`, `metrics`, `rebalance` | Domain and service facades still consumed through owner-local `storage_api` boundaries. | Narrow one group at a time after explicit aliases or wrappers exist. |
| `set_disk`, `object`, `rio`, `bitrot`, `erasure`, `compression`, `cache`, `client`, `store_list` | Low-level object IO, reader, erasure, cache, and migration helper compatibility. | Keep stable while `SetDisks` remains the shared state carrier. |
| `admin`, `event`, `global` | Admin, event hook, and legacy global compatibility. | Do not widen; replace mutable/global access with runtime-source contracts first. |
| `admin`, `event`, `global` | Admin, event hook, and legacy global compatibility. | Keep `global` limited to bootstrap writes and lifecycle controls; read-only runtime access must use runtime-source contracts. |
## External Consumer Boundaries
@@ -20,7 +20,7 @@ External `rustfs_ecstore::api` imports must stay in these local boundary files:
| Boundary file | Current facade families |
|---|---|
| `rustfs/src/storage/storage_api.rs` | Broad RustFS storage owner bridge for admin, bucket, capacity, client, compression, cluster, config, data usage, disk, error, event, global, layout, metrics, notification, rebalance, rio, rpc, set disk, storage, and tier. |
| `rustfs/src/storage/storage_api.rs` | Broad RustFS storage owner bridge for admin, bucket, capacity, client, compression, cluster, config, data usage, disk, error, event, global bootstrap controls, runtime-source getters, layout, metrics, notification, rebalance, rio, rpc, set disk, storage, and tier. |
| `crates/scanner/src/storage_api.rs` | Scanner bridge for bucket lifecycle, replication, metadata, capacity, config, data usage, disk, error, runtime, set disk, storage, and tier. |
| `crates/obs/src/metrics/storage_api.rs` | Metrics bridge for bucket bandwidth, lifecycle, replication, quota, capacity, data usage, error, runtime, and storage. |
| `crates/iam/src/storage_api.rs` | IAM bridge for config, error, notification, runtime, and storage. |
@@ -43,6 +43,9 @@ The architecture guard snapshots the files currently allowed to reference
- `rustfs/src/storage/storage_api.rs`
That boundary now keeps only bootstrap writes and lifecycle controls in the
global facade. Read-only runtime getters must be exported through
`rustfs_ecstore::api::runtime` and consumed through the local storage facade.
New direct uses must either move behind an existing owner-local boundary or
update this plan and the guard in the same reviewed migration PR.
+4 -4
View File
@@ -40,17 +40,17 @@ migration PR removes or replaces each item.
|---|---|---|---|
| `APP_CONTEXT_SINGLETON` | `rustfs/src/app/context/global.rs` | Owner-local compatibility | Keep as the context-first facade while no-context startup and embedded callers still exist. |
| `GLOBAL_OBJECT_API`, `GLOBAL_OBJECT_STORE_RESOLVER` | `crates/ecstore/src/runtime/global.rs`, `rustfs/src/app/context/global.rs`, and storage compatibility APIs | Runtime migration target | Do not migrate first; it is tied to storage startup, IAM-after-storage AppContext publication, and data-plane resolver compatibility. The object-store resolver is now published from the AppContext owner path, no longer re-exported from the RustFS storage root, and RustFS AppContext tests no longer use the old `new_object_layer_fn` fallback chain. RustFS storage root no longer re-exports ECStore runtime/global facade symbols; callers must use storage/app/admin facades. |
| `GLOBAL_ENDPOINTS`, `GLOBAL_IS_ERASURE`, `GLOBAL_IS_DIST_ERASURE`, `GLOBAL_IS_ERASURE_SD`, `GLOBAL_ROOT_DISK_THRESHOLD` | `crates/ecstore/src/runtime/global.rs` and `crates/ecstore/src/runtime/sources.rs` | Runtime migration target | Endpoint, setup-type, and root-disk-threshold access now stays behind ECStore runtime helpers; move endpoint ownership only after readiness and quorum behavior have explicit coverage. |
| `GLOBAL_ENDPOINTS`, `GLOBAL_IS_ERASURE`, `GLOBAL_IS_DIST_ERASURE`, `GLOBAL_IS_ERASURE_SD`, `GLOBAL_ROOT_DISK_THRESHOLD` | `crates/ecstore/src/runtime/global.rs` and `crates/ecstore/src/runtime/sources.rs` | Runtime migration target | Endpoint and setup-type reads now flow through ECStore `api::runtime` helpers at the RustFS storage facade boundary; root-disk-threshold access stays behind ECStore runtime helpers. Move endpoint ownership only after readiness and quorum behavior have explicit coverage. |
| `GLOBAL_LOCAL_DISK_MAP`, `GLOBAL_LOCAL_DISK_ID_MAP`, `GLOBAL_LOCAL_DISK_SET_DRIVES` | `crates/ecstore/src/runtime/global.rs` and `crates/ecstore/src/runtime/sources.rs` | Runtime migration target | Local disk map, disk-id cache, and set-drive access now stay behind ECStore runtime-source helpers instead of direct global access; preserve disk lookup, remote/local classification, and test reset hooks in later ownership changes. |
| `GLOBAL_EXPIRY_STATE`, `GLOBAL_TRANSITION_STATE`, `GLOBAL_LIFECYCLE_SYS` | `crates/ecstore/src/bucket/lifecycle/*`, `crates/ecstore/src/runtime/global.rs`, and `crates/ecstore/src/runtime/sources.rs` | Runtime migration target | Lifecycle state globals now stay behind ECStore lifecycle owner helpers and ECStore runtime-source helpers; RustFS AppContext has expiry/transition state interfaces and resolver coverage, and daily tier stats derive from the transition-state handle instead of a separate context boundary; scanner expiry-state access still uses the ECStore runtime `expiry_state_handle` boundary until scanner gets an injected provider. |
| `GLOBAL_REPLICATION_POOL`, `GLOBAL_REPLICATION_STATS`, `GLOBAL_BUCKET_MONITOR` | `crates/ecstore/src/bucket/replication/*`, `crates/ecstore/src/runtime/global.rs` | Runtime migration target | Replication pool/stat access now stays behind replication owner and ECStore runtime-source helpers; bucket-monitor direct access now stays behind ECStore runtime helpers while AppContext/runtime-source resolvers remain the caller boundary. |
| `GLOBAL_REPLICATION_POOL`, `GLOBAL_REPLICATION_STATS`, `GLOBAL_BUCKET_MONITOR` | `crates/ecstore/src/bucket/replication/*`, `crates/ecstore/src/runtime/global.rs` | Runtime migration target | Replication pool/stat access now stays behind replication owner and ECStore runtime-source helpers; bucket-monitor reads now flow through ECStore `api::runtime` at the RustFS storage facade boundary while AppContext/runtime-source resolvers remain the caller boundary. |
| `GLOBAL_TIER_CONFIG_MGR`, `GLOBAL_STORAGE_CLASS`, `GLOBAL_CONFIG_SYS`, `GLOBAL_SERVER_CONFIG` | `crates/ecstore/src/config`, `crates/config`, `rustfs/src/app/context/runtime_sources.rs` | Runtime migration target | Tier config manager reads and reloads now use the ECStore runtime-source helper; move remaining config state through config/runtime-source owners only, without combining storage-class behavior or persistence changes. |
| `GLOBAL_EVENT_NOTIFIER`, `GLOBAL_NOTIFICATION_SYS` | `crates/ecstore/src/runtime/global.rs`, `crates/ecstore/src/runtime/sources.rs`, and `crates/ecstore/src/services/*` | Runtime migration target | `GLOBAL_EVENT_NOTIFIER` and `GLOBAL_NOTIFICATION_SYS` access now stay behind ECStore runtime-source and notification owner helpers; move remaining notification ownership only through notify/runtime-source boundaries. |
| `EVENT_DISPATCH_HOOK` | `crates/ecstore/src/services/event_notification.rs`, RustFS server event bridge, and storage compatibility APIs | Runtime migration target / owner helper | Direct hook storage stays inside the ECStore event-notification owner; RustFS registers the bridge through the storage compatibility facade until event dispatch ownership moves behind an injected notification sink. |
| `GLOBAL_BUCKET_METADATA_SYS` | `crates/ecstore/src/metadata/bucket_sys.rs`, `crates/ecstore/src/runtime/sources.rs`, and RustFS storage compatibility APIs | Runtime migration target | Bucket metadata system direct access now stays inside the ECStore metadata owner; callers use metadata owner helpers or storage/runtime-source compatibility functions until metadata ownership moves behind an injected runtime context. |
| `GLOBAL_BOOT_TIME`, `GLOBAL_BACKGROUND_SERVICES_CANCEL_TOKEN`, `GLOBAL_DEPLOYMENT_ID`, `GLOBAL_REGION`, `GLOBAL_RUSTFS_PORT`, `GLOBAL_LOCAL_NODE_NAME_FALLBACK`, `GLOBAL_LOCAL_NODE_NAME_HEX_FALLBACK` | `crates/ecstore/src/runtime/global.rs`, `crates/ecstore/src/runtime/sources.rs` | Runtime migration target | Boot time, background service cancellation token reads, and ECStore local-node-name fallback reads now stay behind the ECStore runtime-source API; deployment ID, region, and port direct access now stays behind owner helpers until the remaining scalar handles migrate. |
| `GLOBAL_BOOT_TIME`, `GLOBAL_BACKGROUND_SERVICES_CANCEL_TOKEN`, `GLOBAL_DEPLOYMENT_ID`, `GLOBAL_REGION`, `GLOBAL_RUSTFS_PORT`, `GLOBAL_LOCAL_NODE_NAME_FALLBACK`, `GLOBAL_LOCAL_NODE_NAME_HEX_FALLBACK` | `crates/ecstore/src/runtime/global.rs`, `crates/ecstore/src/runtime/sources.rs` | Runtime migration target | Boot time, background service cancellation token reads, ECStore local-node-name fallback reads, and deployment ID/region/port reads now stay behind the ECStore runtime-source API; scalar writes remain behind bootstrap owner helpers until ownership handles replace them. |
| `WORKLOAD_ADMISSION_SNAPSHOT_PROVIDER` | `crates/ecstore/src/runtime/sources.rs`, RustFS startup background setup, and storage compatibility APIs | Runtime migration target / owner helper | Startup publishes the workload provider through the storage compatibility facade, and ECStore data movement reads it only through the runtime-source helper until workload admission ownership moves into an explicit runtime context. |
| `GLOBAL_LOCAL_LOCK_CLIENT`, `GLOBAL_LOCK_CLIENTS`, `GLOBAL_LOCK_MANAGER` | `crates/ecstore/src/runtime/global.rs`, `crates/lock` | Runtime migration target / process-global split | ECStore lock client direct access now stays behind ECStore runtime helpers; preserve lock quorum and lock client selection while keeping the process-level lock manager separate from endpoint-specific clients. |
| `GLOBAL_LOCAL_LOCK_CLIENT`, `GLOBAL_LOCK_CLIENTS`, `GLOBAL_LOCK_MANAGER` | `crates/ecstore/src/runtime/global.rs`, `crates/lock` | Runtime migration target / process-global split | ECStore lock client reads now flow through ECStore `api::runtime` helpers at the RustFS storage facade boundary; preserve lock quorum and lock client selection while keeping the process-level lock manager separate from endpoint-specific clients. |
| `GLOBAL_CONN_MAP`, `GLOBAL_LOCAL_NODE_NAME`, `GLOBAL_RUSTFS_HOST`, `GLOBAL_RUSTFS_ADDR`, `GLOBAL_ROOT_CERT`, `GLOBAL_MTLS_IDENTITY`, `GLOBAL_OUTBOUND_TLS_GENERATION` | `crates/common`, `crates/tls-runtime`, `crates/ecstore/src/runtime/sources.rs` | Runtime migration target / process-global split | Internode connection cache, common local node name, RustFS host/address reads, and outbound TLS material reads are now owned behind `rustfs_common` helpers; migrate the remaining transport and TLS state only after internode transport and outbound TLS ownership are explicit, without changing cached channel reuse or TLS reload semantics. |
| `GLOBAL_RUSTFS_RPC_SECRET` | `crates/credentials`, `crates/ecstore/src/runtime/sources.rs` | Runtime migration target / process-global split | RPC auth token writes now stay behind the `rustfs_credentials` helper boundary; migrate only if runtime secret ownership changes, preserving lazy environment and credential-derived token semantics. |
| `GLOBAL_HEAL_MANAGER`, `GLOBAL_HEAL_CHANNEL_PROCESSOR`, `GLOBAL_AHM_SERVICES_CANCEL_TOKEN` | `crates/heal/src/lib.rs` | Runtime migration target / process-global split | Direct access now stays inside the heal owner; callers use heal helper functions until heal runtime ownership moves behind explicit owner handles. |
+14 -12
View File
@@ -376,14 +376,16 @@ pub(crate) mod ecstore_event {
pub(crate) mod ecstore_global {
pub(crate) use rustfs_ecstore::api::global::{
get_global_bucket_monitor, get_global_deployment_id, get_global_endpoints_opt, get_global_lock_client,
get_global_lock_clients, get_global_region, global_rustfs_port, is_dist_erasure, set_global_endpoints, set_global_region,
set_global_rustfs_port, set_object_store_resolver, shutdown_background_services, update_erasure_type,
set_global_endpoints, set_global_region, set_global_rustfs_port, set_object_store_resolver, shutdown_background_services,
update_erasure_type,
};
}
pub(crate) mod ecstore_runtime {
pub(crate) use rustfs_ecstore::api::runtime::{boot_time, global_tier_config_mgr};
pub(crate) use rustfs_ecstore::api::runtime::{
boot_time, bucket_monitor, deployment_id, endpoint_pools, global_lock_client, global_lock_clients,
global_tier_config_mgr, region, rustfs_port, setup_is_dist_erasure,
};
}
#[allow(unused_imports)]
@@ -1083,32 +1085,32 @@ pub(crate) fn is_all_buckets_not_found(errs: &[Option<DiskError>]) -> bool {
}
pub(crate) fn get_global_lock_client() -> Option<Arc<dyn rustfs_lock::client::LockClient>> {
ecstore_global::get_global_lock_client()
ecstore_runtime::global_lock_client()
}
pub(crate) fn get_global_lock_clients()
-> Option<&'static std::collections::HashMap<String, Arc<dyn rustfs_lock::client::LockClient>>> {
ecstore_global::get_global_lock_clients()
ecstore_runtime::global_lock_clients()
}
pub(crate) fn get_global_bucket_monitor() -> Option<Arc<BucketBandwidthMonitor>> {
ecstore_global::get_global_bucket_monitor()
ecstore_runtime::bucket_monitor()
}
pub(crate) fn get_global_endpoints_opt() -> Option<EndpointServerPools> {
ecstore_global::get_global_endpoints_opt()
ecstore_runtime::endpoint_pools()
}
pub(crate) fn get_global_deployment_id() -> Option<String> {
ecstore_global::get_global_deployment_id()
ecstore_runtime::deployment_id()
}
pub(crate) fn get_global_region() -> Option<s3s::region::Region> {
ecstore_global::get_global_region()
ecstore_runtime::region()
}
pub(crate) fn global_rustfs_port() -> u16 {
ecstore_global::global_rustfs_port()
ecstore_runtime::rustfs_port()
}
pub(crate) fn get_global_tier_config_mgr() -> Arc<tokio::sync::RwLock<TierConfigMgr>> {
@@ -1130,7 +1132,7 @@ pub(crate) fn get_global_notification_sys() -> Option<&'static NotificationSys>
}
pub(crate) async fn is_dist_erasure() -> bool {
ecstore_global::is_dist_erasure().await
ecstore_runtime::setup_is_dist_erasure().await
}
pub(crate) async fn collect_local_metrics(
@@ -187,6 +187,8 @@ EXTERNAL_RUNTIME_STORAGE_API_BYPASS_HITS_FILE="${TMP_DIR}/external_runtime_stora
GLOBAL_FACADE_BOUNDARY_EXPECTED_FILE="${TMP_DIR}/global_facade_boundary_expected.txt"
GLOBAL_FACADE_BOUNDARY_ACTUAL_FILE="${TMP_DIR}/global_facade_boundary_actual.txt"
GLOBAL_FACADE_BOUNDARY_DIFF_FILE="${TMP_DIR}/global_facade_boundary_diff.txt"
ECSTORE_GLOBAL_READ_ONLY_EXPORT_HITS_FILE="${TMP_DIR}/ecstore_global_read_only_export_hits.txt"
RUSTFS_STORAGE_GLOBAL_READ_ONLY_IMPORT_HITS_FILE="${TMP_DIR}/rustfs_storage_global_read_only_import_hits.txt"
EXTERNAL_STORAGE_API_DOMAIN_BYPASS_HITS_FILE="${TMP_DIR}/external_storage_api_domain_bypass_hits.txt"
EXTERNAL_STORAGE_API_ROOT_REEXPORT_HITS_FILE="${TMP_DIR}/external_storage_api_root_reexport_hits.txt"
RUSTFS_STORAGE_API_ROOT_REEXPORT_HITS_FILE="${TMP_DIR}/rustfs_storage_api_root_reexport_hits.txt"
@@ -1695,6 +1697,28 @@ if ! diff -u "$GLOBAL_FACADE_BOUNDARY_EXPECTED_FILE" "$GLOBAL_FACADE_BOUNDARY_AC
report_failure "ECStore global facade access must stay inside reviewed local storage_api boundaries: $(tr '\n' '; ' <"$GLOBAL_FACADE_BOUNDARY_DIFF_FILE")"
fi
READ_ONLY_ECSTORE_GLOBAL_SYMBOL_PATTERN='get_global_bucket_monitor|get_global_deployment_id|get_global_endpoints|get_global_endpoints_opt|get_global_lock_client|get_global_lock_clients|get_global_region|get_global_tier_config_mgr|global_rustfs_port|is_dist_erasure|is_erasure|is_erasure_sd|is_first_cluster_node_local|new_object_layer_fn|resolve_object_store_handle'
(
cd "$ROOT_DIR"
awk '/pub mod global \{/,/^}/ {print FILENAME ":" FNR ":" $0}' crates/ecstore/src/api/mod.rs |
rg "\b(${READ_ONLY_ECSTORE_GLOBAL_SYMBOL_PATTERN})\b" || true
) >"$ECSTORE_GLOBAL_READ_ONLY_EXPORT_HITS_FILE"
if [[ -s "$ECSTORE_GLOBAL_READ_ONLY_EXPORT_HITS_FILE" ]]; then
report_failure "read-only ECStore runtime getters must be exported through rustfs_ecstore::api::runtime, not api::global: $(paste -sd '; ' "$ECSTORE_GLOBAL_READ_ONLY_EXPORT_HITS_FILE")"
fi
(
cd "$ROOT_DIR"
awk '/pub\(crate\) mod ecstore_global \{/,/^}/ {print FILENAME ":" FNR ":" $0}' rustfs/src/storage/storage_api.rs |
rg "\b(${READ_ONLY_ECSTORE_GLOBAL_SYMBOL_PATTERN})\b" || true
) >"$RUSTFS_STORAGE_GLOBAL_READ_ONLY_IMPORT_HITS_FILE"
if [[ -s "$RUSTFS_STORAGE_GLOBAL_READ_ONLY_IMPORT_HITS_FILE" ]]; then
report_failure "RustFS storage facade must source read-only ECStore runtime getters through api::runtime: $(paste -sd '; ' "$RUSTFS_STORAGE_GLOBAL_READ_ONLY_IMPORT_HITS_FILE")"
fi
(
cd "$ROOT_DIR"
rg -n --with-filename '^use rustfs_storage_api|rustfs_storage_api::' \