mirror of
https://github.com/minio/minio.git
synced 2025-04-01 02:03:42 -04:00
Avoid recursion and use a simple loop to merge entries (#8239)
This avoids stack overflows when there are lot of entries to be skipped, this PR also optimizes the code to reuse the buffers.
This commit is contained in:
parent
fa32c71a56
commit
5392eee250
115
cmd/xl-sets.go
115
cmd/xl-sets.go
@ -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
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user