diff --git a/crates/ecstore/src/bucket/replication/README.md b/crates/ecstore/src/bucket/replication/README.md index c7c04f0ad..45a18e7b6 100644 --- a/crates/ecstore/src/bucket/replication/README.md +++ b/crates/ecstore/src/bucket/replication/README.md @@ -37,6 +37,7 @@ paths. | `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. | | `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. | | `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`. | @@ -82,6 +83,10 @@ paths. compatibility facade. 10. Keep ECStore owner modules outside `bucket/replication` behind bridge contracts when they need replication codec or config helper behavior. +11. Keep storage-api replication status/state helpers behind + `crates/storage-api/src/replication.rs` until the underlying wire contracts + can move without a `rustfs-replication` / `rustfs-storage-api` dependency + cycle. ## First Code-Bearing Step diff --git a/crates/storage-api/Cargo.toml b/crates/storage-api/Cargo.toml index 2218d302a..25f4d2cb4 100644 --- a/crates/storage-api/Cargo.toml +++ b/crates/storage-api/Cargo.toml @@ -29,9 +29,8 @@ doctest = false [dependencies] async-trait.workspace = true -# NOTE: This dependency on rustfs-filemeta is a known architectural limitation. -# The replication types (ReplicationStatusType, VersionPurgeStatusType, ReplicationState) -# are shared between storage-api and filemeta. Moving them would require changes in 300+ files. +# Storage-facing replication contracts are isolated in src/replication.rs until +# the underlying wire types can move without creating a replication/storage-api cycle. rustfs-filemeta.workspace = true serde.workspace = true time.workspace = true diff --git a/crates/storage-api/src/lib.rs b/crates/storage-api/src/lib.rs index 8c15ec8dd..e6bc3ea5d 100644 --- a/crates/storage-api/src/lib.rs +++ b/crates/storage-api/src/lib.rs @@ -23,6 +23,8 @@ pub mod object; pub mod observability; pub mod topology; +mod replication; + pub use admin::{DiskSetSelector, StorageAdminApi}; pub use bucket::{BucketInfo, BucketOperations, BucketOptions, DeleteBucketOptions, MakeBucketOptions, SRBucketDeleteOp}; pub use capability::{CapabilitySnapshotError, CapabilityState, CapabilityStatus}; diff --git a/crates/storage-api/src/object.rs b/crates/storage-api/src/object.rs index 074ce6f54..7853708bf 100644 --- a/crates/storage-api/src/object.rs +++ b/crates/storage-api/src/object.rs @@ -15,7 +15,7 @@ use std::fmt; use std::sync::Arc; -use rustfs_filemeta::{ +use crate::replication::{ ReplicationState, ReplicationStatusType, VersionPurgeStatusType, replication_statuses_map, version_purge_statuses_map, }; use time::OffsetDateTime; diff --git a/crates/storage-api/src/replication.rs b/crates/storage-api/src/replication.rs new file mode 100644 index 000000000..903da4d5f --- /dev/null +++ b/crates/storage-api/src/replication.rs @@ -0,0 +1,17 @@ +// 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_filemeta::{ + ReplicationState, ReplicationStatusType, VersionPurgeStatusType, replication_statuses_map, version_purge_statuses_map, +}; diff --git a/docs/architecture/crate-boundaries.md b/docs/architecture/crate-boundaries.md index 1bebbccd6..36f4019a9 100644 --- a/docs/architecture/crate-boundaries.md +++ b/docs/architecture/crate-boundaries.md @@ -29,6 +29,12 @@ Contract crates must stay below implementation crates. Initial forbidden edges: - `extension-schema -> rustfs` - `extension-schema -> ecstore` +`rustfs-storage-api` may only expose storage-facing replication status/state +contracts through `crates/storage-api/src/replication.rs` while the underlying +wire types still live in `rustfs-filemeta`. This keeps the temporary dependency +centralized until those wire contracts can move without introducing a +`rustfs-replication` / `rustfs-storage-api` cycle. + Existing migration checks live in: - `scripts/check_layer_dependencies.sh` diff --git a/docs/architecture/ecstore-module-split-plan.md b/docs/architecture/ecstore-module-split-plan.md index 70d94b3a9..b41321dde 100644 --- a/docs/architecture/ecstore-module-split-plan.md +++ b/docs/architecture/ecstore-module-split-plan.md @@ -125,6 +125,9 @@ Current coupling: - resync options, bucket/target resync status DTOs, status display labels, and the persisted resync status wire format live in `crates/replication`, with ECStore retaining only error mapping and MRF persistence locally; +- 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`; - admin replication extension target filtering and resync request construction stay behind the admin storage boundary instead of exposing replication work DTO construction to handlers; @@ -168,6 +171,10 @@ Required contracts before crate movement: `crates/ecstore/src/bucket/replication/replication_filemeta_boundary.rs`, while `FileInfo` remains in the storage boundary for storage trait bindings and walk options. +- `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 + `rustfs-replication` / `rustfs-storage-api` dependency cycle. - `ReplicationErrorBoundary`: ECStore error/result contracts and replication-specific error classifiers. `crate::error` imports are concentrated in diff --git a/scripts/check_architecture_migration_rules.sh b/scripts/check_architecture_migration_rules.sh index 124ebcee2..15ea67a97 100755 --- a/scripts/check_architecture_migration_rules.sh +++ b/scripts/check_architecture_migration_rules.sh @@ -84,6 +84,7 @@ require_source_contains "docs/architecture/obs-ecstore-dependency-inventory.md" require_source_contains "docs/architecture/obs-ecstore-dependency-inventory.md" "crates/obs/src/metrics/storage_api.rs" "observability ECStore storage_api boundary" 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" "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" require_source_contains "docs/architecture/ecstore-api-facade-inventory.md" "## Split Dependency Inventory" "ECStore split dependency inventory section" @@ -138,6 +139,7 @@ LIFECYCLE_OBJECT_LOCK_BOUNDARY_BYPASS_HITS_FILE="${TMP_DIR}/lifecycle_object_loc LIFECYCLE_REPLICATION_SINK_BYPASS_HITS_FILE="${TMP_DIR}/lifecycle_replication_sink_bypass_hits.txt" LIFECYCLE_REPLICATION_CRATE_BYPASS_HITS_FILE="${TMP_DIR}/lifecycle_replication_crate_bypass_hits.txt" ECSTORE_REPLICATION_CONTRACT_BYPASS_HITS_FILE="${TMP_DIR}/ecstore_replication_contract_bypass_hits.txt" +STORAGE_API_REPLICATION_CONTRACT_BYPASS_HITS_FILE="${TMP_DIR}/storage_api_replication_contract_bypass_hits.txt" STORE_API_EXTERNAL_LIST_CONSUMER_HITS_FILE="${TMP_DIR}/store_api_external_list_consumer_hits.txt" STORE_API_EXTERNAL_OPERATION_CONSUMER_HITS_FILE="${TMP_DIR}/store_api_external_operation_consumer_hits.txt" STORE_API_OBJECT_OPERATION_LOCAL_METHOD_HITS_FILE="${TMP_DIR}/store_api_object_operation_local_method_hits.txt" @@ -972,6 +974,18 @@ if [[ -s "$ECSTORE_REPLICATION_CONTRACT_BYPASS_HITS_FILE" ]]; then report_failure "ECStore owner replication contracts must stay behind crates/ecstore/src/bucket/replication: $(paste -sd '; ' "$ECSTORE_REPLICATION_CONTRACT_BYPASS_HITS_FILE")" fi +( + cd "$ROOT_DIR" + rg -n -U --with-filename 'use\s+rustfs_filemeta::\{[^}]*\b(ReplicationState|ReplicationStatusType|VersionPurgeStatusType|replication_statuses_map|version_purge_statuses_map)\b|use\s+rustfs_filemeta::(ReplicationState|ReplicationStatusType|VersionPurgeStatusType|replication_statuses_map|version_purge_statuses_map)\b|rustfs_filemeta::(Replication|VersionPurge|replication_statuses_map|version_purge_statuses_map)' \ + crates/storage-api/src \ + --glob '*.rs' | + rg -v '^crates/storage-api/src/replication\.rs:' || true +) >"$STORAGE_API_REPLICATION_CONTRACT_BYPASS_HITS_FILE" + +if [[ -s "$STORAGE_API_REPLICATION_CONTRACT_BYPASS_HITS_FILE" ]]; then + report_failure "storage-api replication contracts must stay behind crates/storage-api/src/replication.rs: $(paste -sd '; ' "$STORAGE_API_REPLICATION_CONTRACT_BYPASS_HITS_FILE")" +fi + ( cd "$ROOT_DIR" rg -n --no-heading 'rustfs_ecstore::store_api(?:::\{[^}]*\b(?:ListObjectVersionsInfo|ListObjectsV2Info|ObjectInfoOrErr)\b|::(?:ListObjectVersionsInfo|ListObjectsV2Info|ObjectInfoOrErr)\b)' \