diff --git a/crates/ecstore/src/bucket/lifecycle/bucket_lifecycle_ops.rs b/crates/ecstore/src/bucket/lifecycle/bucket_lifecycle_ops.rs index bd92a06ad..9d9ebfb55 100644 --- a/crates/ecstore/src/bucket/lifecycle/bucket_lifecycle_ops.rs +++ b/crates/ecstore/src/bucket/lifecycle/bucket_lifecycle_ops.rs @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use super::{object_lock_boundary, runtime_boundary as runtime_sources}; +use super::{metadata_boundary, object_lock_boundary, runtime_boundary as runtime_sources}; use crate::bucket::lifecycle::bucket_lifecycle_audit::{ LcAuditEvent, LcEventSrc, emit_non_transitioned_expiration_event, emit_transition_complete_event, emit_transition_failed_event, emit_transitioned_expiration_event, @@ -26,7 +26,7 @@ use crate::bucket::lifecycle::tier_delete_journal::{process_tier_delete_journal_ use crate::bucket::lifecycle::tier_free_version_recovery::{DEFAULT_FREE_VERSION_RECOVERY_LIMIT, recover_tier_free_versions}; use crate::bucket::lifecycle::tier_last_day_stats::{DailyAllTierStats, LastDayTierStats}; use crate::bucket::lifecycle::tier_sweeper::{Jentry, delete_object_from_remote_tier_idempotent}; -use crate::bucket::{metadata_sys, metadata_sys::get_lifecycle_config, versioning_sys::BucketVersioningSys}; +use crate::bucket::versioning_sys::BucketVersioningSys; use crate::client::object_api_utils::new_getobjectreader; use crate::disk::error::DiskError; use crate::disk::{DeleteOptions, Disk, DiskAPI, RUSTFS_META_MULTIPART_BUCKET, STORAGE_FORMAT_FILE}; @@ -211,7 +211,7 @@ impl LifecycleSys { } pub async fn get(&self, bucket: &str) -> Option { - match get_lifecycle_config(bucket).await { + match metadata_boundary::get_lifecycle_config(bucket).await { Ok((lc, _)) => Some(lc), Err(Error::ConfigNotFound) => None, Err(err) => { @@ -1506,7 +1506,7 @@ async fn stale_upload_lifecycle_due( let bucket = metadata.get(RUSTFS_MULTIPART_BUCKET_KEY)?; let object = metadata.get(RUSTFS_MULTIPART_OBJECT_KEY)?; - let lifecycle = match metadata_sys::get_lifecycle_config(bucket).await { + let lifecycle = match metadata_boundary::get_lifecycle_config(bucket).await { Ok((lifecycle, _)) => lifecycle, Err(_) => return None, }; @@ -1941,11 +1941,11 @@ pub async fn enqueue_immediate_expiry(oi: &ObjectInfo, src: LcEventSrc) { object_infos.push(oi.clone()); } - let lock_config = match metadata_sys::get_object_lock_config(&oi.bucket).await { + let lock_config = match metadata_boundary::get_object_lock_config(&oi.bucket).await { Ok((cfg, _)) => Some(Arc::new(cfg)), Err(_) => None, }; - let replication = match metadata_sys::get_replication_config(&oi.bucket).await { + let replication = match metadata_boundary::get_replication_config(&oi.bucket).await { Ok((cfg, _)) if !cfg.rules.is_empty() => Some(Arc::new(replication_sink::new_replication_config(cfg))), _ => None, }; @@ -2056,14 +2056,14 @@ async fn apply_existing_object_expiry(api: Arc, object: &ObjectInfo, ev } pub async fn enqueue_expiry_for_existing_objects(api: Arc, bucket: &str) -> Result<(), Error> { - let Ok((lc, _)) = metadata_sys::get_lifecycle_config(bucket).await else { + let Ok((lc, _)) = metadata_boundary::get_lifecycle_config(bucket).await else { return Ok(()); }; - let lock_retention = metadata_sys::get_object_lock_config(bucket) + let lock_retention = metadata_boundary::get_object_lock_config(bucket) .await .ok() .and_then(|(cfg, _)| cfg.rule.and_then(|rule| rule.default_retention)); - let replication_config = metadata_sys::get_replication_config(bucket).await.ok(); + let replication_config = metadata_boundary::get_replication_config(bucket).await.ok(); let mut marker = None; let mut version_marker = None; let src = LcEventSrc::Scanner; diff --git a/crates/ecstore/src/bucket/lifecycle/metadata_boundary.rs b/crates/ecstore/src/bucket/lifecycle/metadata_boundary.rs new file mode 100644 index 000000000..6867726aa --- /dev/null +++ b/crates/ecstore/src/bucket/lifecycle/metadata_boundary.rs @@ -0,0 +1,31 @@ +// 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 s3s::dto::{BucketLifecycleConfiguration, ObjectLockConfiguration, ReplicationConfiguration}; +use time::OffsetDateTime; + +use crate::bucket::metadata_sys; +use crate::error::Result; + +pub(crate) async fn get_lifecycle_config(bucket: &str) -> Result<(BucketLifecycleConfiguration, OffsetDateTime)> { + metadata_sys::get_lifecycle_config(bucket).await +} + +pub(crate) async fn get_object_lock_config(bucket: &str) -> Result<(ObjectLockConfiguration, OffsetDateTime)> { + metadata_sys::get_object_lock_config(bucket).await +} + +pub(crate) async fn get_replication_config(bucket: &str) -> Result<(ReplicationConfiguration, OffsetDateTime)> { + metadata_sys::get_replication_config(bucket).await +} diff --git a/crates/ecstore/src/bucket/lifecycle/mod.rs b/crates/ecstore/src/bucket/lifecycle/mod.rs index 35d2fc24b..ffd3f3801 100644 --- a/crates/ecstore/src/bucket/lifecycle/mod.rs +++ b/crates/ecstore/src/bucket/lifecycle/mod.rs @@ -17,6 +17,7 @@ pub mod bucket_lifecycle_ops; mod config_boundary; pub mod core; pub mod evaluator; +mod metadata_boundary; mod object_lock_boundary; pub use self::core as lifecycle; mod replication_sink; diff --git a/docs/architecture/ecstore-module-split-plan.md b/docs/architecture/ecstore-module-split-plan.md index f1fd5ade5..8a4ffdd25 100644 --- a/docs/architecture/ecstore-module-split-plan.md +++ b/docs/architecture/ecstore-module-split-plan.md @@ -57,10 +57,12 @@ Current coupling: - lifecycle workers and transition state read ECStore runtime sources for object-store handles, expiry state, transition state, tier config, deployment IDs, and local node names; -- stale multipart cleanup depends on `SetDisks` internals and bucket metadata; +- stale multipart cleanup depends on `SetDisks` internals and bucket metadata + through the lifecycle metadata boundary; - lifecycle expiry schedules bucket replication delete work directly; - lifecycle evaluation shares S3 DTOs, object metadata, object lock, replication - config, scanner metrics, notification/audit side effects, and tier services. + config reads through lifecycle-local boundaries, scanner metrics, + notification/audit side effects, and tier services. Required contracts before crate movement: @@ -68,7 +70,8 @@ Required contracts before crate movement: cleanup, and version-aware metadata operations needed by lifecycle workers. - `LifecycleMetadataStore`: lifecycle, object-lock, replication, bucket versioning, and stale multipart metadata lookups without importing ECStore - implementation modules. + implementation modules. Current lifecycle config reads are concentrated in + `crates/ecstore/src/bucket/lifecycle/metadata_boundary.rs`. - `LifecycleRuntime`: expiry state, transition state, tier config, deployment ID, local node name, queue metrics, cancellation, and worker sizing. - `LifecycleReplicationSink`: schedule lifecycle-originated replication deletes diff --git a/scripts/check_architecture_migration_rules.sh b/scripts/check_architecture_migration_rules.sh index 7ec69c0ed..ed9f6369d 100755 --- a/scripts/check_architecture_migration_rules.sh +++ b/scripts/check_architecture_migration_rules.sh @@ -132,6 +132,7 @@ STORE_API_LIFECYCLE_HELPER_DEFINITION_HITS_FILE="${TMP_DIR}/store_api_lifecycle_ STORE_API_LIFECYCLE_HELPER_OLD_CONSUMER_HITS_FILE="${TMP_DIR}/store_api_lifecycle_helper_old_consumer_hits.txt" LIFECYCLE_AUDIT_SINK_BYPASS_HITS_FILE="${TMP_DIR}/lifecycle_audit_sink_bypass_hits.txt" LIFECYCLE_CONFIG_BOUNDARY_BYPASS_HITS_FILE="${TMP_DIR}/lifecycle_config_boundary_bypass_hits.txt" +LIFECYCLE_METADATA_BOUNDARY_BYPASS_HITS_FILE="${TMP_DIR}/lifecycle_metadata_boundary_bypass_hits.txt" LIFECYCLE_TAGGING_BOUNDARY_BYPASS_HITS_FILE="${TMP_DIR}/lifecycle_tagging_boundary_bypass_hits.txt" LIFECYCLE_OBJECT_LOCK_BOUNDARY_BYPASS_HITS_FILE="${TMP_DIR}/lifecycle_object_lock_boundary_bypass_hits.txt" LIFECYCLE_REPLICATION_SINK_BYPASS_HITS_FILE="${TMP_DIR}/lifecycle_replication_sink_bypass_hits.txt" @@ -854,6 +855,18 @@ if [[ -s "$LIFECYCLE_CONFIG_BOUNDARY_BYPASS_HITS_FILE" ]]; then report_failure "lifecycle config persistence access must stay behind lifecycle config_boundary: $(paste -sd '; ' "$LIFECYCLE_CONFIG_BOUNDARY_BYPASS_HITS_FILE")" fi +( + cd "$ROOT_DIR" + rg -n --with-filename 'metadata_sys::get_(lifecycle|object_lock|replication)_config|use crate::bucket::metadata_sys::get_(lifecycle|object_lock|replication)_config' \ + crates/ecstore/src/bucket/lifecycle \ + --glob '*.rs' | + rg -v '^crates/ecstore/src/bucket/lifecycle/metadata_boundary\.rs:' || true +) >"$LIFECYCLE_METADATA_BOUNDARY_BYPASS_HITS_FILE" + +if [[ -s "$LIFECYCLE_METADATA_BOUNDARY_BYPASS_HITS_FILE" ]]; then + report_failure "lifecycle metadata config reads must stay behind lifecycle metadata_boundary: $(paste -sd '; ' "$LIFECYCLE_METADATA_BOUNDARY_BYPASS_HITS_FILE")" +fi + ( cd "$ROOT_DIR" rg -n --with-filename 'crate::bucket::tagging::decode_tags_to_map|use crate::bucket::tagging' \