Files
seaweedfs/weed/s3api/s3api_object_handlers_delete_test.go
T
Chris LuandGitHub bb0c7c779f feat(s3/lifecycle): metadata-only delete when entry TtlSec > 0 (Phase 2b) (#9390)
* refactor(s3): thread metadataOnly into delete helpers

Add a metadataOnly bool to deleteUnversionedObjectWithClient and
deleteSpecificObjectVersion. When true the helper sends IsDeleteData=
false to the filer's DeleteEntry RPC so per-chunk DeleteFile RPCs are
skipped — the volume server reclaims chunks on its own at TTL drop.
Non-lifecycle callers (DELETE handlers, batch delete) pass false to
preserve today's eager-chunk-delete behavior; only the lifecycle
handler in the next commit will pass true.

* feat(s3/lifecycle): metadata-only delete when entry TtlSec > 0

Per-write TTL stamping (PR 9377) sets Attributes.TtlSec on every
lifecycle-fitting entry. When the live entry the LifecycleDelete
handler fetched carries TtlSec > 0 the volume server is guaranteed
to reclaim chunks at TTL drop, so the filer can skip per-chunk
DeleteFile RPCs and just remove the entry record. lifecycleDispatch
now computes metadataOnly from the live entry and threads it through
the unversioned, suspended-null, and noncurrent/expired-marker delete
paths. createDeleteMarker is unaffected — it creates a marker, never
deletes chunks.
2026-05-09 18:38:38 -07:00

67 lines
2.8 KiB
Go

package s3api
import (
"testing"
"github.com/seaweedfs/seaweedfs/weed/pb/filer_pb"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestDeleteUnversionedObjectWithClient_MetadataOnlySkipsChunkDelete(t *testing.T) {
// metadataOnly=true must reach the filer as IsDeleteData=false so the
// volume server reclaims chunks via TTL instead of the filer enqueueing
// per-chunk DeleteFile RPCs.
s3a := &S3ApiServer{option: &S3ApiServerOption{BucketsPath: "/buckets"}}
client := &deleteObjectEntryTestClient{}
err := s3a.deleteUnversionedObjectWithClient(client, "b", "k", true)
require.NoError(t, err)
require.NotNil(t, client.deleteReq)
assert.Equal(t, "/buckets/b", client.deleteReq.Directory)
assert.Equal(t, "k", client.deleteReq.Name)
assert.False(t, client.deleteReq.IsDeleteData, "metadataOnly must clear IsDeleteData")
}
func TestDeleteUnversionedObjectWithClient_FullDeletePreservesIsDeleteData(t *testing.T) {
// Default behavior (metadataOnly=false): filer should still enqueue
// chunk deletions as before.
s3a := &S3ApiServer{option: &S3ApiServerOption{BucketsPath: "/buckets"}}
client := &deleteObjectEntryTestClient{}
err := s3a.deleteUnversionedObjectWithClient(client, "b", "k", false)
require.NoError(t, err)
require.NotNil(t, client.deleteReq)
assert.True(t, client.deleteReq.IsDeleteData, "default delete must keep IsDeleteData true")
}
func TestDeleteUnversionedObjectWithClient_FullPathFromBucketsRoot(t *testing.T) {
// Sanity: BucketsPath joins to <bucketsPath>/<bucket>/<object> in the
// DeleteEntryRequest so the filer can locate the entry. Object keys
// with multiple path segments should split into Directory + Name
// correctly.
s3a := &S3ApiServer{option: &S3ApiServerOption{BucketsPath: "/buckets"}}
client := &deleteObjectEntryTestClient{}
err := s3a.deleteUnversionedObjectWithClient(client, "mybucket", "a/b/c.txt", false)
require.NoError(t, err)
require.NotNil(t, client.deleteReq)
assert.Equal(t, "/buckets/mybucket/a/b", client.deleteReq.Directory)
assert.Equal(t, "c.txt", client.deleteReq.Name)
}
func TestDeleteUnversionedObjectWithClient_PropagatesEntryAttributesIrrelevant(t *testing.T) {
// The metadataOnly decision is the caller's responsibility (the
// lifecycle handler). This function is dumb plumbing — it must not
// inspect the entry itself, only translate the bool to IsDeleteData.
s3a := &S3ApiServer{option: &S3ApiServerOption{BucketsPath: "/buckets"}}
client := &deleteObjectEntryTestClient{
// A response with no error is fine; attributes on a delete are unused.
deleteResp: &filer_pb.DeleteEntryResponse{},
}
require.NoError(t, s3a.deleteUnversionedObjectWithClient(client, "b", "k", true))
require.NotNil(t, client.deleteReq)
assert.False(t, client.deleteReq.IsDeleteData)
}