Commit Graph

13219 Commits

Author SHA1 Message Date
Feng Ruohang 7fac646923 refactor(restore): take the restore lock from the object lock domain
Native restore chose its own single-pool lock in the object's erasure
set before the pools layer did. Now that every object write locks in
that domain through NewObjectNSLock, the restore lock is that lock.
Behavior is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Feng Ruohang <rh@vonng.com>
2026-11-05 06:50:32 +08:00
Feng Ruohang 32ab506924 fix(pools): lock single-pool object writes in the object's own erasure set
In a single pool, DELETE in every form, bulk delete, the CopyObject
destination, SelectObjectContent, batch key rotation and the upload-ID
guards of complete, abort, get-info and list-parts locked through the
first erasure set, while PUT, multipart commit, PutObjectPart, tag,
retention, legal-hold and metadata updates, restore, transition and heal
lock in the set the object hashes to. When the sets of a pool use
different hosts, as every automatic layout of 18 or more hosts does, the
two locks ask different lockers and do not exclude each other, and
concurrent read-modify-writes of xl.meta can lose acknowledged versions
and delete markers or leave an object on a minority of drives.

Route these locks through NewObjectNSLock, which returns the lock of
the object's own set in a single pool and the pools lock otherwise.
Upload guards choose the set by their object and keep the name
object/uploadID. True prefix deletion and the locks of names that are
not objects keep their domain.

Bulk delete groups its names by set and takes one set's lock at a time,
in ascending set index; a failed group fails alone. A single node, whose
sets share one local lock table, keeps its one bulk lock.

The COPY handler takes its source and destination locks before it opens
the source, sorted, and transactionally: bounded waits while holding a
lock, release in reverse order and retry with new lock instances, one
deadline. It reads the source under them, passes NoLock to every nested
call, and runs that work in the locks' context. This also removes a
deadlock of copies in opposite directions wherever both locks were in
one domain, such as a single node or several pools.

Lock names and RPCs are unchanged, but a pool is protected only when
every node runs this change: affected pools must be upgraded and rolled
back with a drained restart.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Feng Ruohang <rh@vonng.com>
2026-11-05 06:49:57 +08:00
Feng Ruohang 7f38bf6282 fix(restore): give each native restore an owner and a private upload
A native restore (POST ?restore, issue #281) marks the version ongoing,
and a background worker rehydrates it from the tier. The worker, its
upload and every status write could be reached or confused by other
callers. A public client could write into or complete the restore
upload. POST copied a whole metadata snapshot over newer protection
changes. An old or failed attempt could act on a newer one. Restore,
expiry and heal decided on whichever drive's metadata they read.

Each POST that starts a rehydration now writes an attempt ID into the
version's internal metadata, with the identity it resolved: version
ID, ETag, size and ModTime. The worker, its upload, its part writes and
its completion carry the attempt. A worker whose attempt no longer
owns the version stops without writing, completing or resetting. Every
write uses the versioning state and explicit version ID captured by
POST, so a null target stays null.

The restore upload carries an internal marker, decided over all drives.
Public multipart calls answer NoSuchUpload and listings hide it. An
unknown marker fails closed. A completion holds a commit lock from
staging its commit in the upload until the commit ends. Stale cleanup
decides on every upload past the stale age under that lock, and keeps
it until its rename ends, also past the drive deadline. Abort of a
committing upload takes the lock and removes a failed commit's
leftover only when every copy is staged and older than one hour.

POST, the worker, the reset, the ExpireRestored recheck and heal all
decide on the authoritative revision of the exact version. That is the
revision held by max(n/2+1, read quorum) drives, identified by the
version header and the normalized metadata digest. Status writes patch
only the restore fields of that revision. They write only the drives
that hold it, and put the old revision back on failure. Heal copies
restore fields only when a quorum holds the same fields.

A failed attempt resets itself with bounded in-memory retries. A later
POST takes over a stranded marker when no attempt runs. Otherwise it
answers RestoreAlreadyInProgress within a few seconds.

Older nodes do not know the marker. A new grid capability handler lets
the serving node ask every peer. POST answers 503 ServiceUnavailable
and writes nothing until all peers support native restore, and a
queued worker asks again before it creates its upload.

The single-pool lock-domain split between DELETE/CopyObject and restore
is closed by its own fix, which this change depends on.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Feng Ruohang <rh@vonng.com>
2026-11-05 06:47:38 +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 add36e7052 Integrate R4 with complete R1 safety dependency
Signed-off-by: Feng Ruohang <rh@vonng.com>
2026-11-03 22:45:14 +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 3c8699e216 ci: race-check lifecycle expiry and native restore together
Signed-off-by: Feng Ruohang <rh@vonng.com>
2026-11-03 21:20:24 +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
Feng Ruohang 69fcf50cd2 Merge pull request #275 from botdeniska/fix/ldap-sts-failure-message
Merge LDAP STS failure message compatibility for embedded console root fallback.
2026-11-03 09:01:54 +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
Feng Ruohang 6da8036eb3 Merge pull request #274 from botdeniska/fix/iam-long-record-names
Merge LDAP IAM path validation so long normalized DNs fail consistently before quorum access.
2026-11-03 08:51:40 +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
Feng Ruohang 9897589bc5 Merge pull request #273 from botdeniska/fix/rebalance-stop-without-metadata
Merge rebalance stop guard for an uninitialized rebalance state.
2026-11-03 08:38:04 +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
Feng Ruohang d4e7716085 Merge pull request #269 from botdeniska/fix/lifecycle-active-delmarker-expiration
Merge lifecycle fix: count DelMarkerExpiration as an active scanner rule.
2026-11-03 08:23:22 +08:00
Denis Babchuk a803027b09 fix(ilm): count DelMarkerExpiration as an active lifecycle rule
Signed-off-by: Denis Babchuk <babchukdenis@icloud.com>
2026-11-03 08:08:02 +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 0fc5a0b527 Merge lifecycle migration integrity follow-up (#276)
Merge maintainer follow-up for lifecycle-aware pool migration.

Preserve replication, tier cleanup, lifecycle-generation, and whole-object completion semantics on top of contributor fix #266.
2026-11-02 14:55:35 +08: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