refactor objectQuorumFromMeta() to search for parity quorum (#15844)

This commit is contained in:
Anis Elleuch 2022-10-13 00:42:45 +01:00 committed by GitHub
parent 97112c69be
commit 783dd875f7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 109 additions and 73 deletions

View File

@ -179,53 +179,6 @@ func listOnlineDisks(disks []StorageAPI, partsMetadata []FileInfo, errs []error)
return onlineDisks, modTime return onlineDisks, modTime
} }
// Returns the latest updated FileInfo files and error in case of failure.
func getLatestFileInfo(ctx context.Context, partsMetadata []FileInfo, defaultParityCount int, errs []error) (FileInfo, error) {
// There should be atleast half correct entries, if not return failure
expectedRQuorum := len(partsMetadata) / 2
if defaultParityCount == 0 {
// if parity count is '0', we expected all entries to be present.
expectedRQuorum = len(partsMetadata)
}
reducedErr := reduceReadQuorumErrs(ctx, errs, objectOpIgnoredErrs, expectedRQuorum)
if reducedErr != nil {
return FileInfo{}, reducedErr
}
// List all the file commit ids from parts metadata.
modTimes := listObjectModtimes(partsMetadata, errs)
// Count all latest updated FileInfo values
var count int
var latestFileInfo FileInfo
// Reduce list of UUIDs to a single common value - i.e. the last updated Time
modTime := commonTime(modTimes)
if modTime.IsZero() || modTime.Equal(timeSentinel) {
return FileInfo{}, errErasureReadQuorum
}
// Interate through all the modTimes and count the FileInfo(s) with latest time.
for index, t := range modTimes {
if partsMetadata[index].IsValid() && t.Equal(modTime) {
latestFileInfo = partsMetadata[index]
count++
}
}
if !latestFileInfo.IsValid() {
return FileInfo{}, errErasureReadQuorum
}
if count < latestFileInfo.Erasure.DataBlocks {
return FileInfo{}, errErasureReadQuorum
}
return latestFileInfo, nil
}
// disksWithAllParts - This function needs to be called with // disksWithAllParts - This function needs to be called with
// []StorageAPI returned by listOnlineDisks. Returns, // []StorageAPI returned by listOnlineDisks. Returns,
// //

View File

