Skip to content

Commit 3a90fb1

Browse files
only look for metadata if batch replication asks for metadata filters (minio#18082)
This PR changes the StatObject() to be must have for non-minio source to being a conditional API call. - Calls StatObject() when needed - Calls GetObjectTagging() when needed These calls if we do without these conditionals can cause a lot of delays, so we avoid them if not needed in more common scenario.
1 parent 4eeb48f commit 3a90fb1

File tree

1 file changed

+50
-25
lines changed

1 file changed

+50
-25
lines changed

cmd/batch-handlers.go

Lines changed: 50 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import (
3737
"github.com/dustin/go-humanize"
3838
"github.com/lithammer/shortuuid/v4"
3939
"github.com/minio/madmin-go/v3"
40+
"github.com/minio/minio-go/v7"
4041
miniogo "github.com/minio/minio-go/v7"
4142
"github.com/minio/minio-go/v7/pkg/credentials"
4243
"github.com/minio/minio-go/v7/pkg/encrypt"
@@ -269,6 +270,10 @@ func (r *BatchJobReplicateV1) StartFromSource(ctx context.Context, api ObjectLay
269270
}
270271
rnd := rand.New(rand.NewSource(time.Now().UnixNano()))
271272

273+
isTags := len(r.Flags.Filter.Tags) != 0
274+
isMetadata := len(r.Flags.Filter.Metadata) != 0
275+
isStorageClassOnly := len(r.Flags.Filter.Metadata) == 1 && strings.EqualFold(r.Flags.Filter.Metadata[0].Key, xhttp.AmzStorageClass)
276+
272277
skip := func(oi ObjectInfo) (ok bool) {
273278
if r.Flags.Filter.OlderThan > 0 && time.Since(oi.ModTime) < r.Flags.Filter.OlderThan {
274279
// skip all objects that are newer than specified older duration
@@ -289,7 +294,8 @@ func (r *BatchJobReplicateV1) StartFromSource(ctx context.Context, api ObjectLay
289294
// skip all objects that are created after the specified time.
290295
return true
291296
}
292-
if len(r.Flags.Filter.Tags) > 0 {
297+
298+
if isTags {
293299
// Only parse object tags if tags filter is specified.
294300
tagMap := map[string]string{}
295301
tagStr := oi.UserTags
@@ -312,23 +318,19 @@ func (r *BatchJobReplicateV1) StartFromSource(ctx context.Context, api ObjectLay
312318
return false
313319
}
314320

315-
if len(r.Flags.Filter.Metadata) > 0 {
316-
for _, kv := range r.Flags.Filter.Metadata {
317-
for k, v := range oi.UserDefined {
318-
if !stringsHasPrefixFold(k, "x-amz-meta-") && !isStandardHeader(k) {
319-
continue
320-
}
321-
// We only need to match x-amz-meta or standardHeaders
322-
if kv.Match(BatchJobKV{Key: k, Value: v}) {
323-
return true
324-
}
321+
for _, kv := range r.Flags.Filter.Metadata {
322+
for k, v := range oi.UserDefined {
323+
if !stringsHasPrefixFold(k, "x-amz-meta-") && !isStandardHeader(k) {
324+
continue
325+
}
326+
// We only need to match x-amz-meta or standardHeaders
327+
if kv.Match(BatchJobKV{Key: k, Value: v}) {
328+
return true
325329
}
326330
}
327-
328-
// None of the provided metadata filters match skip the object.
329-
return false
330331
}
331332

333+
// None of the provided filters match
332334
return false
333335
}
334336

@@ -383,17 +385,32 @@ func (r *BatchJobReplicateV1) StartFromSource(ctx context.Context, api ObjectLay
383385
for obj := range objInfoCh {
384386
oi := toObjectInfo(r.Source.Bucket, obj.Key, obj)
385387
if !minioSrc {
386-
oi2, err := c.StatObject(ctx, r.Source.Bucket, obj.Key, miniogo.StatObjectOptions{})
387-
if err == nil {
388-
oi = toObjectInfo(r.Source.Bucket, obj.Key, oi2)
389-
} else {
390-
if isErrMethodNotAllowed(ErrorRespToObjectError(err, r.Source.Bucket, obj.Key)) ||
391-
isErrObjectNotFound(ErrorRespToObjectError(err, r.Source.Bucket, obj.Key)) {
388+
// Check if metadata filter was requested and it is expected to have
389+
// all user metadata or just storageClass. If its only storageClass
390+
// List() already returns relevant information for filter to be applied.
391+
if isMetadata && !isStorageClassOnly {
392+
oi2, err := c.StatObject(ctx, r.Source.Bucket, obj.Key, miniogo.StatObjectOptions{})
393+
if err == nil {
394+
oi = toObjectInfo(r.Source.Bucket, obj.Key, oi2)
395+
} else {
396+
if !isErrMethodNotAllowed(ErrorRespToObjectError(err, r.Source.Bucket, obj.Key)) &&
397+
!isErrObjectNotFound(ErrorRespToObjectError(err, r.Source.Bucket, obj.Key)) {
398+
logger.LogIf(ctx, err)
399+
}
400+
continue
401+
}
402+
}
403+
if isTags {
404+
tags, err := c.GetObjectTagging(ctx, r.Source.Bucket, obj.Key, minio.GetObjectTaggingOptions{})
405+
if err == nil {
406+
oi.UserTags = tags.String()
407+
} else {
408+
if !isErrMethodNotAllowed(ErrorRespToObjectError(err, r.Source.Bucket, obj.Key)) &&
409+
!isErrObjectNotFound(ErrorRespToObjectError(err, r.Source.Bucket, obj.Key)) {
410+
logger.LogIf(ctx, err)
411+
}
392412
continue
393413
}
394-
logger.LogIf(ctx, err)
395-
cancel()
396-
return err
397414
}
398415
}
399416
if skip(oi) {
@@ -481,17 +498,25 @@ func toObjectInfo(bucket, object string, objInfo miniogo.ObjectInfo) ObjectInfo
481498
ReplicationStatusInternal: objInfo.ReplicationStatus,
482499
UserTags: tags.String(),
483500
}
501+
484502
oi.UserDefined = make(map[string]string, len(objInfo.Metadata))
485503
for k, v := range objInfo.Metadata {
486504
oi.UserDefined[k] = v[0]
487505
}
506+
488507
ce, ok := oi.UserDefined[xhttp.ContentEncoding]
489508
if !ok {
490509
ce, ok = oi.UserDefined[strings.ToLower(xhttp.ContentEncoding)]
491510
}
492511
if ok {
493512
oi.ContentEncoding = ce
494513
}
514+
515+
_, ok = oi.UserDefined[xhttp.AmzStorageClass]
516+
if !ok {
517+
oi.UserDefined[xhttp.AmzStorageClass] = objInfo.StorageClass
518+
}
519+
495520
return oi
496521
}
497522

@@ -821,7 +846,7 @@ func (r *BatchJobReplicateV1) Start(ctx context.Context, api ObjectLayer, job Ba
821846
}
822847
rnd := rand.New(rand.NewSource(time.Now().UnixNano()))
823848

824-
skip := func(info FileInfo) (ok bool) {
849+
selectObj := func(info FileInfo) (ok bool) {
825850
if r.Flags.Filter.OlderThan > 0 && time.Since(info.ModTime) < r.Flags.Filter.OlderThan {
826851
// skip all objects that are newer than specified older duration
827852
return false
@@ -931,7 +956,7 @@ func (r *BatchJobReplicateV1) Start(ctx context.Context, api ObjectLayer, job Ba
931956
results := make(chan ObjectInfo, 100)
932957
if err := api.Walk(ctx, r.Source.Bucket, r.Source.Prefix, results, ObjectOptions{
933958
WalkMarker: lastObject,
934-
WalkFilter: skip,
959+
WalkFilter: selectObj,
935960
}); err != nil {
936961
cancel()
937962
// Do not need to retry if we can't list objects on source.

0 commit comments

Comments
 (0)