mirror of
https://github.com/minio/minio.git
synced 2025-04-24 04:10:43 -04:00
fix: batch expiry job doesn't report delete marker in batch-status (#21183)
This commit is contained in:
parent
0379d6a37f
commit
864f80e226
@ -424,12 +424,12 @@ func batchObjsForDelete(ctx context.Context, r *BatchJobExpire, ri *batchJobInfo
|
|||||||
go func(toExpire []expireObjInfo) {
|
go func(toExpire []expireObjInfo) {
|
||||||
defer wk.Give()
|
defer wk.Give()
|
||||||
|
|
||||||
toExpireAll := make([]ObjectInfo, 0, len(toExpire))
|
toExpireAll := make([]expireObjInfo, 0, len(toExpire))
|
||||||
toDel := make([]ObjectToDelete, 0, len(toExpire))
|
toDel := make([]ObjectToDelete, 0, len(toExpire))
|
||||||
oiCache := newObjInfoCache()
|
oiCache := newObjInfoCache()
|
||||||
for _, exp := range toExpire {
|
for _, exp := range toExpire {
|
||||||
if exp.ExpireAll {
|
if exp.ExpireAll {
|
||||||
toExpireAll = append(toExpireAll, exp.ObjectInfo)
|
toExpireAll = append(toExpireAll, exp)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// Cache ObjectInfo value via pointers for
|
// Cache ObjectInfo value via pointers for
|
||||||
@ -527,7 +527,8 @@ func batchObjsForDelete(ctx context.Context, r *BatchJobExpire, ri *batchJobInfo
|
|||||||
|
|
||||||
type expireObjInfo struct {
|
type expireObjInfo struct {
|
||||||
ObjectInfo
|
ObjectInfo
|
||||||
ExpireAll bool
|
ExpireAll bool
|
||||||
|
DeleteMarkerCount int64
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start the batch expiration job, resumes if there was a pending job via "job.ID"
|
// Start the batch expiration job, resumes if there was a pending job via "job.ID"
|
||||||
@ -624,80 +625,115 @@ func (r *BatchJobExpire) Start(ctx context.Context, api ObjectLayer, job BatchJo
|
|||||||
matchedFilter BatchJobExpireFilter
|
matchedFilter BatchJobExpireFilter
|
||||||
versionsCount int
|
versionsCount int
|
||||||
toDel []expireObjInfo
|
toDel []expireObjInfo
|
||||||
|
failed bool
|
||||||
|
done bool
|
||||||
)
|
)
|
||||||
failed := false
|
deleteMarkerCountMap := map[string]int64{}
|
||||||
for result := range results {
|
pushToExpire := func() {
|
||||||
if result.Err != nil {
|
// set preObject deleteMarkerCount
|
||||||
failed = true
|
if len(toDel) > 0 {
|
||||||
batchLogIf(ctx, result.Err)
|
lastDelIndex := len(toDel) - 1
|
||||||
continue
|
lastDel := toDel[lastDelIndex]
|
||||||
|
if lastDel.ExpireAll {
|
||||||
|
toDel[lastDelIndex].DeleteMarkerCount = deleteMarkerCountMap[lastDel.Name]
|
||||||
|
// delete the key
|
||||||
|
delete(deleteMarkerCountMap, lastDel.Name)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
// send down filtered entries to be deleted using
|
||||||
// Apply filter to find the matching rule to apply expiry
|
// DeleteObjects method
|
||||||
// actions accordingly.
|
if len(toDel) > 10 { // batch up to 10 objects/versions to be expired simultaneously.
|
||||||
// nolint:gocritic
|
xfer := make([]expireObjInfo, len(toDel))
|
||||||
if result.Item.IsLatest {
|
copy(xfer, toDel)
|
||||||
// send down filtered entries to be deleted using
|
select {
|
||||||
// DeleteObjects method
|
case expireCh <- xfer:
|
||||||
if len(toDel) > 10 { // batch up to 10 objects/versions to be expired simultaneously.
|
toDel = toDel[:0] // resetting toDel
|
||||||
xfer := make([]expireObjInfo, len(toDel))
|
case <-ctx.Done():
|
||||||
copy(xfer, toDel)
|
done = true
|
||||||
|
|
||||||
var done bool
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
done = true
|
|
||||||
case expireCh <- xfer:
|
|
||||||
toDel = toDel[:0] // resetting toDel
|
|
||||||
}
|
|
||||||
if done {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
var match BatchJobExpireFilter
|
|
||||||
var found bool
|
|
||||||
for _, rule := range r.Rules {
|
|
||||||
if rule.Matches(result.Item, now) {
|
|
||||||
match = rule
|
|
||||||
found = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !found {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
prevObj = result.Item
|
|
||||||
matchedFilter = match
|
|
||||||
versionsCount = 1
|
|
||||||
// Include the latest version
|
|
||||||
if matchedFilter.Purge.RetainVersions == 0 {
|
|
||||||
toDel = append(toDel, expireObjInfo{
|
|
||||||
ObjectInfo: result.Item,
|
|
||||||
ExpireAll: true,
|
|
||||||
})
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
} else if prevObj.Name == result.Item.Name {
|
|
||||||
if matchedFilter.Purge.RetainVersions == 0 {
|
|
||||||
continue // including latest version in toDel suffices, skipping other versions
|
|
||||||
}
|
|
||||||
versionsCount++
|
|
||||||
} else {
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if versionsCount <= matchedFilter.Purge.RetainVersions {
|
|
||||||
continue // retain versions
|
|
||||||
}
|
|
||||||
toDel = append(toDel, expireObjInfo{
|
|
||||||
ObjectInfo: result.Item,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case result, ok := <-results:
|
||||||
|
if !ok {
|
||||||
|
done = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if result.Err != nil {
|
||||||
|
failed = true
|
||||||
|
batchLogIf(ctx, result.Err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if result.Item.DeleteMarker {
|
||||||
|
deleteMarkerCountMap[result.Item.Name]++
|
||||||
|
}
|
||||||
|
// Apply filter to find the matching rule to apply expiry
|
||||||
|
// actions accordingly.
|
||||||
|
// nolint:gocritic
|
||||||
|
if result.Item.IsLatest {
|
||||||
|
var match BatchJobExpireFilter
|
||||||
|
var found bool
|
||||||
|
for _, rule := range r.Rules {
|
||||||
|
if rule.Matches(result.Item, now) {
|
||||||
|
match = rule
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if prevObj.Name != result.Item.Name {
|
||||||
|
// switch the object
|
||||||
|
pushToExpire()
|
||||||
|
}
|
||||||
|
|
||||||
|
prevObj = result.Item
|
||||||
|
matchedFilter = match
|
||||||
|
versionsCount = 1
|
||||||
|
// Include the latest version
|
||||||
|
if matchedFilter.Purge.RetainVersions == 0 {
|
||||||
|
toDel = append(toDel, expireObjInfo{
|
||||||
|
ObjectInfo: result.Item,
|
||||||
|
ExpireAll: true,
|
||||||
|
})
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
} else if prevObj.Name == result.Item.Name {
|
||||||
|
if matchedFilter.Purge.RetainVersions == 0 {
|
||||||
|
continue // including latest version in toDel suffices, skipping other versions
|
||||||
|
}
|
||||||
|
versionsCount++
|
||||||
|
} else {
|
||||||
|
// switch the object
|
||||||
|
pushToExpire()
|
||||||
|
// a file switched with no LatestVersion, logging it
|
||||||
|
batchLogIf(ctx, fmt.Errorf("skipping object %s, no latest version found", result.Item.Name))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if versionsCount <= matchedFilter.Purge.RetainVersions {
|
||||||
|
continue // retain versions
|
||||||
|
}
|
||||||
|
toDel = append(toDel, expireObjInfo{
|
||||||
|
ObjectInfo: result.Item,
|
||||||
|
})
|
||||||
|
pushToExpire()
|
||||||
|
case <-ctx.Done():
|
||||||
|
done = true
|
||||||
|
}
|
||||||
|
if done {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if context.Cause(ctx) != nil {
|
if context.Cause(ctx) != nil {
|
||||||
xioutil.SafeClose(expireCh)
|
xioutil.SafeClose(expireCh)
|
||||||
return context.Cause(ctx)
|
return context.Cause(ctx)
|
||||||
}
|
}
|
||||||
|
pushToExpire()
|
||||||
// Send any remaining objects downstream
|
// Send any remaining objects downstream
|
||||||
if len(toDel) > 0 {
|
if len(toDel) > 0 {
|
||||||
select {
|
select {
|
||||||
|
@ -881,21 +881,23 @@ func (ri *batchJobInfo) clone() *batchJobInfo {
|
|||||||
defer ri.mu.RUnlock()
|
defer ri.mu.RUnlock()
|
||||||
|
|
||||||
return &batchJobInfo{
|
return &batchJobInfo{
|
||||||
Version: ri.Version,
|
Version: ri.Version,
|
||||||
JobID: ri.JobID,
|
JobID: ri.JobID,
|
||||||
JobType: ri.JobType,
|
JobType: ri.JobType,
|
||||||
RetryAttempts: ri.RetryAttempts,
|
RetryAttempts: ri.RetryAttempts,
|
||||||
Complete: ri.Complete,
|
Complete: ri.Complete,
|
||||||
Failed: ri.Failed,
|
Failed: ri.Failed,
|
||||||
StartTime: ri.StartTime,
|
StartTime: ri.StartTime,
|
||||||
LastUpdate: ri.LastUpdate,
|
LastUpdate: ri.LastUpdate,
|
||||||
Bucket: ri.Bucket,
|
Bucket: ri.Bucket,
|
||||||
Object: ri.Object,
|
Object: ri.Object,
|
||||||
Objects: ri.Objects,
|
Objects: ri.Objects,
|
||||||
ObjectsFailed: ri.ObjectsFailed,
|
ObjectsFailed: ri.ObjectsFailed,
|
||||||
BytesTransferred: ri.BytesTransferred,
|
DeleteMarkers: ri.DeleteMarkers,
|
||||||
BytesFailed: ri.BytesFailed,
|
DeleteMarkersFailed: ri.DeleteMarkersFailed,
|
||||||
Attempts: ri.Attempts,
|
BytesTransferred: ri.BytesTransferred,
|
||||||
|
BytesFailed: ri.BytesFailed,
|
||||||
|
Attempts: ri.Attempts,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -994,11 +996,13 @@ func (ri *batchJobInfo) updateAfter(ctx context.Context, api ObjectLayer, durati
|
|||||||
// Note: to be used only with batch jobs that affect multiple versions through
|
// Note: to be used only with batch jobs that affect multiple versions through
|
||||||
// a single action. e.g batch-expire has an option to expire all versions of an
|
// a single action. e.g batch-expire has an option to expire all versions of an
|
||||||
// object which matches the given filters.
|
// object which matches the given filters.
|
||||||
func (ri *batchJobInfo) trackMultipleObjectVersions(info ObjectInfo, success bool) {
|
func (ri *batchJobInfo) trackMultipleObjectVersions(info expireObjInfo, success bool) {
|
||||||
if success {
|
if success {
|
||||||
ri.Objects += int64(info.NumVersions)
|
ri.Objects += int64(info.NumVersions) - info.DeleteMarkerCount
|
||||||
|
ri.DeleteMarkers += info.DeleteMarkerCount
|
||||||
} else {
|
} else {
|
||||||
ri.ObjectsFailed += int64(info.NumVersions)
|
ri.ObjectsFailed += int64(info.NumVersions) - info.DeleteMarkerCount
|
||||||
|
ri.DeleteMarkersFailed += info.DeleteMarkerCount
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2134,12 +2138,14 @@ func (ri *batchJobInfo) metric() madmin.JobMetric {
|
|||||||
switch ri.JobType {
|
switch ri.JobType {
|
||||||
case string(madmin.BatchJobReplicate):
|
case string(madmin.BatchJobReplicate):
|
||||||
m.Replicate = &madmin.ReplicateInfo{
|
m.Replicate = &madmin.ReplicateInfo{
|
||||||
Bucket: ri.Bucket,
|
Bucket: ri.Bucket,
|
||||||
Object: ri.Object,
|
Object: ri.Object,
|
||||||
Objects: ri.Objects,
|
Objects: ri.Objects,
|
||||||
ObjectsFailed: ri.ObjectsFailed,
|
DeleteMarkers: ri.DeleteMarkers,
|
||||||
BytesTransferred: ri.BytesTransferred,
|
ObjectsFailed: ri.ObjectsFailed,
|
||||||
BytesFailed: ri.BytesFailed,
|
DeleteMarkersFailed: ri.DeleteMarkersFailed,
|
||||||
|
BytesTransferred: ri.BytesTransferred,
|
||||||
|
BytesFailed: ri.BytesFailed,
|
||||||
}
|
}
|
||||||
case string(madmin.BatchJobKeyRotate):
|
case string(madmin.BatchJobKeyRotate):
|
||||||
m.KeyRotate = &madmin.KeyRotationInfo{
|
m.KeyRotate = &madmin.KeyRotationInfo{
|
||||||
@ -2150,10 +2156,12 @@ func (ri *batchJobInfo) metric() madmin.JobMetric {
|
|||||||
}
|
}
|
||||||
case string(madmin.BatchJobExpire):
|
case string(madmin.BatchJobExpire):
|
||||||
m.Expired = &madmin.ExpirationInfo{
|
m.Expired = &madmin.ExpirationInfo{
|
||||||
Bucket: ri.Bucket,
|
Bucket: ri.Bucket,
|
||||||
Object: ri.Object,
|
Object: ri.Object,
|
||||||
Objects: ri.Objects,
|
Objects: ri.Objects,
|
||||||
ObjectsFailed: ri.ObjectsFailed,
|
DeleteMarkers: ri.DeleteMarkers,
|
||||||
|
ObjectsFailed: ri.ObjectsFailed,
|
||||||
|
DeleteMarkersFailed: ri.DeleteMarkersFailed,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
module github.com/minio/minio/docs/debugging/s3-verify
|
module github.com/minio/minio/docs/debugging/s3-verify
|
||||||
|
|
||||||
go 1.23
|
go 1.23.0
|
||||||
toolchain go1.24.1
|
|
||||||
|
toolchain go1.24.2
|
||||||
|
|
||||||
require github.com/minio/minio-go/v7 v7.0.83
|
require github.com/minio/minio-go/v7 v7.0.83
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user