Commit Graph

6882 Commits

Author SHA1 Message Date
Feng Ruohang 2b228f678c fix(ilm): purge only the expired key's own versions in delete-all
An expiry of all versions (ExpiredObjectAllVersions, DelMarkerExpiration,
and batch expiration with the default purge) asked the pools layer for a
prefix delete. The pools layer rebuilt the options, so the request ran as
a recursive delete of the key's directory in every erasure set of every
pool. Object a/b is stored inside the directory of a, so every key under
a/ was removed with it, however new, with one audit entry and one
notification for a alone. The rebuilt options also skipped the lifecycle
recheck, so on a single pool a version written after the decision was
deleted too, and tiered versions lost their metadata without free
versions.

The expiry worker, the migration expiry and batch expiration now ask for
a purge with the in-memory ObjectOptions.DeleteAllVersions:

- DeleteObject hands it to the purge under the object lock, before the
  all-pools check. ILM decides with the unchanged lifecycleEventsAllPools,
  on a single pool too; batch needs the version its walk matched to still
  be the latest. A second read of each pool's authoritative history
  records every copy, older writes of the same ID included, and must
  equal the approval. Anything else deletes nothing.
- Before deleting, the purge reads every drive of each set that holds a
  copy. It refuses the key when a drive cannot be read, when any revision
  of an approved version there is transitioned, or, with Object Lock,
  when retention or a legal hold protects one. It then deletes on exactly
  the drive handles it checked.
- Versions are deleted by ID with the drives' version delete, which only
  removes the object directory once it is empty: oldest first, for each
  version older writes before the copy reads choose, and the latest last,
  only once every older version is gone from every pool. No marker,
  replication state or free version is written. The purge stops at the
  first error, repeats a quorum-successful delete on the drives that
  missed it while the lock is held, and stops before the latest version
  at a budget of 1000 versions or one minute, reads included.
- The ILM audit entry records how many versions were removed. A purge
  that failed after it began to delete is audited as partial and sends
  no removal event. Batch counts come from the purge; a key whose latest
  version changed is not expired, and a failed delete marker fails the
  job.

The option is rejected with conflicting options and by every layer below
the pools. Force delete and internal prefix deletes keep DeletePrefix.
Delete-all of a transitioned latest version keeps its current path, and
a key with tiered older versions is refused. On a single pool the purge
relies on the lock-domain fix for writer exclusion.

Reviewed adversarially in two design rounds and four patch rounds; the
last approved with nits, which are applied.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Feng Ruohang <rh@vonng.com>
2026-11-05 03:24:42 +08:00
Feng Ruohang 9f7b49369d fix(ilm): require an authoritative cross-pool history before lifecycle expiry
The merged all-pool check from #279 decided on the versions that a read
quorum of each set merged non-strictly. A drive that missed one metadata
update could therefore supply the revision used for the decision, and a
partially unreadable set could drop a committed version from the history.
Both let an expiry delete data the rule did not cover, including a version
under legal hold.

- objectVersions accepts a revision only when max(n/2+1, the version's
  metadata read quorum) drives hold it, identifies revisions by the header
  plus a sha256 over a canonical, length-prefixed encoding of the
  normalized metadata, filters non-accepted revisions before the strict
  merge, and fails closed on omitted versions, too many unreadable drives,
  or cancellation.
- Lock fields are read with the evaluator's key precedence. Cross-pool
  copies that disagree on hold or retention must be strictly ordered by
  valid timestamps; copies with different ModTime are separate writes and
  an older protected write refuses the object. The set safeguard re-checks
  protection and pending purges on its fresh read for checked version
  deletes.
- Duplicate copies with semantically different replication or purge state
  refuse the object.
- Batch expiry announces and audits only completed or already-absent
  removals.

Reviewed adversarially over seven rounds; the final round approved with no
remaining findings in scope. All-versions expiry through deletePrefix and
tiered delete-all dispatch are tracked separately.

Signed-off-by: Feng Ruohang <rh@vonng.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 00:16:38 +08:00
Feng Ruohang f321f7f1c7 fix(ilm): do not announce batch expiries the recheck rejected
The expiry worker now asks DeleteObjects to recheck ILM against all
pools. A rejected entry keeps its version and returns
errLifecycleNotApplicable, but the worker still wrote an ILMExpiry
audit record, an ILM trace and an s3:ObjectRemoved:Delete event for it.
Event delivery drops the error element, so consumers saw an ordinary
removal, repeated on every scan while the versions stay split across
pools.

