Files
rustfs/crates/ecstore/src/bucket/lifecycle
Zhengchao An 359bdc0f1f refactor(ecstore): migrate the background-services cancel token into InstanceContext (Phase 5 Slice 13) (#4586)
* refactor(ecstore): migrate the background-services cancel token into InstanceContext (Phase 5 Slice 13)

Phase 5 Slice 13 (backlog#939): move the background-services cancellation token
out of the process static into the per-instance InstanceContext, so cancelling
one instance's background workers (scanner/heal/tier/lifecycle) no longer
touches another instance.

- InstanceContext gains `background_cancel_token: OnceLock<CancellationToken>`
  with `init_background_cancel_token` (set-once) and `background_cancel_token()`
  returning an owned clone.
- global.rs `init_/get_/create_/shutdown_` helpers keep their signatures and
  route through the current instance's context; the static is removed. The
  getter now returns an owned `Option<CancellationToken>` instead of a
  `Option<&'static _>`, which is what lets the token live in the context.
- Callers adapt to the owned token: the metadata-refresh loop drops `.cloned()`;
  the lifecycle worker/loops take the owned token (a shared fallback is cloned
  when the token is somehow uninitialized). No Arc cycle is introduced —
  workers hold a token clone, not the instance context.

Single-instance behavior is unchanged: startup creates one token in the
bootstrap context the ECStore adopts, and shutdown cancels that same token.

Tests: the token is set-once and cancelling one instance's token leaves a
distinct instance uninitialized.

Verification: cargo test -p rustfs-ecstore (23 instance-context tests green),
cargo clippy -p rustfs-ecstore --all-targets (clean), make pre-commit (pass).

Refs: backlog#939 (Phase 5, Slice 13)

* test(ecstore): prove multi-instance isolation; document embedded guard retention (Phase 5 Slice 14) (#4588)

Phase 5 (backlog#939) capstone. The prior 13 slices moved every piece of
per-instance runtime state out of process globals into ECStore's
InstanceContext. This slice proves the result and records the remaining work.

- Add `two_instances_isolate_all_migrated_state`: an end-to-end acceptance test
  that constructs two independent InstanceContexts and verifies NONE of the
  migrated state is shared — erasure setup, lock manager, region, deployment id,
  the four service handles (tier/notifier/expiry/transition), the local disk
  registry, the bucket monitor, and the background cancel token. This is the
  object-graph isolation carrier working end to end.
- Document why the embedded single-instance guard (EMBEDDED_SERVER_STARTED) is
  intentionally retained: storage startup still publishes into the process-level
  bootstrap context (write-once region/endpoints/deployment id) and the single
  GLOBAL_OBJECT_API handle, so a second startup would fail-fast on that shared
  state. Lifting the guard requires threading a per-instance context through
  startup — a follow-up beyond migrating the globals. The guard is NOT removed:
  rejecting the second start is safer than the panic it would otherwise become.

Verification: cargo test -p rustfs-ecstore (acceptance test + all instance-context
tests green), cargo clippy -p rustfs-ecstore --all-targets (clean), make
pre-commit (pass).

Refs: backlog#939 (Phase 5, Slice 14). Stacked on Slice 13 (#4586).
2026-07-08 22:06:31 +00:00
..

ECStore Lifecycle Split Inventory

This directory still belongs to ECStore. It is not ready to become a standalone crate because lifecycle workers currently depend on ECStore runtime state, object IO, bucket metadata, replication scheduling, notification/audit sinks, and tier services.

Current Modules

Module Current role Split blocker
core.rs Lifecycle rule model, action evaluation, object options, and transition/expiry decisions. Uses ECStore object metadata types and compatibility DTO re-exports.
bucket_lifecycle_ops.rs Worker orchestration, expiry, transition, stale multipart cleanup, audit, replication delete scheduling, and queue state. Depends on ECStore, SetDisks, runtime globals, bucket metadata/versioning, disk internals, event notification, tier services, and lifecycle-local object-lock/replication boundaries.
evaluator.rs Bucket lifecycle evaluation wrapper. Uses lifecycle-local object-lock boundary and replication state through lifecycle-local replication sink.
rule.rs Lifecycle rule filter helpers. Uses lifecycle-local tagging boundary.
tier_delete_journal.rs Remote tier delete journal persistence and recovery. Uses lifecycle-local config persistence boundary, object IO contracts, metadata bucket paths, and ECStore.
tier_free_version_recovery.rs Free-version recovery queue and object restoration path. Depends on ECStore, object metadata, storage-api contracts, and lifecycle queue callbacks.
tier_last_day_stats.rs Tier statistics helpers. Pure data/stat logic, but still part of lifecycle worker reporting.
tier_sweeper.rs Remote tier deletion worker and transition cleanup. Depends on runtime sources, ECStore, tier journal persistence, signer-error handling, and lifecycle object options.
bucket_lifecycle_audit.rs Lifecycle audit event source labels. Must remain wired to lifecycle audit and notification sinks.

Required Contracts

Contract Responsibility Current dependency to remove
LifecycleObjectStore Object stat, delete, transition, restore, multipart cleanup, and version-aware metadata operations. Direct ECStore, SetDisks, disk, and object API access in worker paths.
LifecycleMetadataStore Lifecycle, object-lock, replication, bucket versioning, and stale multipart metadata reads. Direct bucket metadata/versioning imports; object-lock and replication are still backed by local ECStore boundaries.
LifecycleRuntime Expiry state, transition state, tier config, deployment ID, local node name, queue metrics, cancellation, and worker sizing. Direct runtime source/global access and process environment reads inside worker code.
LifecycleConfigStore Persist, read, and remove lifecycle-owned journal/config objects. Direct ECStore config persistence helper imports from worker paths.
LifecycleTagFilter Decode object tag strings for lifecycle rule matching. Direct bucket tagging helper imports from lifecycle rule paths.
LifecycleObjectLockStore Object-lock retention and deletion checks used by lifecycle evaluation and worker deletion paths. Direct object-lock module imports from lifecycle code.
LifecycleReplicationSink Lifecycle-originated delete and version-purge replication scheduling. Boundary is local, but still backed by ECStore bucket replication internals.
LifecycleAuditSink Lifecycle audit and notification event emission. Direct event notification service calls and audit-side effects from worker code.

Migration Rules

  1. Do not move bucket/lifecycle into a new crate while any worker imports crate::store::ECStore, crate::set_disk::SetDisks, runtime globals, or bucket replication internals directly.
  2. Extract pure contracts before runtime movement. The first code-bearing PR should introduce one contract boundary and keep the old ECStore call path.
  3. Preserve lifecycle queue behavior, transition/expiry state, replication delete scheduling, notification/audit events, scanner-visible metrics, and stale multipart cleanup semantics.
  4. Keep rustfs_ecstore::api::bucket::lifecycle compatibility until scanner, OBS, and test boundary files compile through replacement paths.
  5. Verify each code-bearing step with focused lifecycle tests before attempting broad gates.

First Code-Bearing Step

Start with LifecycleRuntime or LifecycleAuditSink. Both can be introduced as narrow internal contracts while keeping the current ECStore worker behavior unchanged. Do not start with a crate move.

Current first boundary: runtime_boundary.rs centralizes lifecycle access to runtime state while preserving the existing ECStore-backed implementations. config_boundary.rs centralizes lifecycle-owned config object persistence for tier delete journal recovery while preserving the existing ECStore config store. tagging_boundary.rs centralizes lifecycle tag decoding while preserving the existing ECStore bucket tagging implementation. object_lock_boundary.rs centralizes lifecycle object-lock checks while preserving the existing ECStore object-lock implementation. replication_sink.rs centralizes lifecycle-originated replication config checks and delete scheduling while preserving the existing ECStore replication worker path.