diff --git a/internal/bucket/lifecycle/lifecycle.go b/internal/bucket/lifecycle/lifecycle.go index fdaabd962..788027a77 100644 --- a/internal/bucket/lifecycle/lifecycle.go +++ b/internal/bucket/lifecycle/lifecycle.go @@ -471,9 +471,11 @@ func (lc Lifecycle) eval(obj ObjectOpts, now time.Time) Event { if len(events) > 0 { sort.Slice(events, func(i, j int) bool { - if events[i].Due.Equal(events[j].Due) { - // Prefer Expiration over Transition for both current - // and noncurrent versions + // Prefer Expiration over Transition for both current + // and noncurrent versions when, + // - now is past the expected time to action + // - expected time to action is the same for both actions + if now.After(events[i].Due) && now.After(events[j].Due) || events[i].Due.Equal(events[j].Due) { switch events[i].Action { case DeleteAction, DeleteVersionAction: return true diff --git a/internal/bucket/lifecycle/lifecycle_test.go b/internal/bucket/lifecycle/lifecycle_test.go index 88d3c5b3a..0e8aaa79a 100644 --- a/internal/bucket/lifecycle/lifecycle_test.go +++ b/internal/bucket/lifecycle/lifecycle_test.go @@ -539,6 +539,46 @@ func TestEval(t *testing.T) { objectModTime: time.Now().UTC().Add(-15 * 24 * time.Hour), expectedAction: DeleteAction, }, + { + inputConfig: ` + + Rule 1 + Enabled + + + WARM-1 + 30 + + + 60 + + + `, + objectName: "obj-1", + objectModTime: time.Now().UTC().Add(-90 * 24 * time.Hour), + expectedAction: DeleteAction, + }, + { + inputConfig: ` + + Rule 2 + + Enabled + + 60 + + + WARM-1 + 30 + + + `, + objectName: "obj-1", + isNoncurrent: true, + objectModTime: time.Now().UTC().Add(-90 * 24 * time.Hour), + objectSuccessorModTime: time.Now().UTC().Add(-90 * 24 * time.Hour), + expectedAction: DeleteVersionAction, + }, } for _, tc := range testCases {