Files
rustfs/crates/ecstore/src/bucket/replication/replication_object_bridge.rs
T
Zhengchao An 6cf9cf7bb5 chore(ecstore): drop the bucket dead_code blanket (#6147)
* chore(ecstore): drop the bucket dead_code blanket

The last blanket of the backlog#1823 burn-down, and the largest: 71 items across lifecycle, replication, metadata, quota, object lock and bucket utils. Four are deleted.

Deleted, all trivial:

- check_valid_object_name and check_valid_object_name_prefix, a pair that only calls into each other with no external caller. Worth stating plainly so nobody reads this as a validation gap: object names are validated through check_object_name_for_length_and_slash, which is live; this pair is a second, unwired entry point.
- DEFAULT_HEALTH_CHECK_RELOAD_DURATION, a lone unused constant.
- The LifecycleReplicationConfig alias, which orphaned a re-export in replication/mod.rs that goes with it.

Everything else is kept, in four groups, because the blanket here was hiding structure rather than rot:

Windows platform gating. WINDOWS_RESERVED_NAMES, the two reason constants and object_name_has_windows_incompatible_segment are called from inside the #[cfg(target_os = "windows")] block in check_object_name_for_length_and_slash (utils.rs:228-255), so they only read as dead on non-Windows hosts. As with the Linux gating in the disk root, this cannot be adjudicated locally: cargo check for both x86_64-pc-windows-msvc and x86_64-unknown-linux-gnu fails in the aws-lc-sys build script for want of a cross C toolchain. CI covers both.

Declared boundary surface. The *_boundary.rs and *_bridge.rs files carry the replication split plan's contracts, which scripts/check_architecture_migration_rules.sh pins through the EcstoreReplicationBoundaryImports section of the split-plan doc. Their unused items are declarations, not leftovers.

test-util seams. ConfigWriteLockProbe with install/wait_until_attempted follows the same pattern as the barriers in the services and set_disk roots.

MinIO-parity tier/lifecycle entry points that this port never wired: apply_lifecycle_action, get_transitioned_object_reader, recover_tier_free_versions, delete_object_from_remote_tier, abort_tier_delete_journal_entry and the replication pool's worker-management surface. These are complete, substantial machinery with no caller — the same shape as data_usage's local_snapshot feature. Removing them is a product decision, so they are made explicit here rather than deleted.

Verification, four lanes warning-free: default, --tests, --features rio-v2 --tests, --features test-util --tests. cargo nextest run -p rustfs-ecstore 4096 passed; clippy --lib --tests -D warnings clean; make pre-commit exit 0. Note that clippy is what caught the orphaned re-export above: cargo check and pre-commit both treat unused_imports as a warning.

Ref rustfs/backlog#1823 (step 2, final root).

* chore(ecstore): correct inaccurate dead_code reasons in the bucket root

Six items were labelled 'asserted by this file's tests' or as MinIO-parity
entry points while having no caller at all - free get_bucket_acl_config and
created_at only reach their own live methods (production goes through
created_at_in), BucketVersioningSys::get_in, utils::serialize_content and
ServiceType have no reference anywhere, and with_transition_queue_env_async
is an unused test fixture, not a tier entry point. Name what each one is so
the next reader does not assume coverage that is not there.

Ref rustfs/backlog#1823.
2026-08-16 21:39:04 +08:00

204 lines
7.5 KiB
Rust

// 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.
use std::{collections::HashMap, sync::Arc};
use super::replication_error_boundary::Result;
use super::replication_filemeta_boundary::{ReplicateDecision, ReplicatedTargetInfo, ReplicationStatusType, ReplicationType};
use super::replication_metadata_boundary::ReplicationInstanceContext;
use super::replication_object_config::{
DeleteReplicationConfigSnapshot, check_replicate_delete, check_replicate_delete_strict, check_replicate_delete_with_snapshot,
get_must_replicate_options, load_delete_replication_config_in, load_delete_request_config_in, must_replicate,
};
use super::replication_object_decision_boundary::MustReplicateOptions;
use super::replication_pool::{schedule_replication, schedule_replication_delete};
use super::replication_queue_boundary::DeletedObjectReplicationInfo;
use super::replication_storage_boundary::{
DeletedObject, ObjectInfo, ObjectOptions, ObjectToDelete, ReplicationObjectStore, ReplicationStorage,
deleted_object_for_replication,
};
pub struct ReplicationObjectBridge;
impl ReplicationObjectBridge {
pub fn must_replicate_options(
user_defined: &HashMap<String, String>,
user_tags: String,
status: ReplicationStatusType,
op_type: ReplicationType,
opts: ObjectOptions,
) -> MustReplicateOptions {
get_must_replicate_options(user_defined, user_tags, status, op_type, opts)
}
pub async fn must_replicate(bucket: &str, object: &str, options: MustReplicateOptions) -> ReplicateDecision {
must_replicate(bucket, object, options).await
}
pub async fn check_delete(
bucket: &str,
object: &ObjectToDelete,
source: &ObjectInfo,
opts: &ObjectOptions,
get_error: Option<String>,
) -> ReplicateDecision {
check_replicate_delete(bucket, object, source, opts, get_error).await
}
pub async fn check_delete_strict(
bucket: &str,
object: &ObjectToDelete,
source: &ObjectInfo,
opts: &ObjectOptions,
get_error: Option<String>,
) -> Result<ReplicateDecision> {
check_replicate_delete_strict(bucket, object, source, opts, get_error).await
}
pub async fn delete_request_config(api: &ReplicationObjectStore, bucket: &str) -> Result<DeleteReplicationConfigSnapshot> {
load_delete_request_config_in(&api.ctx, bucket).await
}
pub(crate) async fn delete_request_config_in(
ctx: &ReplicationInstanceContext,
bucket: &str,
) -> Result<DeleteReplicationConfigSnapshot> {
load_delete_request_config_in(ctx, bucket).await
}
#[allow(
dead_code,
reason = "declared boundary surface for the ECStore replication split plan; no caller in this port (backlog#1823)"
)]
pub(crate) async fn delete_config_snapshot_in(
ctx: &ReplicationInstanceContext,
bucket: &str,
opts: &ObjectOptions,
) -> Result<DeleteReplicationConfigSnapshot> {
load_delete_replication_config_in(ctx, bucket, opts).await
}
pub fn has_active_delete_rule(snapshot: &DeleteReplicationConfigSnapshot, object: &str) -> bool {
snapshot.has_active_rule(object)
}
pub fn force_delete_target_set(
snapshot: &DeleteReplicationConfigSnapshot,
prefix: &str,
) -> Option<(Vec<String>, time::OffsetDateTime)> {
snapshot.force_delete_target_set(prefix)
}
pub fn check_delete_with_snapshot(
object: &ObjectToDelete,
source: &ObjectInfo,
opts: &ObjectOptions,
source_error: bool,
snapshot: &DeleteReplicationConfigSnapshot,
) -> ReplicateDecision {
check_replicate_delete_with_snapshot(object, source, opts, source_error, snapshot)
}
pub async fn schedule_object<S: ReplicationStorage>(
object: ObjectInfo,
storage: Arc<S>,
decision: ReplicateDecision,
op_type: ReplicationType,
) {
schedule_replication(object, storage, decision, op_type).await;
}
pub async fn schedule_delete(delete_object: DeletedObjectReplicationInfo) {
schedule_replication_delete(delete_object).await;
}
pub async fn schedule_deletes(delete_objects: &[DeletedObjectReplicationInfo]) {
if let Some(pool) = super::runtime_boundary::replication_pool() {
let _ = pool.queue_replica_delete_batch(delete_objects).await;
}
if let Some(stats) = super::runtime_boundary::replication_stats() {
for delete_object in delete_objects {
if let Some(rs) = &delete_object.delete_object.replication_state {
for k in rs.targets.keys() {
let ri = ReplicatedTargetInfo {
arn: k.clone(),
size: 0,
duration: std::time::Duration::default(),
op_type: ReplicationType::Delete,
..Default::default()
};
stats
.update(&delete_object.bucket, &ri, ReplicationStatusType::Pending, ReplicationStatusType::Empty)
.await;
}
}
}
}
}
pub async fn schedule_storage_delete(delete_object: DeletedObject, bucket: String, event_type: String) {
Self::schedule_delete(DeletedObjectReplicationInfo {
delete_object: deleted_object_for_replication(delete_object),
bucket,
event_type,
..Default::default()
})
.await;
}
pub async fn schedule_storage_deletes(delete_objects: Vec<DeletedObject>, bucket: String, event_type: String) {
let delete_objects = delete_objects
.into_iter()
.map(|delete_object| DeletedObjectReplicationInfo {
delete_object: deleted_object_for_replication(delete_object),
bucket: bucket.clone(),
event_type: event_type.clone(),
..Default::default()
})
.collect::<Vec<_>>();
Self::schedule_deletes(&delete_objects).await;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn object_bridge_builds_operation_specific_options() {
let user_defined = HashMap::new();
let metadata = ReplicationObjectBridge::must_replicate_options(
&user_defined,
String::new(),
ReplicationStatusType::Empty,
ReplicationType::Metadata,
ObjectOptions::default(),
);
assert!(metadata.is_metadata_replication());
assert!(!metadata.is_existing_object_replication());
let existing = ReplicationObjectBridge::must_replicate_options(
&user_defined,
String::new(),
ReplicationStatusType::Empty,
ReplicationType::ExistingObject,
ObjectOptions::default(),
);
assert!(existing.is_existing_object_replication());
assert!(!existing.is_metadata_replication());
}
}