Implement DELETE precondition checks for If-Match header - #12
Conversation
AWS S3 supports the If-Match conditional header on DeleteObject, but MinIO silently ignored it — the object was deleted regardless of ETag mismatch. Add precondition checks following the same two-tier pattern used by PutObject/GetObject: handler sets CheckPrecondFn, erasure layer evaluates it atomically after fetching fresh ObjectInfo. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
16e34f9 to
f48dbe7
Compare
|
Isn't the repo AGPL3 and not Apache 2? Would make more sense to require all contributions under the same license |
|
Thanks — conditional |
Vonng
left a comment
There was a problem hiding this comment.
The underlying issue is valid, but this implementation is not safe to merge in its current form. Requesting changes after the 20260903 adversarial review.
CheckPrecondFnis evaluated independently inside eacherasureObjectspool. Pools can hold copies from different times. A two-pool reproduction shows both unsafe outcomes: a request can return 412 after deleting an older matching copy, or return success after deleting the current copy while an older non-matching copy survives and becomes visible.erasureServerPools.DeleteObjecthas an outer current-delete-marker fast path before the proposed callback.If-Match: *can therefore report success without evaluating the condition.- The callback can be invoked concurrently by multiple pools while closing over one
http.ResponseWriter. - The S3 contract also includes per-object
ETagconditions inDeleteObjects; this PR does not define whether that API is supported, rejected, or still silently downgraded. - The commit predates the repository DCO policy and carries no
Signed-off-bytrailer.
Required direction: select the logical current object and evaluate the client condition exactly once in erasureServerPools.DeleteObject under its outer namespace lock, before any pool mutates; clear the callback before invoking lower pools; cover current delete markers, missing keys, wildcard and quoted ETags, two-pool divergence, read-quorum failure, versioned/null-version paths, and concurrent response safety. Batch conditions should land only with their complete independent semantics, or fail closed when supplied.
The detailed counterexamples and minimal design are recorded at https://silo.pgsty.com/blog/design/conditional-delete/ . This feature is explicitly deferred from RELEASE.2026-09-03 rather than shipping partial conditional semantics.
Community Contribution License
All community contributions in this pull request are licensed to the project maintainers
under the terms of the Apache 2 license.
By creating this pull request I represent that I have the right to license the
contributions to the project maintainers under the Apache 2 license.
Description
Add support for the
If-Matchconditional header on theDeleteObjectAPI operation, as specified by AWS S3. (fixes #10)When
If-Matchis provided, the object is deleted only if its ETag matches the specified value; otherwise a412 Precondition Failedresponse is returned. WhenIf-Matchis specified and the object does not exist, a404 NoSuchKeyerror is returned instead of the usual204 No Content.This follows the same two-tier precondition check pattern already used by
PutObjectandGetObject:DeleteObjectHandler) — reads theIf-Matchheader, setsopts.CheckPrecondFnclosure andopts.HasIfMatchflag.erasureObjects.DeleteObject) — evaluatesCheckPrecondFnatomically after fetching the latestObjectInfoviagetObjectInfoAndQuorum, before proceeding with the actual deletion. Non-NotFound errors (e.g.InsufficientReadQuorum) are propagated to prevent unverified deletes.Source changes across 4 files:
cmd/object-handlers-common.go— newcheckPreconditionsDELETE()function that checksIf-Matchfor DELETE requests.cmd/object-handlers.go—DeleteObjectHandlernow parses theIf-Matchheader and sets the precondition function. Error handling updated:PreConditionFailedreturns early;If-Matchon a non-existent object returnsNoSuchKeyerror instead of204 No Content.cmd/erasure-object.go—DeleteObjectevaluatesCheckPrecondFnaftergetObjectInfoAndQuorumand beforeEvalMetadataFn, with proper quorum-error propagation.cmd/object-api-interface.go— updatedCheckPrecondFnfield comment to be generic.Test changes across 3 files:
cmd/object-handlers-common_test.go—TestCheckPreconditionsDELETE: 6 unit test cases for the precondition function (non-DELETE ignored, matching/non-matching/wildcard/quoted ETags, no headers).cmd/erasure-object-conditional_test.go—TestDeleteObjectConditional: 3 erasure-layer integration tests (wrong ETag → fail, correct ETag → succeed, missing object → error).TestDeleteObjectConditionalWithReadQuorumFailure: 1 test verifying that conditional delete is blocked when read quorum is lost.cmd/object-handlers_test.go— 2 handler-level tests added toTestAPIDeleteObjectHandler(wrongIf-Match→ 412 and object remains;If-Matchon missing key → 404NoSuchKey).Motivation and Context
AWS S3 supports the
If-Matchconditional header onDeleteObjectoperations, returning412 Precondition Failedwhen the condition is not met. This enables safe concurrent delete workflows where callers can ensure they only delete a specific known version of an object (by ETag), preventing accidental deletion of an object that was updated between a read and a delete.MinIO already supports conditional headers for
GetObject,HeadObject,PutObject,CopyObject, and multipart operations — butDeleteObjectwas missingIf-Matchsupport entirely. There were no checks, no TODOs, and no code paths for conditional headers in the delete flow.How to test this PR?
Reproduce the bug (before the fix)
DeleteObjectrequest withIf-Match: "wrong-etag"(an ETag that does not match the object).204 No Contentinstead of being rejected with412 Precondition Failed.Minimal reproduction using .NET 10 + AWS SDK (save as
test-ifmatch.cs, run withdotnet run --file test-ifmatch.cs):Before fix:
BUG: object deleted even though ETag does not match!After fix:
OK: 412 Precondition Failed (expected behavior)Unit tests
Types of changes
Checklist:
commit-idorPR #here)