|
|
@ -821,13 +821,16 @@ func (f *FileInfoCh) Push(fi FileInfo) { |
|
|
|
f.Valid = true |
|
|
|
f.Valid = true |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Calculate least entry across multiple FileInfo channels, additionally
|
|
|
|
// Calculate least entry across multiple FileInfo channels,
|
|
|
|
// returns a boolean to indicate if the caller needs to call again.
|
|
|
|
// returns the least common entry and the total number of times
|
|
|
|
func leastEntry(entriesCh []FileInfoCh, totalDrives int, heal bool) (FileInfo, bool) { |
|
|
|
// we found this entry. Additionally also returns a boolean
|
|
|
|
var entriesValid = make([]bool, len(entriesCh)) |
|
|
|
// to indicate if the caller needs to call this function
|
|
|
|
var entries = make([]FileInfo, len(entriesCh)) |
|
|
|
// again to list the next entry. It is callers responsibility
|
|
|
|
for i := range entriesCh { |
|
|
|
// if the caller wishes to list N entries to call leastEntry
|
|
|
|
entries[i], entriesValid[i] = entriesCh[i].Pop() |
|
|
|
// N times until this boolean is 'false'.
|
|
|
|
|
|
|
|
func leastEntry(entryChs []FileInfoCh, entries []FileInfo, entriesValid []bool) (FileInfo, int, bool) { |
|
|
|
|
|
|
|
for i := range entryChs { |
|
|
|
|
|
|
|
entries[i], entriesValid[i] = entryChs[i].Pop() |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
var isTruncated = false |
|
|
|
var isTruncated = false |
|
|
@ -856,9 +859,9 @@ func leastEntry(entriesCh []FileInfoCh, totalDrives int, heal bool) (FileInfo, b |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// We haven't been able to find any least entry,
|
|
|
|
// We haven't been able to find any least entry,
|
|
|
|
// this would mean that we don't have valid.
|
|
|
|
// this would mean that we don't have valid entry.
|
|
|
|
if !found { |
|
|
|
if !found { |
|
|
|
return lentry, isTruncated |
|
|
|
return lentry, 0, isTruncated |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
leastEntryCount := 0 |
|
|
|
leastEntryCount := 0 |
|
|
@ -876,53 +879,76 @@ func leastEntry(entriesCh []FileInfoCh, totalDrives int, heal bool) (FileInfo, b |
|
|
|
|
|
|
|
|
|
|
|
// Push all entries which are lexically higher
|
|
|
|
// Push all entries which are lexically higher
|
|
|
|
// and will be returned later in Pop()
|
|
|
|
// and will be returned later in Pop()
|
|
|
|
entriesCh[i].Push(entries[i]) |
|
|
|
entryChs[i].Push(entries[i]) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
quorum := lentry.Quorum |
|
|
|
return lentry, leastEntryCount, isTruncated |
|
|
|
if quorum == 0 { |
|
|
|
|
|
|
|
quorum = totalDrives / 2 |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if heal { |
|
|
|
|
|
|
|
// When healing is enabled, we should
|
|
|
|
|
|
|
|
// list only objects which need healing.
|
|
|
|
|
|
|
|
if leastEntryCount != totalDrives { |
|
|
|
|
|
|
|
return lentry, isTruncated |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
if leastEntryCount >= quorum { |
|
|
|
|
|
|
|
return lentry, isTruncated |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return leastEntry(entriesCh, totalDrives, heal) |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// mergeEntriesCh - merges FileInfo channel to entries upto maxKeys.
|
|
|
|
// mergeEntriesCh - merges FileInfo channel to entries upto maxKeys.
|
|
|
|
func mergeEntriesCh(entriesCh []FileInfoCh, maxKeys int, totalDrives int, heal bool) (entries FilesInfo) { |
|
|
|
func mergeEntriesCh(entryChs []FileInfoCh, maxKeys int, totalDrives int, heal bool) (entries FilesInfo) { |
|
|
|
var i = 0 |
|
|
|
var i = 0 |
|
|
|
|
|
|
|
entriesInfos := make([]FileInfo, len(entryChs)) |
|
|
|
|
|
|
|
entriesValid := make([]bool, len(entryChs)) |
|
|
|
for { |
|
|
|
for { |
|
|
|
fi, valid := leastEntry(entriesCh, totalDrives, heal) |
|
|
|
fi, quorumCount, valid := leastEntry(entryChs, entriesInfos, entriesValid) |
|
|
|
if !valid { |
|
|
|
if !valid { |
|
|
|
|
|
|
|
// We have reached EOF across all entryChs, break the loop.
|
|
|
|
break |
|
|
|
break |
|
|
|
} |
|
|
|
} |
|
|
|
if i == maxKeys { |
|
|
|
|
|
|
|
entries.IsTruncated = true |
|
|
|
rquorum := fi.Quorum |
|
|
|
// Re-insert the last entry so it can be
|
|
|
|
// Quorum is zero for all directories.
|
|
|
|
// listed in the next listing iteration.
|
|
|
|
if rquorum == 0 { |
|
|
|
for j := range entriesCh { |
|
|
|
// Choose N/2 quoroum for directory entries.
|
|
|
|
if !entriesCh[j].Valid { |
|
|
|
rquorum = totalDrives / 2 |
|
|
|
entriesCh[j].Push(fi) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if heal { |
|
|
|
|
|
|
|
// When healing is enabled, we should
|
|
|
|
|
|
|
|
// list only objects which need healing.
|
|
|
|
|
|
|
|
if quorumCount == totalDrives { |
|
|
|
|
|
|
|
// Skip good entries.
|
|
|
|
|
|
|
|
continue |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
// Regular listing, we skip entries not in quorum.
|
|
|
|
|
|
|
|
if quorumCount < rquorum { |
|
|
|
|
|
|
|
// Skip entries which do not have quorum.
|
|
|
|
|
|
|
|
continue |
|
|
|
} |
|
|
|
} |
|
|
|
break |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
entries.Files = append(entries.Files, fi) |
|
|
|
entries.Files = append(entries.Files, fi) |
|
|
|
i++ |
|
|
|
i++ |
|
|
|
|
|
|
|
if i == maxKeys { |
|
|
|
|
|
|
|
entries.IsTruncated = isTruncated(entryChs, entriesInfos, entriesValid) |
|
|
|
|
|
|
|
break |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
return entries |
|
|
|
return entries |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func isTruncated(entryChs []FileInfoCh, entries []FileInfo, entriesValid []bool) bool { |
|
|
|
|
|
|
|
for i := range entryChs { |
|
|
|
|
|
|
|
entries[i], entriesValid[i] = entryChs[i].Pop() |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var isTruncated = false |
|
|
|
|
|
|
|
for _, valid := range entriesValid { |
|
|
|
|
|
|
|
if !valid { |
|
|
|
|
|
|
|
continue |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
isTruncated = true |
|
|
|
|
|
|
|
break |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
for i := range entryChs { |
|
|
|
|
|
|
|
if entriesValid[i] { |
|
|
|
|
|
|
|
entryChs[i].Push(entries[i]) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return isTruncated |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Starts a walk channel across all disks and returns a slice.
|
|
|
|
// Starts a walk channel across all disks and returns a slice.
|
|
|
|
func (s *xlSets) startMergeWalks(ctx context.Context, bucket, prefix, marker string, recursive bool, endWalkCh chan struct{}) []FileInfoCh { |
|
|
|
func (s *xlSets) startMergeWalks(ctx context.Context, bucket, prefix, marker string, recursive bool, endWalkCh chan struct{}) []FileInfoCh { |
|
|
|
var entryChs []FileInfoCh |
|
|
|
var entryChs []FileInfoCh |
|
|
@ -955,15 +981,26 @@ func (s *xlSets) listObjectsNonSlash(ctx context.Context, bucket, prefix, marker |
|
|
|
var eof bool |
|
|
|
var eof bool |
|
|
|
var prevPrefix string |
|
|
|
var prevPrefix string |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
entriesValid := make([]bool, len(entryChs)) |
|
|
|
|
|
|
|
entries := make([]FileInfo, len(entryChs)) |
|
|
|
for { |
|
|
|
for { |
|
|
|
if len(objInfos) == maxKeys { |
|
|
|
if len(objInfos) == maxKeys { |
|
|
|
break |
|
|
|
break |
|
|
|
} |
|
|
|
} |
|
|
|
result, ok := leastEntry(entryChs, s.drivesPerSet, false) |
|
|
|
result, quorumCount, ok := leastEntry(entryChs, entries, entriesValid) |
|
|
|
if !ok { |
|
|
|
if !ok { |
|
|
|
eof = true |
|
|
|
eof = true |
|
|
|
break |
|
|
|
break |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
rquorum := result.Quorum |
|
|
|
|
|
|
|
// Quorum is zero for all directories.
|
|
|
|
|
|
|
|
if rquorum == 0 { |
|
|
|
|
|
|
|
// Choose N/2 quorum for directory entries.
|
|
|
|
|
|
|
|
rquorum = s.drivesPerSet / 2 |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if quorumCount < rquorum { |
|
|
|
|
|
|
|
continue |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
var objInfo ObjectInfo |
|
|
|
var objInfo ObjectInfo |
|
|
|
|
|
|
|
|
|
|
|