mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-10-28 07:50:44 +01:00
An operator changes the lifecycle rule between two shell-driven
sweeps. The second sweep must respect the NEW rule, not a cached
copy of the old one. Each runLifecycleShard invocation spawns a
fresh weed shell subprocess, so cached engine state from a previous
sweep doesn't persist — but a regression that caches rules across
PutBucketLifecycleConfiguration calls within the S3 server itself
would still surface here.
Sweep 1: rule prefix="first/", PUT + backdate firstKey, run worker
→ firstKey expires.
Update rule to prefix="second/", PUT + backdate secondKey AND a
new key under the OLD prefix ("first/post-update.txt"). Sweep 2
must expire only the second-prefix object; the post-update old-
prefix one must survive — config replacement, not merge.
80 lines
2.8 KiB
Go
80 lines
2.8 KiB
Go
// Lifecycle config update across sweeps.
|
|
package lifecycle
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/aws/aws-sdk-go-v2/aws"
|
|
"github.com/aws/aws-sdk-go-v2/service/s3"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
// TestLifecycleConfigUpdateBetweenSweeps: an operator changes the
|
|
// lifecycle rule between two shell-driven sweeps. The second sweep
|
|
// must respect the NEW rule, not a cached version of the old one.
|
|
//
|
|
// Each `runLifecycleShard` invocation spawns a fresh `weed shell`
|
|
// subprocess, so cached engine state from a previous sweep doesn't
|
|
// persist across runs. This test pins that the freshly-loaded config
|
|
// actually changes routing — under the new prefix only matching
|
|
// objects expire, even if there are still backdated objects sitting
|
|
// under the old prefix.
|
|
func TestLifecycleConfigUpdateBetweenSweeps(t *testing.T) {
|
|
c := s3Client(t)
|
|
fc, fcClose := filerClient(t)
|
|
defer fcClose()
|
|
|
|
bucket := uniqueBucket("config-update")
|
|
mustCreateBucket(t, c, bucket)
|
|
|
|
// Sweep 1: rule expires anything under "first/".
|
|
putExpirationLifecycle(t, c, bucket, "first/", 1)
|
|
|
|
const firstKey = "first/initial.txt"
|
|
putObject(t, c, bucket, firstKey, "first")
|
|
backdateMtime(t, fc, bucket, firstKey, 30)
|
|
|
|
out := runLifecycleShard(t)
|
|
t.Logf("sweep 1 output:\n%s", out)
|
|
|
|
require.Eventuallyf(t, func() bool {
|
|
_, err := c.HeadObject(context.Background(), &s3.HeadObjectInput{
|
|
Bucket: aws.String(bucket), Key: aws.String(firstKey),
|
|
})
|
|
return isS3NotFound(err)
|
|
}, 30*time.Second, 500*time.Millisecond, "sweep 1 must expire %s", firstKey)
|
|
|
|
// Update the rule to a different prefix. The old "first/" prefix
|
|
// is no longer covered by any rule; objects under it must NOT be
|
|
// expired by sweep 2 even when backdated.
|
|
putExpirationLifecycle(t, c, bucket, "second/", 1)
|
|
|
|
const secondKey = "second/new.txt"
|
|
const oldPrefixKey = "first/post-update.txt"
|
|
putObject(t, c, bucket, secondKey, "second")
|
|
putObject(t, c, bucket, oldPrefixKey, "stale rule")
|
|
backdateMtime(t, fc, bucket, secondKey, 30)
|
|
backdateMtime(t, fc, bucket, oldPrefixKey, 30)
|
|
|
|
out = runLifecycleShard(t)
|
|
t.Logf("sweep 2 output:\n%s", out)
|
|
|
|
// Sweep 2 expires the new-prefix object.
|
|
require.Eventuallyf(t, func() bool {
|
|
_, err := c.HeadObject(context.Background(), &s3.HeadObjectInput{
|
|
Bucket: aws.String(bucket), Key: aws.String(secondKey),
|
|
})
|
|
return isS3NotFound(err)
|
|
}, 30*time.Second, 500*time.Millisecond, "sweep 2 must expire %s under the new rule", secondKey)
|
|
|
|
// Sweep 2 must NOT expire the old-prefix object — the rule was
|
|
// replaced, not merged. A regression that caches old rules across
|
|
// PutBucketLifecycleConfiguration calls would fail here.
|
|
_, err := c.HeadObject(context.Background(), &s3.HeadObjectInput{
|
|
Bucket: aws.String(bucket), Key: aws.String(oldPrefixKey),
|
|
})
|
|
require.NoError(t, err, "old-prefix object must survive after rule update — config replacement, not merge")
|
|
}
|