Enhance picking valid xlMeta based on quorum (#6297)

This PR borrows the idea from getFormatXLQuorum()
This commit is contained in:
Harshavardhana 2018-08-17 14:42:04 -07:00 committed by kannappanr
parent 50dec08002
commit 3de5a3157f
5 changed files with 53 additions and 20 deletions

View File

@ -404,7 +404,7 @@ func healObject(ctx context.Context, storageDisks []StorageAPI, bucket string, o
// Latest xlMetaV1 for reference. If a valid metadata is not // Latest xlMetaV1 for reference. If a valid metadata is not
// present, it is as good as object not found. // present, it is as good as object not found.
latestMeta, pErr := pickValidXLMeta(ctx, partsMetadata, modTime) latestMeta, pErr := pickValidXLMeta(ctx, partsMetadata, modTime, quorum)
if pErr != nil { if pErr != nil {
return result, toObjectErr(pErr, bucket, object) return result, toObjectErr(pErr, bucket, object)
} }

View File

@ -18,9 +18,9 @@ package cmd
import ( import (
"context" "context"
"crypto/sha256"
"encoding/hex" "encoding/hex"
"encoding/json" "encoding/json"
"fmt"
"path" "path"
"sort" "sort"
"sync" "sync"
@ -295,18 +295,52 @@ func (m xlMetaV1) ObjectToPartOffset(ctx context.Context, offset int64) (partInd
return 0, 0, InvalidRange{} return 0, 0, InvalidRange{}
} }
func getXLMetaInQuorum(ctx context.Context, metaArr []xlMetaV1, modTime time.Time, quorum int) (xmv xlMetaV1, e error) {
metaHashes := make([]string, len(metaArr))
for i, meta := range metaArr {
if meta.IsValid() && meta.Stat.ModTime.Equal(modTime) {
h := sha256.New()
for _, p := range meta.Parts {
h.Write([]byte(p.Name))
}
metaHashes[i] = hex.EncodeToString(h.Sum(nil))
}
}
metaHashCountMap := make(map[string]int)
for _, hash := range metaHashes {
if hash == "" {
continue
}
metaHashCountMap[hash]++
}
maxHash := ""
maxCount := 0
for hash, count := range metaHashCountMap {
if count > maxCount {
maxCount = count
maxHash = hash
}
}
if maxCount < quorum {
return xlMetaV1{}, errXLReadQuorum
}
for i, hash := range metaHashes {
if hash == maxHash {
return metaArr[i], nil
}
}
return xlMetaV1{}, errXLReadQuorum
}
// pickValidXLMeta - picks one valid xlMeta content and returns from a // pickValidXLMeta - picks one valid xlMeta content and returns from a
// slice of xlmeta content. // slice of xlmeta content.
func pickValidXLMeta(ctx context.Context, metaArr []xlMetaV1, modTime time.Time) (xmv xlMetaV1, e error) { func pickValidXLMeta(ctx context.Context, metaArr []xlMetaV1, modTime time.Time, quorum int) (xmv xlMetaV1, e error) {
// Pick latest valid metadata. return getXLMetaInQuorum(ctx, metaArr, modTime, quorum)
for _, meta := range metaArr {
if meta.IsValid() && meta.Stat.ModTime.Equal(modTime) {
return meta, nil
}
}
err := fmt.Errorf("No valid xl.json present")
logger.LogIf(ctx, err)
return xmv, err
} }
// list of all errors that can be ignored in a metadata operation. // list of all errors that can be ignored in a metadata operation.

View File

@ -19,7 +19,6 @@ package cmd
import ( import (
"bytes" "bytes"
"context" "context"
"errors"
"os" "os"
"path" "path"
"strconv" "strconv"
@ -349,11 +348,11 @@ func TestPickValidXLMeta(t *testing.T) {
metaArr: invalidXS, metaArr: invalidXS,
modTime: now, modTime: now,
xlMeta: invalidX1, xlMeta: invalidX1,
expectedErr: errors.New("No valid xl.json present"), expectedErr: errXLReadQuorum,
}, },
} }
for i, test := range testCases { for i, test := range testCases {
xlMeta, err := pickValidXLMeta(context.Background(), test.metaArr, test.modTime) xlMeta, err := pickValidXLMeta(context.Background(), test.metaArr, test.modTime, len(test.metaArr)/2)
if test.expectedErr != nil { if test.expectedErr != nil {
if err.Error() != test.expectedErr.Error() { if err.Error() != test.expectedErr.Error() {
t.Errorf("Test %d: Expected to fail with %v but received %v", t.Errorf("Test %d: Expected to fail with %v but received %v",

View File

@ -346,7 +346,7 @@ func (xl xlObjects) PutObjectPart(ctx context.Context, bucket, object, uploadID
onlineDisks, modTime := listOnlineDisks(xl.getDisks(), partsMetadata, errs) onlineDisks, modTime := listOnlineDisks(xl.getDisks(), partsMetadata, errs)
// Pick one from the first valid metadata. // Pick one from the first valid metadata.
xlMeta, err := pickValidXLMeta(ctx, partsMetadata, modTime) xlMeta, err := pickValidXLMeta(ctx, partsMetadata, modTime, writeQuorum)
if err != nil { if err != nil {
return pi, err return pi, err
} }
@ -445,7 +445,7 @@ func (xl xlObjects) PutObjectPart(ctx context.Context, bucket, object, uploadID
onlineDisks, modTime = listOnlineDisks(onlineDisks, partsMetadata, errs) onlineDisks, modTime = listOnlineDisks(onlineDisks, partsMetadata, errs)
// Pick one from the first valid metadata. // Pick one from the first valid metadata.
xlMeta, err = pickValidXLMeta(ctx, partsMetadata, modTime) xlMeta, err = pickValidXLMeta(ctx, partsMetadata, modTime, writeQuorum)
if err != nil { if err != nil {
return pi, err return pi, err
} }
@ -645,7 +645,7 @@ func (xl xlObjects) CompleteMultipartUpload(ctx context.Context, bucket string,
var objectSize int64 var objectSize int64
// Pick one from the first valid metadata. // Pick one from the first valid metadata.
xlMeta, err := pickValidXLMeta(ctx, partsMetadata, modTime) xlMeta, err := pickValidXLMeta(ctx, partsMetadata, modTime, writeQuorum)
if err != nil { if err != nil {
return oi, err return oi, err
} }

View File

@ -102,7 +102,7 @@ func (xl xlObjects) CopyObject(ctx context.Context, srcBucket, srcObject, dstBuc
_, modTime := listOnlineDisks(storageDisks, metaArr, errs) _, modTime := listOnlineDisks(storageDisks, metaArr, errs)
// Pick latest valid metadata. // Pick latest valid metadata.
xlMeta, err := pickValidXLMeta(ctx, metaArr, modTime) xlMeta, err := pickValidXLMeta(ctx, metaArr, modTime, readQuorum)
if err != nil { if err != nil {
return oi, toObjectErr(err, srcBucket, srcObject) return oi, toObjectErr(err, srcBucket, srcObject)
} }
@ -221,7 +221,7 @@ func (xl xlObjects) getObject(ctx context.Context, bucket, object string, startO
onlineDisks, modTime := listOnlineDisks(xl.getDisks(), metaArr, errs) onlineDisks, modTime := listOnlineDisks(xl.getDisks(), metaArr, errs)
// Pick latest valid metadata. // Pick latest valid metadata.
xlMeta, err := pickValidXLMeta(ctx, metaArr, modTime) xlMeta, err := pickValidXLMeta(ctx, metaArr, modTime, readQuorum)
if err != nil { if err != nil {
return err return err
} }
@ -402,7 +402,7 @@ func (xl xlObjects) getObjectInfo(ctx context.Context, bucket, object string) (o
modTime, _ := commonTime(modTimes) modTime, _ := commonTime(modTimes)
// Pick latest valid metadata. // Pick latest valid metadata.
xlMeta, err := pickValidXLMeta(ctx, metaArr, modTime) xlMeta, err := pickValidXLMeta(ctx, metaArr, modTime, readQuorum)
if err != nil { if err != nil {
return objInfo, err return objInfo, err
} }