diff --git a/crates/ecstore/src/bucket/replication/README.md b/crates/ecstore/src/bucket/replication/README.md index 855a8305d..7d6da9d0f 100644 --- a/crates/ecstore/src/bucket/replication/README.md +++ b/crates/ecstore/src/bucket/replication/README.md @@ -12,8 +12,8 @@ and lifecycle/heal scheduling paths. | `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. | | `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, versioning systems, storage contracts through the replication storage boundary, 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 and file metadata replication contracts through local boundaries, and owns shared replication pool/stat 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, 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, and error contracts through local boundaries, and owns shared replication pool/stat state. | | `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. | @@ -25,6 +25,7 @@ and lifecycle/heal scheduling 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 concentrated in `replication_metadata_boundary.rs`; versioning sys and config storage imports remain. | | `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. | +| `ReplicationErrorBoundary` | ECStore error/result contracts and replication-specific error classifiers. | `crate::error` imports are concentrated in `replication_error_boundary.rs`. | | `ReplicationTargetStore` | Bucket target listing, target client lookup, target offline checks, target config types, and target operation option types. | Bucket target sys access, `BucketTargets`, and target operation types are concentrated in `replication_target_boundary.rs`. | | `ReplicationRuntime` | Worker pool, queue sizing, stats, bucket monitor, local node identity, cancellation, and admission state. | Direct runtime source/global access and shared replication pool/stat state. | | `ReplicationBandwidthLimiter` | Target reader wrapping for replication bandwidth accounting and throttling. | Direct bucket bandwidth reader imports from resyncer paths. | diff --git a/crates/ecstore/src/bucket/replication/mod.rs b/crates/ecstore/src/bucket/replication/mod.rs index bf7b0a976..af09536ed 100644 --- a/crates/ecstore/src/bucket/replication/mod.rs +++ b/crates/ecstore/src/bucket/replication/mod.rs @@ -16,6 +16,7 @@ mod config; pub mod datatypes; mod replication_bandwidth_boundary; mod replication_config_store; +mod replication_error_boundary; mod replication_event_sink; mod replication_filemeta_boundary; mod replication_lock_boundary; diff --git a/crates/ecstore/src/bucket/replication/replication_config_store.rs b/crates/ecstore/src/bucket/replication/replication_config_store.rs index 0ee48f0e1..5e1b38173 100644 --- a/crates/ecstore/src/bucket/replication/replication_config_store.rs +++ b/crates/ecstore/src/bucket/replication/replication_config_store.rs @@ -12,9 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. +use super::replication_error_boundary::Result; use super::replication_storage_boundary::ReplicationObjectIO; use crate::config::com; -use crate::error::Result; use std::sync::Arc; pub(crate) async fn read(api: Arc, file: &str) -> Result> diff --git a/crates/ecstore/src/bucket/replication/replication_error_boundary.rs b/crates/ecstore/src/bucket/replication/replication_error_boundary.rs new file mode 100644 index 000000000..502dca6a9 --- /dev/null +++ b/crates/ecstore/src/bucket/replication/replication_error_boundary.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 crate::error::{Error, Result, is_err_object_not_found, is_err_version_not_found}; diff --git a/crates/ecstore/src/bucket/replication/replication_metadata_boundary.rs b/crates/ecstore/src/bucket/replication/replication_metadata_boundary.rs index 3848bea2c..6d46db0c3 100644 --- a/crates/ecstore/src/bucket/replication/replication_metadata_boundary.rs +++ b/crates/ecstore/src/bucket/replication/replication_metadata_boundary.rs @@ -14,11 +14,12 @@ use crate::bucket::metadata_sys; use crate::disk::{BUCKET_META_PREFIX, RUSTFS_META_BUCKET}; -use crate::error::{Error, Result}; use rustfs_utils::path::path_join_buf; use s3s::dto::ReplicationConfiguration; use time::OffsetDateTime; +use super::replication_error_boundary::{Error, Result}; + pub(crate) const REPLICATION_DIR: &str = ".replication"; pub(crate) const RESYNC_FILE_NAME: &str = "resync.bin"; pub(crate) const MRF_REPLICATION_FILE: &str = "config/replication/mrf.bin"; diff --git a/crates/ecstore/src/bucket/replication/replication_pool.rs b/crates/ecstore/src/bucket/replication/replication_pool.rs index 1beff008d..d77929e9e 100644 --- a/crates/ecstore/src/bucket/replication/replication_pool.rs +++ b/crates/ecstore/src/bucket/replication/replication_pool.rs @@ -13,6 +13,7 @@ // limitations under the License. use super::replication_config_store as config_store; +use super::replication_error_boundary::Error as EcstoreError; use super::replication_filemeta_boundary::{ MrfOpKind, MrfReplicateEntry, REPLICATE_EXISTING, REPLICATE_HEAL, REPLICATE_HEAL_DELETE, ReplicateDecision, ReplicateObjectInfo, ReplicatedTargetInfo, ReplicationStatusType, ReplicationType, ReplicationWorkerOperation, @@ -32,7 +33,6 @@ use crate::bucket::replication::replication_resyncer::{ save_resync_status, }; use crate::bucket::replication::replication_state::ReplicationStats; -use crate::error::Error as EcstoreError; use lazy_static::lazy_static; use rustfs_utils::http::{SUFFIX_REPLICATION_TIMESTAMP, get_str}; use std::any::Any; diff --git a/crates/ecstore/src/bucket/replication/replication_resyncer.rs b/crates/ecstore/src/bucket/replication/replication_resyncer.rs index e2cd8a86f..85a8d899e 100644 --- a/crates/ecstore/src/bucket/replication/replication_resyncer.rs +++ b/crates/ecstore/src/bucket/replication/replication_resyncer.rs @@ -14,6 +14,7 @@ use super::replication_bandwidth_boundary; use super::replication_config_store as config_store; +use super::replication_error_boundary::{Error, Result, is_err_object_not_found, is_err_version_not_found}; use super::replication_event_sink::{EventArgs, send_event, send_local_event}; use super::replication_filemeta_boundary::{ MrfOpKind, MrfReplicateEntry, REPLICATE_EXISTING, REPLICATE_EXISTING_DELETE, ReplicateDecision, ReplicateObjectInfo, @@ -37,7 +38,6 @@ use super::replication_versioning_boundary as versioning_boundary; use super::runtime_boundary as runtime_sources; use crate::bucket::replication::ResyncStatusType; use crate::bucket::replication::{ObjectOpts, ReplicationConfigurationExt as _}; -use crate::error::{Error, Result, is_err_object_not_found, is_err_version_not_found}; use aws_sdk_s3::error::{ProvideErrorMetadata, SdkError}; use aws_sdk_s3::operation::head_object::{HeadObjectError, HeadObjectOutput}; use aws_sdk_s3::primitives::ByteStream; diff --git a/crates/ecstore/src/bucket/replication/replication_state.rs b/crates/ecstore/src/bucket/replication/replication_state.rs index 62103fa99..09facdf5b 100644 --- a/crates/ecstore/src/bucket/replication/replication_state.rs +++ b/crates/ecstore/src/bucket/replication/replication_state.rs @@ -12,9 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. +use super::replication_error_boundary::Error; use super::replication_filemeta_boundary::{ReplicatedTargetInfo, ReplicationStatusType, ReplicationType}; use super::runtime_boundary as runtime_sources; -use crate::error::Error; use serde::{Deserialize, Serialize}; use std::collections::{HashMap, VecDeque}; use std::sync::Arc; @@ -990,7 +990,7 @@ impl ReplicationStats { ri.op_type, ri.endpoint.clone(), ri.secure, - ri.error.as_ref().map(|e| crate::error::Error::other(e.clone())), + ri.error.as_ref().map(|e| Error::other(e.clone())), ); } ReplicationStatusType::Completed if ri.op_type.is_data_replication() => { @@ -1002,7 +1002,7 @@ impl ReplicationStats { ri.op_type, ri.endpoint.clone(), ri.secure, - ri.error.as_ref().map(|e| crate::error::Error::other(e.clone())), + ri.error.as_ref().map(|e| Error::other(e.clone())), ); } ReplicationStatusType::Failed @@ -1016,7 +1016,7 @@ impl ReplicationStats { ri.op_type, ri.endpoint.clone(), ri.secure, - ri.error.as_ref().map(|e| crate::error::Error::other(e.clone())), + ri.error.as_ref().map(|e| Error::other(e.clone())), ); } ReplicationStatusType::Replica if ri.op_type == ReplicationType::Object => { @@ -1028,7 +1028,7 @@ impl ReplicationStats { ri.op_type, String::new(), false, - ri.error.as_ref().map(|e| crate::error::Error::other(e.clone())), + ri.error.as_ref().map(|e| Error::other(e.clone())), ); } _ => {} diff --git a/crates/ecstore/src/bucket/replication/replication_storage_boundary.rs b/crates/ecstore/src/bucket/replication/replication_storage_boundary.rs index 6de5aa128..bd50e125c 100644 --- a/crates/ecstore/src/bucket/replication/replication_storage_boundary.rs +++ b/crates/ecstore/src/bucket/replication/replication_storage_boundary.rs @@ -12,10 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::error::Error; use rustfs_filemeta::FileInfo; use tokio_util::sync::CancellationToken; +use super::replication_error_boundary::Error; pub(crate) use crate::client::api_get_options::{AdvancedGetOptions, StatObjectOptions}; pub(crate) use crate::object_api::{GetObjectReader, ObjectInfo, ObjectOptions, PutObjReader}; pub(crate) use crate::storage_api_contracts::list::{ diff --git a/docs/architecture/ecstore-module-split-plan.md b/docs/architecture/ecstore-module-split-plan.md index b568b632f..cd8f2fbc4 100644 --- a/docs/architecture/ecstore-module-split-plan.md +++ b/docs/architecture/ecstore-module-split-plan.md @@ -137,6 +137,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. +- `ReplicationErrorBoundary`: ECStore error/result contracts and + replication-specific error classifiers. `crate::error` imports are + concentrated in + `crates/ecstore/src/bucket/replication/replication_error_boundary.rs`. - `ReplicationTargetStore`: bucket target listing, target client lookup, target offline checks, target config types, and target operation option types. Bucket target sys access, `BucketTargets`, and target operation types diff --git a/scripts/check_architecture_migration_rules.sh b/scripts/check_architecture_migration_rules.sh index 8f7d3ed8e..a1f2ed73e 100755 --- a/scripts/check_architecture_migration_rules.sh +++ b/scripts/check_architecture_migration_rules.sh @@ -200,6 +200,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_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" REPLICATION_EVENT_SINK_BYPASS_HITS_FILE="${TMP_DIR}/replication_event_sink_bypass_hits.txt" REPLICATION_EVENT_HOST_BYPASS_HITS_FILE="${TMP_DIR}/replication_event_host_bypass_hits.txt" REPLICATION_FILEMETA_BOUNDARY_BYPASS_HITS_FILE="${TMP_DIR}/replication_filemeta_boundary_bypass_hits.txt" @@ -2548,6 +2549,25 @@ if [[ -s "$REPLICATION_CONFIG_STORE_BYPASS_HITS_FILE" ]]; then report_failure "replication config persistence must stay behind replication config store: $(paste -sd '; ' "$REPLICATION_CONFIG_STORE_BYPASS_HITS_FILE")" fi +( + cd "$ROOT_DIR" + find crates/ecstore/src/bucket/replication -type f -name '*.rs' -print0 | + xargs -0 perl -0ne ' + while (/(?:crate::error::|use\s+crate::error\b|use\s+crate::\{[^;]*\berror\b)/sg) { + my $prefix = substr($_, 0, $-[0]); + my $line = ($prefix =~ tr/\n//) + 1; + my $match = $&; + $match =~ s/\s+/ /g; + print "$ARGV:$line:$match\n"; + } + ' | + rg -v '^crates/ecstore/src/bucket/replication/replication_error_boundary\.rs:' || true +) >"$REPLICATION_ERROR_BOUNDARY_BYPASS_HITS_FILE" + +if [[ -s "$REPLICATION_ERROR_BOUNDARY_BYPASS_HITS_FILE" ]]; then + report_failure "replication error contracts must stay behind replication error boundary: $(paste -sd '; ' "$REPLICATION_ERROR_BOUNDARY_BYPASS_HITS_FILE")" +fi + ( cd "$ROOT_DIR" find crates/ecstore/src/bucket/replication -type f -name '*.rs' -print0 |