Skip rejected entries; eligible entries of the same batch are still
reported.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Feng Ruohang <rh@vonng.com>
2026-11-04 16:37:42 +08:00
Feng Ruohang 82dd12faf8 fix(restore): keep queued restores waiting for the object lock
The POST handler marks a version ongoing before its background restore
takes the object lock, and only the restore clears that marker. Native
restore holds the lock through the tier download, so a restore of
another version of the same key waits behind it. Both restore entry
points gave up after one delete-operation timeout and returned before
any cleanup, leaving ongoing-request="true" with no owner. Every later
POST for that version was refused with 409 and nothing expired the
marker.

Retry the lock while the restore's context lives, with a dedicated
dynamic timeout so repeated waits do not lengthen delete timeouts.
Shutdown or a caller deadline still ends the wait.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Feng Ruohang <rh@vonng.com>
2026-11-04 16:37:42 +08:00
Feng Ruohang 3ddf83182f Integrate scanner rejection contract regression
Signed-off-by: Feng Ruohang <rh@vonng.com>
2026-11-03 22:56:05 +08:00
Feng Ruohang 374b9ce309 Include R1 scanner rejection regression in R4 dependency
Signed-off-by: Feng Ruohang <rh@vonng.com>
2026-11-03 22:56:05 +08:00
Feng Ruohang 0c5a309507 test(ilm): assert scanner refusal without claiming absence
Keep the specialized scanner routing test aligned with the explicit lifecycle rejection result, and verify the addressed version and payload remain readable. This distinguishes refused expiry from an idempotent missing version.

Signed-off-by: Feng Ruohang <rh@vonng.com>
2026-11-03 22:56:05 +08:00
Feng Ruohang f83d02831c Integrate complete R3 encoding and safe-read repairs
Signed-off-by: Feng Ruohang <rh@vonng.com>
2026-11-03 22:46:04 +08:00
Feng Ruohang 496eab4b76 fix(restore): preserve safe read context through nested lookups
Carry restore context through POST status reads, metadata copy routing, null-version Object Lock checks and multipart completion. A failed read must preserve the target and must not enqueue its remote object for garbage collection. Cover initial/status POST failures and metadata loss during single and multipart rehydration on real disks.

Signed-off-by: Feng Ruohang <rh@vonng.com>
2026-11-03 22:46:03 +08:00
Feng Ruohang ea483e453e Integrate R2 restore cleanup boundary repairs
Signed-off-by: Feng Ruohang <rh@vonng.com>
2026-11-03 22:45:14 +08:00
Feng Ruohang ce60cc0c84 Integrate R1 adversarial review repairs
Signed-off-by: Feng Ruohang <rh@vonng.com>
2026-11-03 22:45:14 +08:00
Feng Ruohang 997846f413 Merge R1 lifecycle safety repair into R4
Signed-off-by: Feng Ruohang <rh@vonng.com>
2026-11-03 22:42:42 +08:00
Feng Ruohang 7c9b6386c6 Merge R2 restore safety repair into R3
Signed-off-by: Feng Ruohang <rh@vonng.com>
2026-11-03 22:40:08 +08:00
Feng Ruohang ba0b59d0b2 fix(ilm): preserve duplicate metadata and reject unapplied purges
Merge lock and tag revisions for duplicate versions before all-pool
lifecycle evaluation using the existing metadata reconciliation rules.

Distinguish ordinary lifecycle refusal from object absence and clear
rejected batch replication state so a late policy withdrawal cannot
schedule a purge. Preserve restored-copy cleanup and real absence.

Cover single and batch protections, legitimate metadata removals, and
actual replication queue and HTTP target behavior.

Signed-off-by: Feng Ruohang <rh@vonng.com>
2026-11-03 22:38:21 +08:00
Feng Ruohang d9837fb319 fix(restore): retain legacy part-size compatibility
Signed-off-by: Feng Ruohang <rh@vonng.com>
2026-11-03 22:32:55 +08:00
Feng Ruohang 8579466557 fix(restore): preserve versions across cleanup and failed reads
Reject replacement markers and legacy objects before restored-copy cleanup
can mutate their type or data. Prevent restore lookups and failure cleanup
from purging dangling versions, preserve Transition on metadata rereads,
and return without writing when the initial restore read fails.

Add real-disk regressions for stale null tasks, minority metadata, initial
and cleanup read failures, and stored marker/legacy types. Keep ordinary
dangling cleanup and marker purge as positive controls.

