Quorum based listing for XL (#5475)

fixes #5380
master
Krishna Srinivas 7 years ago committed by kannappanr
parent b606ba3f81
commit 2afd196c83
  1. 9
      cmd/tree-walk_test.go
  2. 41
      cmd/xl-v1-list-objects.go
  3. 36
      cmd/xl-v1-object.go

@ -312,14 +312,17 @@ func TestListDir(t *testing.T) {
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
if len(entries) != 1 { if len(entries) != 2 {
t.Fatal("Expected the number of entries to be 1") t.Fatal("Expected the number of entries to be 2")
} }
if entries[0] != file1 { if entries[0] != file1 {
t.Fatal("Expected the entry to be file1") t.Fatal("Expected the entry to be file1")
} }
if entries[1] != file2 {
t.Fatal("Expected the entry to be file2")
}
// Remove fsDir1 to test failover. // Remove fsDir1, list should return entries from fsDir2
err = os.RemoveAll(fsDir1) err = os.RemoveAll(fsDir1)
if err != nil { if err != nil {
t.Error(err) t.Error(err)

@ -16,18 +16,24 @@
package cmd package cmd
import "github.com/minio/minio/pkg/errors" import (
"sort"
"github.com/minio/minio/pkg/errors"
)
// Returns function "listDir" of the type listDirFunc. // Returns function "listDir" of the type listDirFunc.
// isLeaf - is used by listDir function to check if an entry is a leaf or non-leaf entry. // isLeaf - is used by listDir function to check if an entry is a leaf or non-leaf entry.
// disks - used for doing disk.ListDir(). FS passes single disk argument, XL passes a list of disks. // disks - used for doing disk.ListDir()
func listDirFactory(isLeaf isLeafFunc, treeWalkIgnoredErrs []error, disks ...StorageAPI) listDirFunc { func listDirFactory(isLeaf isLeafFunc, treeWalkIgnoredErrs []error, disks ...StorageAPI) listDirFunc {
// listDir - lists all the entries at a given prefix and given entry in the prefix. // Returns sorted merged entries from all the disks.
listDir := func(bucket, prefixDir, prefixEntry string) (entries []string, delayIsLeaf bool, err error) { listDir := func(bucket, prefixDir, prefixEntry string) (mergedEntries []string, delayIsLeaf bool, err error) {
for _, disk := range disks { for _, disk := range disks {
if disk == nil { if disk == nil {
continue continue
} }
var entries []string
var newEntries []string
entries, err = disk.ListDir(bucket, prefixDir) entries, err = disk.ListDir(bucket, prefixDir)
if err != nil { if err != nil {
// For any reason disk was deleted or goes offline, continue // For any reason disk was deleted or goes offline, continue
@ -38,11 +44,24 @@ func listDirFactory(isLeaf isLeafFunc, treeWalkIgnoredErrs []error, disks ...Sto
return nil, false, errors.Trace(err) return nil, false, errors.Trace(err)
} }
entries, delayIsLeaf = filterListEntries(bucket, prefixDir, entries, prefixEntry, isLeaf) // Find elements in entries which are not in mergedEntries
return entries, delayIsLeaf, nil for _, entry := range entries {
idx := sort.SearchStrings(mergedEntries, entry)
// if entry is already present in mergedEntries don't add.
if idx < len(mergedEntries) && mergedEntries[idx] == entry {
continue
}
newEntries = append(newEntries, entry)
}
if len(newEntries) > 0 {
// Merge the entries and sort it.
mergedEntries = append(mergedEntries, newEntries...)
sort.Strings(mergedEntries)
}
} }
// Nothing found in all disks mergedEntries, delayIsLeaf = filterListEntries(bucket, prefixDir, mergedEntries, prefixEntry, isLeaf)
return nil, false, nil return mergedEntries, delayIsLeaf, nil
} }
return listDir return listDir
} }
@ -90,8 +109,10 @@ func (xl xlObjects) listObjects(bucket, prefix, marker, delimiter string, maxKey
var err error var err error
objInfo, err = xl.getObjectInfo(bucket, entry) objInfo, err = xl.getObjectInfo(bucket, entry)
if err != nil { if err != nil {
// Ignore errFileNotFound // Ignore errFileNotFound as the object might have got deleted in the interim period
if errors.Cause(err) == errFileNotFound { // of listing and getObjectInfo()
// Ignore quorum error as it might be an entry from an outdated disk.
if errors.Cause(err) == errFileNotFound || errors.Cause(err) == errXLReadQuorum {
continue continue
} }
return loi, toObjectErr(err, bucket, prefix) return loi, toObjectErr(err, bucket, prefix)

@ -446,30 +446,48 @@ func (xl xlObjects) GetObjectInfo(bucket, object string) (oi ObjectInfo, e error
// getObjectInfo - wrapper for reading object metadata and constructs ObjectInfo. // getObjectInfo - wrapper for reading object metadata and constructs ObjectInfo.
func (xl xlObjects) getObjectInfo(bucket, object string) (objInfo ObjectInfo, err error) { func (xl xlObjects) getObjectInfo(bucket, object string) (objInfo ObjectInfo, err error) {
// Read metadata associated with the object from all disks.
metaArr, errs := readAllXLMetadata(xl.storageDisks, bucket, object)
// Extracts xlStat and xlMetaMap. // get Quorum for this object
xlStat, xlMetaMap, err := xl.readXLMetaStat(bucket, object) readQuorum, _, err := objectQuorumFromMeta(xl, metaArr, errs)
if err != nil { if err != nil {
return ObjectInfo{}, err return objInfo, toObjectErr(err, bucket, object)
}
if reducedErr := reduceReadQuorumErrs(errs, objectOpIgnoredErrs, readQuorum); reducedErr != nil {
return objInfo, toObjectErr(reducedErr, bucket, object)
}
// List all the file commit ids from parts metadata.
modTimes := listObjectModtimes(metaArr, errs)
// Reduce list of UUIDs to a single common value.
modTime, _ := commonTime(modTimes)
// Pick latest valid metadata.
xlMeta, err := pickValidXLMeta(metaArr, modTime)
if err != nil {
return objInfo, err
} }
objInfo = ObjectInfo{ objInfo = ObjectInfo{
IsDir: false, IsDir: false,
Bucket: bucket, Bucket: bucket,
Name: object, Name: object,
Size: xlStat.Size, Size: xlMeta.Stat.Size,
ModTime: xlStat.ModTime, ModTime: xlMeta.Stat.ModTime,
ContentType: xlMetaMap["content-type"], ContentType: xlMeta.Meta["content-type"],
ContentEncoding: xlMetaMap["content-encoding"], ContentEncoding: xlMeta.Meta["content-encoding"],
} }
// Extract etag. // Extract etag.
objInfo.ETag = extractETag(xlMetaMap) objInfo.ETag = extractETag(xlMeta.Meta)
// etag/md5Sum has already been extracted. We need to // etag/md5Sum has already been extracted. We need to
// remove to avoid it from appearing as part of // remove to avoid it from appearing as part of
// response headers. e.g, X-Minio-* or X-Amz-*. // response headers. e.g, X-Minio-* or X-Amz-*.
objInfo.UserDefined = cleanMetadata(xlMetaMap) objInfo.UserDefined = cleanMetadata(xlMeta.Meta)
// Success. // Success.
return objInfo, nil return objInfo, nil

Loading…
Cancel
Save