From e5ab6ee8e40368359aa1f9086faab1b7789a9fc7 Mon Sep 17 00:00:00 2001 From: Zhengchao An Date: Wed, 1 Jul 2026 18:54:05 +0800 Subject: [PATCH] refactor(ecstore): make replication facade explicit (#4139) --- .../ecstore/src/bucket/replication/README.md | 6 ++++-- crates/ecstore/src/bucket/replication/mod.rs | 18 ++++++++++++------ crates/ecstore/src/runtime/sources.rs | 5 ++++- docs/architecture/ecstore-module-split-plan.md | 7 ++++++- scripts/check_architecture_migration_rules.sh | 11 +++++++++++ 5 files changed, 37 insertions(+), 10 deletions(-) diff --git a/crates/ecstore/src/bucket/replication/README.md b/crates/ecstore/src/bucket/replication/README.md index 2f75e1852..23168f050 100644 --- a/crates/ecstore/src/bucket/replication/README.md +++ b/crates/ecstore/src/bucket/replication/README.md @@ -10,14 +10,14 @@ and lifecycle/heal scheduling paths. | Module | Current role | Split blocker | |---|---|---| | `config.rs` | Replication config helpers, rule matching, and tag filtering. | Uses replication-local filemeta/tagging boundaries and S3 DTOs directly. | -| `datatypes.rs` | Replication status and operation DTOs. | Publicly re-exported through the ECStore replication facade. | +| `datatypes.rs` | Replication status and operation DTOs. | Explicitly re-exported through the ECStore replication facade. | | `replication_pool.rs` | Replication queue, worker pool, MRF persistence, bucket stats, and delete/object scheduling. | Depends on bucket target sys, bucket metadata sys, metadata paths, and file metadata replication contracts through local boundaries, config storage, storage contracts through the replication storage boundary, runtime sources, and notification state. | | `replication_resyncer.rs` | Object replication, delete replication, resync, MRF encode/decode, target calls, and multipart target upload paths. | Depends on target calls and target config types through the replication target boundary, metadata paths and metadata systems through the replication metadata boundary, file metadata replication contracts through the filemeta boundary, error contracts through the error boundary, versioning systems, storage contracts through the replication storage boundary, config-derived storage class labels through the config store, runtime sources, notification events and local event host selection through the event sink, bandwidth reader wrapping, and SetDisks lock timing. | | `replication_state.rs` | Replication queue/stat state and worker accounting. | Reads runtime sources, file metadata replication contracts, error contracts, and bucket monitor handles through local boundaries, and owns shared replication pool/stat state. | | `replication_lifecycle_bridge.rs` | Lifecycle-originated delete replication admission and version-purge state construction. | Depends on replication config/rule matching, delete-replication decisions, and replication delete scheduling through a local contract type. | | `replication_scanner_bridge.rs` | Scanner-originated replication heal admission. | Keeps scanner-facing heal queueing behind a local contract type instead of exporting the internal queue function directly. | | `rule.rs` | Rule evaluation helpers for object replication options. | Depends on ECStore replication object option types. | -| `mod.rs` | Compatibility re-export facade for the current ECStore owner. | Must stay stable until downstream scanner, lifecycle, heal, and metrics paths compile through replacement contracts. | +| `mod.rs` | Explicit compatibility re-export facade for the current ECStore owner. | Wildcard re-exports are guarded so internal helpers do not leak back into the public facade. | ## Required Contracts @@ -60,6 +60,8 @@ and lifecycle/heal scheduling paths. focused replication tests before broad gates. Non-behavioral contract-shape cleanup may batch already-established boundary wrappers when the owner and call semantics do not change. +8. Keep the compatibility facade in `mod.rs` as an explicit symbol list. Do not + reintroduce wildcard re-exports for replication implementation modules. ## First Code-Bearing Step diff --git a/crates/ecstore/src/bucket/replication/mod.rs b/crates/ecstore/src/bucket/replication/mod.rs index 345235dfd..d9a8620b0 100644 --- a/crates/ecstore/src/bucket/replication/mod.rs +++ b/crates/ecstore/src/bucket/replication/mod.rs @@ -23,7 +23,7 @@ mod replication_lifecycle_bridge; mod replication_lock_boundary; mod replication_metadata_boundary; mod replication_msgp_boundary; -mod replication_pool; +pub(crate) mod replication_pool; mod replication_resyncer; mod replication_scanner_bridge; mod replication_state; @@ -34,12 +34,18 @@ mod replication_versioning_boundary; mod rule; mod runtime_boundary; -pub use config::*; -pub use datatypes::*; +pub use config::{ObjectOpts, ReplicationConfigurationExt}; +pub use datatypes::ResyncStatusType; pub(crate) use replication_lifecycle_bridge::{ReplicationLifecycleBridge, ReplicationLifecycleConfig}; -pub use replication_pool::*; -pub use replication_resyncer::*; +pub use replication_pool::{ + DynReplicationPool, ReplicationHealQueueResult, ReplicationPoolTrait, ReplicationQueueAdmission, get_global_replication_pool, + get_global_replication_stats, init_background_replication, schedule_replication, schedule_replication_delete, +}; +pub use replication_resyncer::{ + BucketReplicationResyncStatus, DeletedObjectReplicationInfo, MustReplicateOptions, ReplicationConfig, ResyncOpts, + TargetReplicationResyncStatus, check_replicate_delete, get_must_replicate_options, must_replicate, +}; +pub(crate) use replication_resyncer::{decode_resync_file, encode_resync_file}; pub use replication_scanner_bridge::ReplicationScannerBridge; pub use replication_state::{BucketStats, ReplicationStats}; pub use replication_storage_boundary::{ReplicationObjectIO, ReplicationStorage}; -pub use rule::*; diff --git a/crates/ecstore/src/runtime/sources.rs b/crates/ecstore/src/runtime/sources.rs index 6bde5c8bc..42d7000cb 100644 --- a/crates/ecstore/src/runtime/sources.rs +++ b/crates/ecstore/src/runtime/sources.rs @@ -23,7 +23,10 @@ use crate::disk::endpoint::Endpoint; use crate::{ bucket::lifecycle::bucket_lifecycle_ops::{ExpiryState, GLOBAL_EXPIRY_STATE, GLOBAL_TRANSITION_STATE, TransitionState}, bucket::metadata_sys::{BucketMetadataSys, get_global_bucket_metadata_sys}, - bucket::replication::{DynReplicationPool, GLOBAL_REPLICATION_POOL, GLOBAL_REPLICATION_STATS, ReplicationStats}, + bucket::replication::{ + DynReplicationPool, ReplicationStats, + replication_pool::{GLOBAL_REPLICATION_POOL, GLOBAL_REPLICATION_STATS}, + }, config::{get_global_storage_class, set_global_storage_class, storageclass}, disk::{DiskAPI, DiskOption, DiskStore, new_disk}, error::Result, diff --git a/docs/architecture/ecstore-module-split-plan.md b/docs/architecture/ecstore-module-split-plan.md index c6992d8e7..4a9612294 100644 --- a/docs/architecture/ecstore-module-split-plan.md +++ b/docs/architecture/ecstore-module-split-plan.md @@ -121,7 +121,9 @@ Current coupling: - modules inside `bucket/replication` use local relative paths rather than the ECStore owner path for replication self-imports; - replication runtime source access uses storage/bandwidth boundary aliases for - ECStore object store and bucket monitor implementation types. + ECStore object store and bucket monitor implementation types; +- the ECStore replication facade in `mod.rs` uses explicit compatibility + exports instead of wildcard re-exports from implementation modules. Required contracts before crate movement: @@ -174,6 +176,9 @@ Required contracts before crate movement: - `ReplicationScannerBridge`: scanner-originated replication heal scheduling is exposed through the contract type in `crates/ecstore/src/bucket/replication/replication_scanner_bridge.rs`. +- `ReplicationFacade`: the current `rustfs_ecstore::api::bucket::replication` + compatibility surface is an explicit symbol list guarded against wildcard + re-exports while downstream owners migrate to narrower contracts. First safe PR: diff --git a/scripts/check_architecture_migration_rules.sh b/scripts/check_architecture_migration_rules.sh index 825cfc82a..7ab0700b6 100755 --- a/scripts/check_architecture_migration_rules.sh +++ b/scripts/check_architecture_migration_rules.sh @@ -198,6 +198,7 @@ EXTERNAL_TEST_ECSTORE_COMPAT_BYPASS_HITS_FILE="${TMP_DIR}/external_test_ecstore_ FUZZ_ECSTORE_COMPAT_BYPASS_HITS_FILE="${TMP_DIR}/fuzz_ecstore_compat_bypass_hits.txt" EXTERNAL_ECSTORE_API_BOUNDARY_HITS_FILE="${TMP_DIR}/external_ecstore_api_boundary_hits.txt" REPLICATION_FACADE_BYPASS_HITS_FILE="${TMP_DIR}/replication_facade_bypass_hits.txt" +REPLICATION_FACADE_WILDCARD_EXPORT_HITS_FILE="${TMP_DIR}/replication_facade_wildcard_export_hits.txt" REPLICATION_BANDWIDTH_BOUNDARY_BYPASS_HITS_FILE="${TMP_DIR}/replication_bandwidth_boundary_bypass_hits.txt" REPLICATION_CONFIG_STORE_BYPASS_HITS_FILE="${TMP_DIR}/replication_config_store_bypass_hits.txt" REPLICATION_ERROR_BOUNDARY_BYPASS_HITS_FILE="${TMP_DIR}/replication_error_boundary_bypass_hits.txt" @@ -2504,6 +2505,16 @@ if [[ -s "$REPLICATION_FACADE_BYPASS_HITS_FILE" ]]; then report_failure "replication facade imports must stay in local storage_api boundaries: $(paste -sd '; ' "$REPLICATION_FACADE_BYPASS_HITS_FILE")" fi +( + cd "$ROOT_DIR" + rg -n --with-filename 'pub(?:\(crate\))?\s+use\s+(?:config|datatypes|replication_pool|replication_resyncer|replication_state|rule)::\*;' \ + crates/ecstore/src/bucket/replication/mod.rs || true +) >"$REPLICATION_FACADE_WILDCARD_EXPORT_HITS_FILE" + +if [[ -s "$REPLICATION_FACADE_WILDCARD_EXPORT_HITS_FILE" ]]; then + report_failure "replication facade must use explicit compatibility exports instead of wildcard re-exports: $(paste -sd '; ' "$REPLICATION_FACADE_WILDCARD_EXPORT_HITS_FILE")" +fi + ( cd "$ROOT_DIR" find crates/ecstore/src/bucket/replication -type f -name '*.rs' -print0 |