@ -30,6 +30,53 @@ import (
"github.com/minio/madmin-go" "github.com/minio/madmin-go"
) )
// Returns the latest updated FileInfo files and error in case of failure.
func getLatestFileInfo(ctx context.Context, partsMetadata []FileInfo, defaultParityCount int, errs []error) (FileInfo, error) {
// There should be atleast half correct entries, if not return failure
expectedRQuorum := len(partsMetadata) / 2
if defaultParityCount == 0 {
// if parity count is '0', we expected all entries to be present.
expectedRQuorum = len(partsMetadata)
}
reducedErr := reduceReadQuorumErrs(ctx, errs, objectOpIgnoredErrs, expectedRQuorum)
if reducedErr != nil {
return FileInfo{}, reducedErr
}
// List all the file commit ids from parts metadata.
modTimes := listObjectModtimes(partsMetadata, errs)
// Count all latest updated FileInfo values
var count int
var latestFileInfo FileInfo
// Reduce list of UUIDs to a single common value - i.e. the last updated Time
modTime := commonTime(modTimes)
if modTime.IsZero() || modTime.Equal(timeSentinel) {
return FileInfo{}, errErasureReadQuorum
}
// Interate through all the modTimes and count the FileInfo(s) with latest time.
for index, t := range modTimes {
if partsMetadata[index].IsValid() && t.Equal(modTime) {
latestFileInfo = partsMetadata[index]
count++
}
}
if !latestFileInfo.IsValid() {
return FileInfo{}, errErasureReadQuorum
}
if count < latestFileInfo.Erasure.DataBlocks {
return FileInfo{}, errErasureReadQuorum
}
return latestFileInfo, nil
}
// validates functionality provided to find most common // validates functionality provided to find most common
// time occurrence from a list of time. // time occurrence from a list of time.
func TestCommonTime(t *testing.T) { func TestCommonTime(t *testing.T) {

View File

@ -394,21 +394,73 @@ func writeUniqueFileInfo(ctx context.Context, disks []StorageAPI, bucket, prefix
return evalDisks(disks, mErrs), err return evalDisks(disks, mErrs), err
} }
func commonParity(parities []int) int {
occMap := make(map[int]int)
for _, p := range parities {
occMap[p]++
}
var maxOcc, commonParity int
for parity, occ := range occMap {
if parity == -1 {
// Ignore non defined parity
continue
}
if occ >= maxOcc {
maxOcc = occ
commonParity = parity
}
}
if maxOcc == 0 {
// Did not found anything useful
return -1
}
return commonParity
}
func listObjectParities(partsMetadata []FileInfo, errs []error) (parities []int) {
parities = make([]int, len(partsMetadata))
for index, metadata := range partsMetadata {
if errs[index] != nil {
parities[index] = -1
continue
}
parities[index] = metadata.Erasure.ParityBlocks
}
return
}
// Returns per object readQuorum and writeQuorum // Returns per object readQuorum and writeQuorum
// readQuorum is the min required disks to read data. // readQuorum is the min required disks to read data.
// writeQuorum is the min required disks to write data. // writeQuorum is the min required disks to write data.
func objectQuorumFromMeta(ctx context.Context, partsMetaData []FileInfo, errs []error, defaultParityCount int) (objectReadQuorum, objectWriteQuorum int, err error) { func objectQuorumFromMeta(ctx context.Context, partsMetaData []FileInfo, errs []error, defaultParityCount int) (objectReadQuorum, objectWriteQuorum int, err error) {
// get the latest updated Metadata and a count of all the latest updated FileInfo(s) // There should be atleast half correct entries, if not return failure
latestFileInfo, err := getLatestFileInfo(ctx, partsMetaData, defaultParityCount, errs) expectedRQuorum := len(partsMetaData) / 2
if err != nil { if defaultParityCount == 0 {
return 0, 0, err // if parity count is '0', we expected all entries to be present.
expectedRQuorum = len(partsMetaData)
} }
if latestFileInfo.Deleted { reducedErr := reduceReadQuorumErrs(ctx, errs, objectOpIgnoredErrs, expectedRQuorum)
// special case when parity is '0' if reducedErr != nil {
if defaultParityCount == 0 { return -1, -1, reducedErr
return len(partsMetaData), len(partsMetaData), nil }
}
// special case when parity is '0'
if defaultParityCount == 0 {
return len(partsMetaData), len(partsMetaData), nil
}
parities := listObjectParities(partsMetaData, errs)
parityBlocks := commonParity(parities)
if parityBlocks < 0 {
return -1, -1, errErasureReadQuorum
}
if parityBlocks == 0 {
// For delete markers do not use 'defaultParityCount' as it is not expected to be the case. // For delete markers do not use 'defaultParityCount' as it is not expected to be the case.
// Use maximum allowed read quorum instead, writeQuorum+1 is returned for compatibility sake // Use maximum allowed read quorum instead, writeQuorum+1 is returned for compatibility sake
// but there are no callers that shall be using this. // but there are no callers that shall be using this.
@ -416,23 +468,7 @@ func objectQuorumFromMeta(ctx context.Context, partsMetaData []FileInfo, errs []
return readQuorum, readQuorum + 1, nil return readQuorum, readQuorum + 1, nil
} }
parityBlocks := globalStorageClass.GetParityForSC(latestFileInfo.Metadata[xhttp.AmzStorageClass]) dataBlocks := len(partsMetaData) - parityBlocks
if parityBlocks < 0 {
parityBlocks = defaultParityCount
}
// For erasure code upgraded objects choose the parity
// blocks saved internally, instead of 'defaultParityCount'
if _, ok := latestFileInfo.Metadata[minIOErasureUpgraded]; ok {
if latestFileInfo.Erasure.ParityBlocks != 0 {
parityBlocks = latestFileInfo.Erasure.ParityBlocks
}
}
dataBlocks := latestFileInfo.Erasure.DataBlocks
if dataBlocks == 0 {
dataBlocks = len(partsMetaData) - parityBlocks
}
writeQuorum := dataBlocks writeQuorum := dataBlocks
if dataBlocks == parityBlocks { if dataBlocks == parityBlocks {