diff --git a/crates/ecstore/src/bucket/replication/README.md b/crates/ecstore/src/bucket/replication/README.md index 366d84115..042282292 100644 --- a/crates/ecstore/src/bucket/replication/README.md +++ b/crates/ecstore/src/bucket/replication/README.md @@ -35,6 +35,7 @@ paths. | `ReplicationStorage` | Object read/write/delete, object walk, metadata update, and target object IO. | ECStore object API, storage-api contracts, and read option types are concentrated in `replication_storage_boundary.rs`. | | `ReplicationMetadataStore` | Replication config, MRF/resync state, target reset headers, and status persistence. | Metadata sys access and replication metadata path constants are exposed through the contract type in `replication_metadata_boundary.rs`; versioning sys and config storage imports remain separate contracts. | | `EcstoreReplicationBoundaryImports` | ECStore-side imports from `rustfs-replication`. | Direct `rustfs-replication` imports under `crates/ecstore/src/bucket/replication` stay in `*_boundary.rs` modules, including config and resync facade re-exports. | +| `RuntimeReplicationFacadeConsumers` | Runtime owner consumers of replication DTOs and status types. | Scanner, admin, and storage owner facades import replication DTOs/status types through `rustfs-ecstore`; app storage keeps the remaining direct object/delete helper calls behind its local storage API boundary. | | `ReplicationResyncContracts` | Resync options, target status, bucket status, status classifiers, and persisted resync/MRF status wire format. | Owned by `crates/replication`; ECStore imports them through `replication_resync_boundary.rs`, which maps crate errors to ECStore errors. | | `ReplicationCrateFileMetaFacade` | Replication facade compatibility symbols that still originate in filemeta wire contracts. | `crates/replication/src/filemeta.rs` is the only direct `rustfs-filemeta` import boundary inside `rustfs-replication`. | | `ReplicationConfigStore` | Replication config persistence and config-derived labels used by target options. | Config read/save helpers and storage class labels are exposed through the contract type in `replication_config_store.rs`. | @@ -96,6 +97,9 @@ paths. concentrated in `crates/replication/src/storage_api.rs`. 14. Keep direct `rustfs-replication` imports inside ECStore replication concentrated in `*_boundary.rs` modules. +15. Keep scanner, admin, and storage-owner replication status/DTO consumers + behind the ECStore replication facade; only `rustfs/src/app/storage_api.rs` + may retain direct object/delete replication helper calls. ## First Code-Bearing Step diff --git a/crates/ecstore/src/bucket/replication/mod.rs b/crates/ecstore/src/bucket/replication/mod.rs index d129b1551..c52e8b9fa 100644 --- a/crates/ecstore/src/bucket/replication/mod.rs +++ b/crates/ecstore/src/bucket/replication/mod.rs @@ -47,9 +47,9 @@ pub use replication_config_boundary::{ObjectOpts, ReplicationConfigurationExt}; #[cfg(test)] pub(crate) use replication_filemeta_boundary::ReplicateTargetDecision; pub(crate) use replication_filemeta_boundary::{ - ReplicateDecision, ReplicationState, ReplicationStatusType, VersionPurgeStatusType, replication_statuses_map, - version_purge_statuses_map, + ReplicateDecision, ReplicationState, replication_statuses_map, version_purge_statuses_map, }; +pub use replication_filemeta_boundary::{ReplicateObjectInfo, ReplicationStatusType, ReplicationType, VersionPurgeStatusType}; pub(crate) use replication_lifecycle_bridge::{ReplicationLifecycleBridge, ReplicationLifecycleConfig}; pub(crate) use replication_migration_bridge::ReplicationMigrationBridge; pub use replication_object_bridge::ReplicationObjectBridge; diff --git a/crates/ecstore/src/bucket/replication/replication_filemeta_boundary.rs b/crates/ecstore/src/bucket/replication/replication_filemeta_boundary.rs index 997f64ccc..4bac7701e 100644 --- a/crates/ecstore/src/bucket/replication/replication_filemeta_boundary.rs +++ b/crates/ecstore/src/bucket/replication/replication_filemeta_boundary.rs @@ -15,7 +15,8 @@ pub(crate) use rustfs_replication::{MrfOpKind, MrfReplicateEntry}; pub(crate) use rustfs_replication::{ REPLICATE_EXISTING, REPLICATE_EXISTING_DELETE, REPLICATE_HEAL_DELETE, REPLICATE_INCOMING_DELETE, ReplicateDecision, - ReplicateObjectInfo, ReplicateTargetDecision, ReplicatedInfos, ReplicatedTargetInfo, ReplicationAction, ReplicationState, - ReplicationStatusType, ReplicationType, ReplicationWorkerOperation, ResyncDecision, VersionPurgeStatusType, - get_replication_state, parse_replicate_decision, replication_statuses_map, target_reset_header, version_purge_statuses_map, + ReplicateTargetDecision, ReplicatedInfos, ReplicatedTargetInfo, ReplicationAction, ReplicationState, + ReplicationWorkerOperation, ResyncDecision, get_replication_state, parse_replicate_decision, replication_statuses_map, + target_reset_header, version_purge_statuses_map, }; +pub use rustfs_replication::{ReplicateObjectInfo, ReplicationStatusType, ReplicationType, VersionPurgeStatusType}; diff --git a/crates/scanner/src/storage_api.rs b/crates/scanner/src/storage_api.rs index fb136ac1c..b7f574508 100644 --- a/crates/scanner/src/storage_api.rs +++ b/crates/scanner/src/storage_api.rs @@ -15,8 +15,6 @@ use std::collections::HashMap; use std::sync::Arc; -use rustfs_replication::{ReplicateObjectInfo, ReplicationType}; -pub(crate) use rustfs_replication::{ReplicationStatusType, VersionPurgeStatusType}; use serde::{Deserialize, Serialize}; pub(crate) use rustfs_ecstore::api::bucket::bucket_target_sys::BucketTargetSys as EcstoreBucketTargetSys; @@ -34,10 +32,13 @@ pub(crate) use rustfs_ecstore::api::bucket::metadata_sys::{ get_replication_config as ecstore_get_replication_config, }; pub(crate) use rustfs_ecstore::api::bucket::replication::{ - ReplicationConfig as EcstoreReplicationConfig, ReplicationConfigurationExt as EcstoreReplicationConfigurationExt, + ReplicateObjectInfo, ReplicationConfig as EcstoreReplicationConfig, + ReplicationConfigurationExt as EcstoreReplicationConfigurationExt, ReplicationHealQueueResult as EcstoreReplicationHealQueueResult, ReplicationQueueAdmission as EcstoreReplicationQueueAdmission, ReplicationScannerBridge as EcstoreReplicationScannerBridge, + ReplicationType, }; +pub(crate) use rustfs_ecstore::api::bucket::replication::{ReplicationStatusType, VersionPurgeStatusType}; pub(crate) use rustfs_ecstore::api::bucket::target::BucketTargets as EcstoreBucketTargets; pub(crate) use rustfs_ecstore::api::bucket::versioning::VersioningApi as EcstoreVersioningApi; pub(crate) use rustfs_ecstore::api::bucket::versioning_sys::BucketVersioningSys as EcstoreBucketVersioningSys; diff --git a/docs/architecture/ecstore-module-split-plan.md b/docs/architecture/ecstore-module-split-plan.md index e19cbc5b9..0cd48ca86 100644 --- a/docs/architecture/ecstore-module-split-plan.md +++ b/docs/architecture/ecstore-module-split-plan.md @@ -137,6 +137,8 @@ Current coupling: - admin replication extension target filtering and resync request construction stay behind the admin storage boundary instead of exposing replication work DTO construction to handlers; +- scanner, admin, and storage-owner replication status/DTO consumers import + those contracts through the ECStore replication facade; - app object and multipart writes call object-replication boundary helpers instead of constructing replication work DTOs or choosing object replication operation types at the use-case layer; @@ -185,6 +187,10 @@ Required contracts before crate movement: `crates/replication/src/storage_api.rs` inside `rustfs-replication`. - `EcstoreReplicationBoundaryImports`: ECStore-side imports from `rustfs-replication` are concentrated in replication `*_boundary.rs` modules. +- `RuntimeReplicationFacadeConsumers`: scanner, admin, and storage-owner + replication status/DTO consumers import through `rustfs-ecstore`, while app + storage keeps direct object/delete helper calls inside its storage API + boundary. - `StorageApiReplicationContracts`: storage-api delete DTO replication state/status helpers are concentrated in `crates/storage-api/src/replication.rs` until the underlying wire contracts can move without a diff --git a/rustfs/src/admin/storage_api.rs b/rustfs/src/admin/storage_api.rs index 94020c4d0..f0c3fa3db 100644 --- a/rustfs/src/admin/storage_api.rs +++ b/rustfs/src/admin/storage_api.rs @@ -150,7 +150,7 @@ pub(crate) trait AdminReplicationConfigExt { impl AdminReplicationConfigExt for s3s::dto::ReplicationConfiguration { fn filter_all_replication_target_arns(&self) -> Vec { let obj = ecstore_bucket::replication::ObjectOpts { - op_type: rustfs_replication::ReplicationType::All, + op_type: ecstore_bucket::replication::ReplicationType::All, ..Default::default() }; ::filter_target_arns( @@ -309,7 +309,7 @@ pub(crate) mod quota { pub(crate) mod replication { pub(crate) type BucketReplicationResyncStatus = super::ecstore_bucket::replication::BucketReplicationResyncStatus; pub(crate) type BucketStats = super::ecstore_bucket::replication::BucketStats; - pub(crate) type ReplicationStatusType = rustfs_replication::ReplicationStatusType; + pub(crate) type ReplicationStatusType = super::ecstore_bucket::replication::ReplicationStatusType; pub(crate) type ResyncOpts = super::ecstore_bucket::replication::ResyncOpts; #[cfg(test)] pub(crate) type ResyncStatusType = super::ecstore_bucket::replication::ResyncStatusType; diff --git a/rustfs/src/storage/storage_api.rs b/rustfs/src/storage/storage_api.rs index 94934ce4a..7e3be8a34 100644 --- a/rustfs/src/storage/storage_api.rs +++ b/rustfs/src/storage/storage_api.rs @@ -522,7 +522,7 @@ pub(crate) type ReadMultipleReq = ecstore_disk::ReadMultipleReq; pub(crate) type ReadMultipleResp = ecstore_disk::ReadMultipleResp; pub(crate) type ReadOptions = ecstore_disk::ReadOptions; pub(crate) type RenameDataResp = ecstore_disk::RenameDataResp; -pub(crate) type ReplicationStatusType = rustfs_replication::ReplicationStatusType; +pub(crate) type ReplicationStatusType = ecstore_bucket::replication::ReplicationStatusType; pub(crate) type ReplicationStats = StorageReplicationStatsHandle; pub(crate) type SetupType = ecstore_layout::SetupType; pub(crate) type StorageError = ecstore_error::StorageError; diff --git a/scripts/check_architecture_migration_rules.sh b/scripts/check_architecture_migration_rules.sh index c5aef38cd..eb831d36f 100755 --- a/scripts/check_architecture_migration_rules.sh +++ b/scripts/check_architecture_migration_rules.sh @@ -85,6 +85,7 @@ require_source_contains "docs/architecture/obs-ecstore-dependency-inventory.md" require_source_contains "docs/architecture/overview.md" "ecstore-api-facade-inventory.md" "architecture overview ECStore facade inventory link" require_source_contains "docs/architecture/ecstore-module-split-plan.md" "ecstore-api-facade-inventory.md" "ECStore split plan facade inventory link" require_source_contains "docs/architecture/ecstore-module-split-plan.md" "EcstoreReplicationBoundaryImports" "ECStore split plan replication boundary imports section" +require_source_contains "docs/architecture/ecstore-module-split-plan.md" "RuntimeReplicationFacadeConsumers" "ECStore split plan runtime replication facade consumer section" require_source_contains "docs/architecture/ecstore-module-split-plan.md" "ReplicationCrateFileMetaFacade" "ECStore split plan replication crate filemeta facade section" require_source_contains "docs/architecture/ecstore-module-split-plan.md" "ReplicationCrateStorageApiBoundary" "ECStore split plan replication crate storage-api boundary section" require_source_contains "docs/architecture/ecstore-module-split-plan.md" "StorageApiReplicationContracts" "ECStore split plan storage-api replication contract section" @@ -207,6 +208,8 @@ EXTERNAL_ECSTORE_API_BOUNDARY_HITS_FILE="${TMP_DIR}/external_ecstore_api_boundar 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" ECSTORE_REPLICATION_BOUNDARY_BYPASS_HITS_FILE="${TMP_DIR}/ecstore_replication_boundary_bypass_hits.txt" +SCANNER_REPLICATION_FACADE_BYPASS_HITS_FILE="${TMP_DIR}/scanner_replication_facade_bypass_hits.txt" +RUSTFS_REPLICATION_FACADE_BYPASS_HITS_FILE="${TMP_DIR}/rustfs_replication_facade_bypass_hits.txt" REPLICATION_CRATE_FILEMETA_BYPASS_HITS_FILE="${TMP_DIR}/replication_crate_filemeta_bypass_hits.txt" REPLICATION_CRATE_STORAGE_API_BYPASS_HITS_FILE="${TMP_DIR}/replication_crate_storage_api_bypass_hits.txt" REPLICATION_CONFIG_RULE_CONTRACT_BACKSLIDE_HITS_FILE="${TMP_DIR}/replication_config_rule_contract_backslide_hits.txt" @@ -2625,6 +2628,29 @@ if [[ -s "$ECSTORE_REPLICATION_BOUNDARY_BYPASS_HITS_FILE" ]]; then report_failure "ECStore replication rustfs-replication imports must stay behind *_boundary.rs modules: $(paste -sd '; ' "$ECSTORE_REPLICATION_BOUNDARY_BYPASS_HITS_FILE")" fi +( + cd "$ROOT_DIR" + rg -n --with-filename 'rustfs_replication::|use\s+rustfs_replication\b' \ + crates/scanner/src \ + --glob '*.rs' || true +) >"$SCANNER_REPLICATION_FACADE_BYPASS_HITS_FILE" + +if [[ -s "$SCANNER_REPLICATION_FACADE_BYPASS_HITS_FILE" ]]; then + report_failure "scanner replication contracts must come through the ECStore replication facade: $(paste -sd '; ' "$SCANNER_REPLICATION_FACADE_BYPASS_HITS_FILE")" +fi + +( + cd "$ROOT_DIR" + rg -n --with-filename 'rustfs_replication::|use\s+rustfs_replication\b' \ + rustfs/src \ + --glob '*.rs' | + rg -v '^rustfs/src/app/storage_api\.rs:' || true +) >"$RUSTFS_REPLICATION_FACADE_BYPASS_HITS_FILE" + +if [[ -s "$RUSTFS_REPLICATION_FACADE_BYPASS_HITS_FILE" ]]; then + report_failure "RustFS runtime replication contracts must stay behind storage API facades: $(paste -sd '; ' "$RUSTFS_REPLICATION_FACADE_BYPASS_HITS_FILE")" +fi + ( cd "$ROOT_DIR" rg -n --with-filename 'rustfs_filemeta::|use\s+rustfs_filemeta\b' \