Signed-off-by: Feng Ruohang <rh@vonng.com>
2026-11-03 22:32:33 +08:00
Feng Ruohang 4f72d0ea94 fix(restore): preserve encoded object identity and part layout
Signed-off-by: Feng Ruohang <rh@vonng.com>
2026-11-03 22:16:47 +08:00
Feng Ruohang 3c4980eb80 Integrate multipart error-case test lint correction
Signed-off-by: Feng Ruohang <rh@vonng.com>
2026-11-03 21:40:59 +08:00
Feng Ruohang 50a234d6c3 test(restore): clarify invalid-upload error cases
Signed-off-by: Feng Ruohang <rh@vonng.com>
2026-11-03 21:40:59 +08:00
Feng Ruohang a02ce222f0 Integrate complete R3 input-error regression fix
Signed-off-by: Feng Ruohang <rh@vonng.com>
2026-11-03 21:39:36 +08:00
Feng Ruohang 8ae19de7a8 fix(restore): avoid duplicate completion locks for empty upload IDs
Keep the storage layer error precedence for missing buckets and uploads while retaining every real upload lock. Cover the alias with short deadlines and race-check the original complete-multipart suite in CI.

Signed-off-by: Feng Ruohang <rh@vonng.com>
2026-11-03 21:39:36 +08:00
Feng Ruohang 7a4f0b5e69 Integrate native restore test lint correction
Signed-off-by: Feng Ruohang <rh@vonng.com>
2026-11-03 21:21:46 +08:00
Feng Ruohang 5ac27f7533 test(restore): follow context-first helper convention
Signed-off-by: Feng Ruohang <rh@vonng.com>
2026-11-03 21:21:46 +08:00
Feng Ruohang fa42a09826 fix(restore): rehydrate the selected version under its object lock
Preserve handler version context, resolve the requested version's pool, and
return on the initial read failure without writing metadata. Propagate owned
NoLock to single-part writes, multipart completion and failure cleanup in
the same patch. Preserve the original modification time on cleanup.

Route the restore status copy by version and allow internal multipart
rehydration only for a matching completed transition. Single-pool restores
own the metadata snapshot through download and cleanup. Keep public
completion in object-before-upload order, using the target set's object
lock for single-pool calls, so concurrent completion cannot invert locks.

Cover real signed POSTs, null/UUID and cross-pool identity, download failures,
multipart overwrite guards, Object Lock updates and concurrent completion.
This package depends on the restored-copy cleanup safety delivered in R2.

Signed-off-by: Feng Ruohang <rh@vonng.com>
2026-11-03 21:20:24 +08:00
Feng Ruohang fcbf2c7d49 Integrate complete native restore repair after R2 cleanup safety
Signed-off-by: Feng Ruohang <rh@vonng.com>
2026-11-03 21:20:24 +08:00
Feng Ruohang 18ba0ef32a Merge R4 version-specific read expiry rechecks after R1
Signed-off-by: Feng Ruohang <rh@vonng.com>
2026-11-03 21:11:41 +08:00
Feng Ruohang 65c59dd860 fix(ilm): recheck read expiry for the resolved version
Recheck only local ordinary-delete candidates that need an all-pool lifecycle decision. Match UUID/null identity to the version actually read and queue its global metadata and event; retain a successful read on recheck failure, absence, or refusal.

Keep GET reader cleanup, authorization, proxy reads, single-pool reads, restored-copy cleanup, and expiry response headers on their existing paths. Cover signed GET/HEAD, queue contents, metadata faults, missing matches, no-scan boundaries, and reader-lock release.

