From 4538919efa36a04f4ff878e88c0c2bb4d90185dc Mon Sep 17 00:00:00 2001 From: Zhengchao An Date: Tue, 30 Jun 2026 14:32:01 +0800 Subject: [PATCH] refactor(ecstore): route runtime getters through facade (#4109) --- crates/ecstore/src/api/mod.rs | 11 ++++---- crates/ecstore/src/runtime/sources.rs | 24 ++++++++++------- .../ecstore-api-facade-inventory.md | 4 +-- .../global-state-crate-split-plan.md | 3 +++ docs/architecture/global-state-inventory.md | 8 +++--- rustfs/src/storage/storage_api.rs | 26 ++++++++++--------- scripts/check_architecture_migration_rules.sh | 24 +++++++++++++++++ 7 files changed, 66 insertions(+), 34 deletions(-) diff --git a/crates/ecstore/src/api/mod.rs b/crates/ecstore/src/api/mod.rs index 15ecd8533..2d130e66c 100644 --- a/crates/ecstore/src/api/mod.rs +++ b/crates/ecstore/src/api/mod.rs @@ -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, }; } diff --git a/crates/ecstore/src/runtime/sources.rs b/crates/ecstore/src/runtime/sources.rs index dc89809d7..6bde5c8bc 100644 --- a/crates/ecstore/src/runtime/sources.rs +++ b/crates/ecstore/src/runtime/sources.rs @@ -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> { resolve_object_store_handle() } -pub(crate) fn endpoint_pools() -> Option { +pub fn endpoint_pools() -> Option { 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 { +pub fn deployment_id() -> Option { get_global_deployment_id() } @@ -289,7 +289,11 @@ pub(crate) fn global_lock_manager() -> Arc { rustfs_lock::get_global_lock_manager() } -pub(crate) fn global_lock_clients() -> Option<&'static HashMap>> { +pub fn global_lock_client() -> Option> { + get_global_lock_client() +} + +pub fn global_lock_clients() -> Option<&'static HashMap>> { get_global_lock_clients() } @@ -320,7 +324,7 @@ pub(crate) fn bucket_metadata_sys() -> Option>> { get_global_bucket_metadata_sys() } -pub(crate) fn region() -> Option { +pub fn region() -> Option { get_global_region() } diff --git a/docs/architecture/ecstore-api-facade-inventory.md b/docs/architecture/ecstore-api-facade-inventory.md index 3eb3a2ab1..4305d8968 100644 --- a/docs/architecture/ecstore-api-facade-inventory.md +++ b/docs/architecture/ecstore-api-facade-inventory.md @@ -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. | diff --git a/docs/architecture/global-state-crate-split-plan.md b/docs/architecture/global-state-crate-split-plan.md index d278add03..2db6a811c 100644 --- a/docs/architecture/global-state-crate-split-plan.md +++ b/docs/architecture/global-state-crate-split-plan.md @@ -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. diff --git a/docs/architecture/global-state-inventory.md b/docs/architecture/global-state-inventory.md index 114e02188..6960d26a4 100644 --- a/docs/architecture/global-state-inventory.md +++ b/docs/architecture/global-state-inventory.md @@ -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. | diff --git a/rustfs/src/storage/storage_api.rs b/rustfs/src/storage/storage_api.rs index 399bba2fb..e57a4f5de 100644 --- a/rustfs/src/storage/storage_api.rs +++ b/rustfs/src/storage/storage_api.rs @@ -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]) -> bool { } pub(crate) fn get_global_lock_client() -> Option> { - ecstore_global::get_global_lock_client() + ecstore_runtime::global_lock_client() } pub(crate) fn get_global_lock_clients() -> Option<&'static std::collections::HashMap>> { - ecstore_global::get_global_lock_clients() + ecstore_runtime::global_lock_clients() } pub(crate) fn get_global_bucket_monitor() -> Option> { - ecstore_global::get_global_bucket_monitor() + ecstore_runtime::bucket_monitor() } pub(crate) fn get_global_endpoints_opt() -> Option { - ecstore_global::get_global_endpoints_opt() + ecstore_runtime::endpoint_pools() } pub(crate) fn get_global_deployment_id() -> Option { - ecstore_global::get_global_deployment_id() + ecstore_runtime::deployment_id() } pub(crate) fn get_global_region() -> Option { - 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> { @@ -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( diff --git a/scripts/check_architecture_migration_rules.sh b/scripts/check_architecture_migration_rules.sh index b85ce9031..7ec69c0ed 100755 --- a/scripts/check_architecture_migration_rules.sh +++ b/scripts/check_architecture_migration_rules.sh @@ -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::' \