From d19ee6c51c68a3e33f1a9cbb53db86d53a65e357 Mon Sep 17 00:00:00 2001 From: Zhengchao An Date: Fri, 3 Jul 2026 22:48:21 +0800 Subject: [PATCH] refactor(replication): isolate storage api contracts (#4244) --- crates/ecstore/src/bucket/replication/README.md | 3 +++ crates/replication/src/delete.rs | 5 ++--- crates/replication/src/lib.rs | 1 + crates/replication/src/operation.rs | 4 ++-- crates/replication/src/queue.rs | 5 ++--- crates/replication/src/storage_api.rs | 15 +++++++++++++++ docs/architecture/ecstore-module-split-plan.md | 5 +++++ scripts/check_architecture_migration_rules.sh | 14 ++++++++++++++ 8 files changed, 44 insertions(+), 8 deletions(-) create mode 100644 crates/replication/src/storage_api.rs diff --git a/crates/ecstore/src/bucket/replication/README.md b/crates/ecstore/src/bucket/replication/README.md index c0d807292..c0e28ea80 100644 --- a/crates/ecstore/src/bucket/replication/README.md +++ b/crates/ecstore/src/bucket/replication/README.md @@ -39,6 +39,7 @@ paths. | `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`. | | `ReplicationFileMeta` | Replication status, decisions, MRF entries, resync decisions, and target reset helpers. | `rustfs_filemeta` replication contracts are concentrated in `replication_filemeta_boundary.rs`; `FileInfo` remains in the storage boundary for storage trait bindings and walk options. | | `StorageApiReplicationContracts` | Storage-api delete DTO replication state/status helpers. | The temporary `rustfs-filemeta` dependency is isolated in `crates/storage-api/src/replication.rs` until the wire contracts can move without creating a `rustfs-replication` / `rustfs-storage-api` cycle. | +| `ReplicationCrateStorageApiBoundary` | Storage API delete DTOs consumed by `rustfs-replication`. | `crates/replication/src/storage_api.rs` is the only direct `rustfs-storage-api` import boundary inside `rustfs-replication`. | | `ReplicationObjectDecisionContracts` | Object replication options, delete replication decisions, resync target projection, multipart planning, and delete-marker retry classifiers. | Owned by `crates/replication`; ECStore imports them through `replication_object_decision_boundary.rs`. | | `ReplicationQueueContracts` | Queue admission, heal queue results/actions, worker operations, worker sizing, and backpressure decisions. | Owned by `crates/replication`; ECStore imports them through `replication_queue_boundary.rs`. | | `ReplicationStatsContracts` | Bucket stats, replication target stats, queue/proxy metrics, and worker metric snapshots. | Owned by `crates/replication`; ECStore imports them through `replication_stats_boundary.rs`. | @@ -90,6 +91,8 @@ paths. cycle. 12. Keep direct `rustfs-filemeta` imports inside `rustfs-replication` concentrated in `crates/replication/src/filemeta.rs`. +13. Keep direct `rustfs-storage-api` imports inside `rustfs-replication` + concentrated in `crates/replication/src/storage_api.rs`. ## First Code-Bearing Step diff --git a/crates/replication/src/delete.rs b/crates/replication/src/delete.rs index 319d1f1d7..75430c2a9 100644 --- a/crates/replication/src/delete.rs +++ b/crates/replication/src/delete.rs @@ -14,8 +14,7 @@ use std::any::Any; -use rustfs_storage_api::DeletedObject; - +use crate::storage_api::DeletedObject; use crate::{MrfOpKind, MrfReplicateEntry, ReplicationType, ReplicationWorkerOperation}; #[derive(Debug, Clone, Default)] @@ -85,8 +84,8 @@ mod tests { DeletedObjectReplicationInfo, is_retryable_delete_replication_head_error, is_version_delete_replication, should_retry_delete_marker_purge, }; + use crate::storage_api::DeletedObject; use crate::{MrfOpKind, ReplicationType, ReplicationWorkerOperation}; - use rustfs_storage_api::DeletedObject; use uuid::Uuid; #[test] diff --git a/crates/replication/src/lib.rs b/crates/replication/src/lib.rs index 87d62991d..778171c36 100644 --- a/crates/replication/src/lib.rs +++ b/crates/replication/src/lib.rs @@ -24,6 +24,7 @@ pub mod resync; pub mod rule; pub mod runtime; pub mod stats; +mod storage_api; pub mod tagging; pub use config::{ diff --git a/crates/replication/src/operation.rs b/crates/replication/src/operation.rs index b8b9ffe95..f738f9480 100644 --- a/crates/replication/src/operation.rs +++ b/crates/replication/src/operation.rs @@ -14,7 +14,6 @@ use std::collections::HashMap; -use rustfs_storage_api::ObjectToDelete; use rustfs_utils::http::{ AMZ_BUCKET_REPLICATION_STATUS, AMZ_OBJECT_TAGGING, SSEC_ALGORITHM_HEADER, SSEC_KEY_HEADER, SSEC_KEY_MD5_HEADER, SUFFIX_REPLICATION_RESET_STATUS, get_header_map, @@ -23,6 +22,7 @@ use s3s::dto::ReplicationConfiguration; use time::OffsetDateTime; use uuid::Uuid; +use crate::storage_api::ObjectToDelete; use crate::{ ObjectOpts, ReplicateDecision, ReplicateTargetDecision, ReplicationConfigurationExt as _, ReplicationState, ReplicationStatusType, ReplicationType, ResyncTargetDecision, VersionPurgeStatusType, replication_statuses_map, @@ -318,8 +318,8 @@ mod tests { is_ssec_encrypted, resync_target_for_object, should_schedule_delete_replication, should_use_existing_delete_replication_info, should_use_existing_delete_replication_source, }; + use crate::storage_api::ObjectToDelete; use crate::{ReplicationStatusType, ReplicationType, VersionPurgeStatusType, target_reset_header}; - use rustfs_storage_api::ObjectToDelete; use rustfs_utils::http::{AMZ_BUCKET_REPLICATION_STATUS, SSEC_ALGORITHM_HEADER}; use s3s::dto::{ DeleteMarkerReplication, DeleteMarkerReplicationStatus, Destination, ExistingObjectReplication, diff --git a/crates/replication/src/queue.rs b/crates/replication/src/queue.rs index 016db9d61..8d34a1d58 100644 --- a/crates/replication/src/queue.rs +++ b/crates/replication/src/queue.rs @@ -14,8 +14,7 @@ use std::any::Any; -use rustfs_storage_api::DeletedObject; - +use crate::storage_api::DeletedObject; use crate::{ DeletedObjectReplicationInfo, MrfReplicateEntry, REPLICATE_EXISTING, REPLICATE_HEAL, REPLICATE_HEAL_DELETE, ReplicateObjectInfo, ReplicationStatusType, ReplicationType, ReplicationWorkerOperation, ResyncDecision, @@ -265,12 +264,12 @@ impl ReplicationWorkerOperation for ReplicationOperation { mod tests { use std::str::FromStr; - use rustfs_storage_api::DeletedObject; use uuid::Uuid; use super::{ ReplicationHealQueueAction, ReplicationOperation, ReplicationPriority, ReplicationQueueAdmission, ReplicationWorkerQueue, }; + use crate::storage_api::DeletedObject; use crate::{ DeletedObjectReplicationInfo, REPLICATE_EXISTING, REPLICATE_HEAL, REPLICATE_HEAL_DELETE, ReplicateDecision, ReplicateObjectInfo, ReplicateTargetDecision, ReplicationStatusType, ReplicationType, ReplicationWorkerOperation, diff --git a/crates/replication/src/storage_api.rs b/crates/replication/src/storage_api.rs new file mode 100644 index 000000000..9de76ebe9 --- /dev/null +++ b/crates/replication/src/storage_api.rs @@ -0,0 +1,15 @@ +// 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. + +pub(crate) use rustfs_storage_api::{DeletedObject, ObjectToDelete}; diff --git a/docs/architecture/ecstore-module-split-plan.md b/docs/architecture/ecstore-module-split-plan.md index 90dff1181..110acc44f 100644 --- a/docs/architecture/ecstore-module-split-plan.md +++ b/docs/architecture/ecstore-module-split-plan.md @@ -127,6 +127,8 @@ Current coupling: ECStore retaining only error mapping and MRF persistence locally; - `crates/replication/src/filemeta.rs` is the only direct filemeta wire-contract import boundary inside `rustfs-replication`; +- `crates/replication/src/storage_api.rs` is the only direct storage-api delete + DTO import boundary inside `rustfs-replication`; - storage-api delete replication status/state helpers use the local `crates/storage-api/src/replication.rs` contract boundary while the underlying wire types remain in `rustfs-filemeta`; @@ -176,6 +178,9 @@ Required contracts before crate movement: - `ReplicationCrateFileMetaFacade`: replication facade compatibility symbols that still originate in filemeta wire contracts are concentrated in `crates/replication/src/filemeta.rs` inside `rustfs-replication`. +- `ReplicationCrateStorageApiBoundary`: storage API delete DTOs consumed by + replication delete/queue/operation helpers are concentrated in + `crates/replication/src/storage_api.rs` inside `rustfs-replication`. - `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/scripts/check_architecture_migration_rules.sh b/scripts/check_architecture_migration_rules.sh index 5f689aa92..3012894cd 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" "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" require_source_contains "docs/architecture/ecstore-api-facade-inventory.md" "## Facade Group Inventory" "ECStore facade inventory group section" require_source_contains "docs/architecture/ecstore-api-facade-inventory.md" "## External Consumer Boundaries" "ECStore facade inventory consumer boundary section" @@ -205,6 +206,7 @@ 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" 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" REPLICATION_DELETE_WORKER_CONTRACT_BACKSLIDE_HITS_FILE="${TMP_DIR}/replication_delete_worker_contract_backslide_hits.txt" REPLICATION_OPERATION_CONTRACT_BACKSLIDE_HITS_FILE="${TMP_DIR}/replication_operation_contract_backslide_hits.txt" @@ -2621,6 +2623,18 @@ if [[ -s "$REPLICATION_CRATE_FILEMETA_BYPASS_HITS_FILE" ]]; then report_failure "replication crate filemeta contracts must stay behind crates/replication/src/filemeta.rs: $(paste -sd '; ' "$REPLICATION_CRATE_FILEMETA_BYPASS_HITS_FILE")" fi +( + cd "$ROOT_DIR" + rg -n --with-filename 'rustfs_storage_api::|use\s+rustfs_storage_api\b' \ + crates/replication/src \ + --glob '*.rs' | + rg -v '^crates/replication/src/storage_api\.rs:' || true +) >"$REPLICATION_CRATE_STORAGE_API_BYPASS_HITS_FILE" + +if [[ -s "$REPLICATION_CRATE_STORAGE_API_BYPASS_HITS_FILE" ]]; then + report_failure "replication crate storage-api contracts must stay behind crates/replication/src/storage_api.rs: $(paste -sd '; ' "$REPLICATION_CRATE_STORAGE_API_BYPASS_HITS_FILE")" +fi + ( cd "$ROOT_DIR" replication_config_rule_status=0