Signed-off-by: Feng Ruohang <rh@vonng.com>
2026-11-03 21:07:55 +08:00
Feng Ruohang e7103d77cb fix(ilm): constrain restored-copy cleanup to verified tiered versions
Signed-off-by: Feng Ruohang <rh@vonng.com>
2026-11-03 20:55:22 +08:00
Feng Ruohang 167b95dfd8 Integrate R2 restored-copy cleanup safety with R1
Signed-off-by: Feng Ruohang <rh@vonng.com>
2026-11-03 20:55:22 +08:00
Feng Ruohang 3a1e132f16 fix(ilm): preserve all-pool approval for tiered batch expiry
Signed-off-by: Feng Ruohang <rh@vonng.com>
2026-11-03 20:40:04 +08:00
Feng Ruohang 30588eb830 Merge PR #282 restore-copy version targeting for R2
Signed-off-by: Feng Ruohang <rh@vonng.com>
2026-11-03 20:37:15 +08:00
Feng Ruohang 393a84076f Merge commit '8c1473a618f89fda3b89462c69c2a3c1a320d4c6' into codex/silo-lifecycle-r1-r4
Signed-off-by: Feng Ruohang <rh@vonng.com>
2026-11-03 20:36:07 +08:00
Denis Babchuk a21f420de3 fix(sts): keep "not found" in the LDAP auth failure so the console can log in root
Signed-off-by: Denis Babchuk <babchukdenis@icloud.com>
2026-11-03 08:52:08 +08:00
Denis Babchuk dcad2ae3e7 fix(iam): decide unstorable IAM record names before asking the drives
Signed-off-by: Denis Babchuk <babchukdenis@icloud.com>
2026-11-03 08:38:24 +08:00
Denis Babchuk 525ab5fa40 fix(pools): do not panic when a rebalance stop finds no rebalance metadata
Signed-off-by: Denis Babchuk <babchukdenis@icloud.com>
2026-11-03 08:23:59 +08:00
Denis Babchuk 001078e9ca fix(ilm): expire a restored copy by the version it belongs to
Signed-off-by: Denis Babchuk <babchukdenis@icloud.com>
2026-11-02 21:41:37 +03:00
Denis Babchuk 8c1473a618 fix(pools): move versions whose ILM expiry does not hold with the versions in other pools
Signed-off-by: Denis Babchuk <babchukdenis@icloud.com>
2026-11-02 19:59:13 +03:00
Denis Babchuk 347d9763fd fix(ilm): recheck expiries decided in one pool against the versions of the object in all pools
Signed-off-by: Denis Babchuk <babchukdenis@icloud.com>
2026-11-02 19:58:46 +03:00
Feng Ruohang a47bacbc0f test: satisfy lint for lifecycle migration follow-up
Signed-off-by: Feng Ruohang <rh@vonng.com>
2026-11-02 14:37:04 +08:00
Feng Ruohang 0a546c7a76 fix(pools): preserve ILM cleanup during migration
Signed-off-by: Feng Ruohang <rh@vonng.com>
2026-11-02 14:20:17 +08:00
Feng Ruohang 4cc3bc1de6 Merge remote-tracking branch 'origin/main' into HEAD
Signed-off-by: Feng Ruohang <rh@vonng.com>

# Conflicts:
#	cmd/erasure-server-pool-decom.go
2026-11-02 12:29:08 +08:00
Feng Ruohang 0cfca29cb2 Merge pull request #264 from botdeniska/fix/free-versions-not-versions
fix(ilm): stop taking the free versions of deleted tiered versions for versions
2026-11-01 14:44:22 +08:00
Feng Ruohang a7310d59fe Fix cross-parity version listing after pool removal (#267)
Availability-optimized writes can leave a readable null version across separate EC groups when a disk is unavailable. Recount ordinary null versions for all non-strict reads and cover version listing and complete pool removal with end-to-end tests.

Signed-off-by: Feng Ruohang <rh@vonng.com>
2026-11-01 14:08:09 +08:00
Denis Babchuk a995921e57 fix(pools): move ILM-expired current versions during rebalance and remove moved copies
Signed-off-by: Denis Babchuk <babchukdenis@icloud.com>
2026-10-29 22:43:26 +03:00
Denis Babchuk 19910230aa fix(pools): move ILM-expired current versions during decommission
Signed-off-by: Denis Babchuk <babchukdenis@icloud.com>
2026-10-29 22:42:17 +03:00
Denis Babchuk 7ce7fc8d1e fix(pools): do not list or move free versions as delete markers
Signed-off-by: Denis Babchuk <babchukdenis@icloud.com>
2026-10-29 22:32:44 +03:00
Denis Babchuk fb1a619ee1 fix(listing): skip free versions when looking for a latest delete marker
Signed-off-by: Denis Babchuk <babchukdenis@icloud.com>
2026-10-29 22:08:34 +03:00
Denis Babchuk 3f13c26eed fix(ilm): do not let a free version make the latest version noncurrent
Signed-off-by: Denis Babchuk <babchukdenis@icloud.com>
2026-10-29 22:07:56 +03:00
Feng Ruohang dcc2f20ba8 Preserve maintenance timeout behavior and prepare SILO 20261026
Signed-off-by: Feng Ruohang <rh@vonng.com>
2026-10-26 22:43:51 +08:00
Feng Ruohang 39278e068e fix: isolate storage mutation guards by exact resource
Replace process-wide hash-slot mutexes with reference-counted locks keyed
by drive and path. Retain entries before waiting, acquire unique paths in
order, and reclaim entries only after all holders and waiters finish.

Add real storage API regressions for unrelated colliding objects and drives,
plus coverage for cancellation, multi-path order and waiter lifetimes.

Signed-off-by: Feng Ruohang <rh@vonng.com>
2026-10-26 21:11:49 +08:00