mirror of
https://github.com/minio/minio.git
synced 2025-04-22 03:24:38 -04:00
Add more advanced cases for dangling (#18968)
This commit is contained in:
parent
8b68e0bfdc
commit
f225ca3312
@ -900,11 +900,10 @@ func isObjectDangling(metaArr []FileInfo, errs []error, dataErrs []error) (valid
|
|||||||
// We can consider an object data not reliable
|
// We can consider an object data not reliable
|
||||||
// when xl.meta is not found in read quorum disks.
|
// when xl.meta is not found in read quorum disks.
|
||||||
// or when xl.meta is not readable in read quorum disks.
|
// or when xl.meta is not readable in read quorum disks.
|
||||||
danglingErrsCount := func(cerrs []error) (int, int, int) {
|
danglingErrsCount := func(cerrs []error) (int, int) {
|
||||||
var (
|
var (
|
||||||
notFoundCount int
|
notFoundCount int
|
||||||
corruptedCount int
|
nonActionableCount int
|
||||||
driveNotFoundCount int
|
|
||||||
)
|
)
|
||||||
for _, readErr := range cerrs {
|
for _, readErr := range cerrs {
|
||||||
if readErr == nil {
|
if readErr == nil {
|
||||||
@ -913,29 +912,16 @@ func isObjectDangling(metaArr []FileInfo, errs []error, dataErrs []error) (valid
|
|||||||
switch {
|
switch {
|
||||||
case errors.Is(readErr, errFileNotFound) || errors.Is(readErr, errFileVersionNotFound):
|
case errors.Is(readErr, errFileNotFound) || errors.Is(readErr, errFileVersionNotFound):
|
||||||
notFoundCount++
|
notFoundCount++
|
||||||
case errors.Is(readErr, errFileCorrupt):
|
|
||||||
corruptedCount++
|
|
||||||
default:
|
default:
|
||||||
// All other errors are non-actionable
|
// All other errors are non-actionable
|
||||||
driveNotFoundCount++
|
nonActionableCount++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return notFoundCount, corruptedCount, driveNotFoundCount
|
return notFoundCount, nonActionableCount
|
||||||
}
|
}
|
||||||
|
|
||||||
ndataErrs := make([]error, len(dataErrs))
|
notFoundMetaErrs, nonActionableMetaErrs := danglingErrsCount(errs)
|
||||||
for i := range dataErrs {
|
notFoundPartsErrs, nonActionablePartsErrs := danglingErrsCount(dataErrs)
|
||||||
if errs[i] != dataErrs[i] {
|
|
||||||
// Only count part errors, if the error is not
|
|
||||||
// same as xl.meta error. This is to avoid
|
|
||||||
// double counting when both parts and xl.meta
|
|
||||||
// are not available.
|
|
||||||
ndataErrs[i] = dataErrs[i]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
notFoundMetaErrs, corruptedMetaErrs, driveNotFoundMetaErrs := danglingErrsCount(errs)
|
|
||||||
notFoundPartsErrs, corruptedPartsErrs, driveNotFoundPartsErrs := danglingErrsCount(ndataErrs)
|
|
||||||
|
|
||||||
for _, m := range metaArr {
|
for _, m := range metaArr {
|
||||||
if m.IsValid() {
|
if m.IsValid() {
|
||||||
@ -945,10 +931,9 @@ func isObjectDangling(metaArr []FileInfo, errs []error, dataErrs []error) (valid
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !validMeta.IsValid() {
|
if !validMeta.IsValid() {
|
||||||
// validMeta is invalid because notFoundPartsErrs is
|
// validMeta is invalid because all xl.meta is missing apparently
|
||||||
// greater than parity blocks, thus invalidating the FileInfo{}
|
// we should figure out if dataDirs are also missing > dataBlocks.
|
||||||
// every dataErrs[i], metaArr[i] is an empty FileInfo{}
|
dataBlocks := (len(dataErrs) + 1) / 2
|
||||||
dataBlocks := (len(ndataErrs) + 1) / 2
|
|
||||||
if notFoundPartsErrs > dataBlocks {
|
if notFoundPartsErrs > dataBlocks {
|
||||||
// Not using parity to ensure that we do not delete
|
// Not using parity to ensure that we do not delete
|
||||||
// any valid content, if any is recoverable. But if
|
// any valid content, if any is recoverable. But if
|
||||||
@ -965,25 +950,40 @@ func isObjectDangling(metaArr []FileInfo, errs []error, dataErrs []error) (valid
|
|||||||
return validMeta, false
|
return validMeta, false
|
||||||
}
|
}
|
||||||
|
|
||||||
if driveNotFoundMetaErrs > 0 || driveNotFoundPartsErrs > 0 {
|
if nonActionableMetaErrs > 0 || nonActionablePartsErrs > 0 {
|
||||||
return validMeta, false
|
return validMeta, false
|
||||||
}
|
}
|
||||||
|
|
||||||
if validMeta.Deleted {
|
if validMeta.Deleted {
|
||||||
// notFoundPartsErrs is ignored since
|
// notFoundPartsErrs is ignored since
|
||||||
// - delete marker does not have any parts
|
// - delete marker does not have any parts
|
||||||
return validMeta, corruptedMetaErrs+notFoundMetaErrs > len(errs)/2
|
dataBlocks := (len(errs) + 1) / 2
|
||||||
|
return validMeta, notFoundMetaErrs > dataBlocks
|
||||||
}
|
}
|
||||||
|
|
||||||
totalErrs := notFoundMetaErrs + corruptedMetaErrs + notFoundPartsErrs + corruptedPartsErrs
|
quorum := validMeta.Erasure.DataBlocks
|
||||||
if validMeta.IsRemote() {
|
if validMeta.Erasure.DataBlocks == validMeta.Erasure.ParityBlocks {
|
||||||
// notFoundPartsErrs is ignored since
|
quorum++
|
||||||
// - transition status of complete has no parts
|
|
||||||
totalErrs = notFoundMetaErrs + corruptedMetaErrs
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// We have valid meta, now verify if we have enough files with parity blocks.
|
// TODO: It is possible to replay the object via just single
|
||||||
return validMeta, totalErrs > validMeta.Erasure.ParityBlocks
|
// xl.meta file, considering quorum number of data-dirs are still
|
||||||
|
// present on other drives.
|
||||||
|
//
|
||||||
|
// However this requires a bit of a rewrite, leave this up for
|
||||||
|
// future work.
|
||||||
|
|
||||||
|
if notFoundMetaErrs > 0 && notFoundMetaErrs >= quorum {
|
||||||
|
// All xl.meta is beyond data blocks missing, this is dangling
|
||||||
|
return validMeta, true
|
||||||
|
}
|
||||||
|
|
||||||
|
if notFoundPartsErrs > 0 && notFoundPartsErrs >= quorum {
|
||||||
|
// All data-dir is beyond data blocks missing, this is dangling
|
||||||
|
return validMeta, true
|
||||||
|
}
|
||||||
|
|
||||||
|
return validMeta, false
|
||||||
}
|
}
|
||||||
|
|
||||||
// HealObject - heal the given object, automatically deletes the object if stale/corrupted if `remove` is true.
|
// HealObject - heal the given object, automatically deletes the object if stale/corrupted if `remove` is true.
|
||||||
|
@ -41,6 +41,10 @@ func TestIsObjectDangling(t *testing.T) {
|
|||||||
fi := newFileInfo("test-object", 2, 2)
|
fi := newFileInfo("test-object", 2, 2)
|
||||||
fi.Erasure.Index = 1
|
fi.Erasure.Index = 1
|
||||||
|
|
||||||
|
ifi := newFileInfo("test-object", 2, 2)
|
||||||
|
ifi.SetInlineData()
|
||||||
|
ifi.Erasure.Index = 1
|
||||||
|
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
name string
|
name string
|
||||||
metaArr []FileInfo
|
metaArr []FileInfo
|
||||||
@ -129,13 +133,77 @@ func TestIsObjectDangling(t *testing.T) {
|
|||||||
expectedMeta: FileInfo{},
|
expectedMeta: FileInfo{},
|
||||||
expectedDangling: false,
|
expectedDangling: false,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "FileInfoUnDecided-case4",
|
||||||
|
metaArr: []FileInfo{
|
||||||
|
{},
|
||||||
|
{},
|
||||||
|
{},
|
||||||
|
ifi,
|
||||||
|
},
|
||||||
|
errs: []error{
|
||||||
|
errFileNotFound,
|
||||||
|
errFileCorrupt,
|
||||||
|
errFileCorrupt,
|
||||||
|
nil,
|
||||||
|
},
|
||||||
|
dataErrs: nil,
|
||||||
|
expectedMeta: ifi,
|
||||||
|
expectedDangling: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "FileInfoUnDecided-case5-(ignore errFileCorrupt error)",
|
||||||
|
metaArr: []FileInfo{
|
||||||
|
{},
|
||||||
|
{},
|
||||||
|
{},
|
||||||
|
fi,
|
||||||
|
},
|
||||||
|
errs: []error{
|
||||||
|
errFileNotFound,
|
||||||
|
errFileCorrupt,
|
||||||
|
nil,
|
||||||
|
nil,
|
||||||
|
},
|
||||||
|
dataErrs: []error{
|
||||||
|
errFileCorrupt,
|
||||||
|
errFileNotFound,
|
||||||
|
nil,
|
||||||
|
errFileCorrupt,
|
||||||
|
},
|
||||||
|
expectedMeta: fi,
|
||||||
|
expectedDangling: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "FileInfoUnDecided-case6-(data-dir intact)",
|
||||||
|
metaArr: []FileInfo{
|
||||||
|
{},
|
||||||
|
{},
|
||||||
|
{},
|
||||||
|
fi,
|
||||||
|
},
|
||||||
|
errs: []error{
|
||||||
|
errFileNotFound,
|
||||||
|
errFileNotFound,
|
||||||
|
errFileNotFound,
|
||||||
|
nil,
|
||||||
|
},
|
||||||
|
dataErrs: []error{
|
||||||
|
errFileNotFound,
|
||||||
|
errFileCorrupt,
|
||||||
|
nil,
|
||||||
|
nil,
|
||||||
|
},
|
||||||
|
expectedMeta: fi,
|
||||||
|
expectedDangling: false,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "FileInfoDecided-case1",
|
name: "FileInfoDecided-case1",
|
||||||
metaArr: []FileInfo{
|
metaArr: []FileInfo{
|
||||||
{},
|
{},
|
||||||
{},
|
{},
|
||||||
{},
|
{},
|
||||||
fi,
|
ifi,
|
||||||
},
|
},
|
||||||
errs: []error{
|
errs: []error{
|
||||||
errFileNotFound,
|
errFileNotFound,
|
||||||
@ -144,25 +212,7 @@ func TestIsObjectDangling(t *testing.T) {
|
|||||||
nil,
|
nil,
|
||||||
},
|
},
|
||||||
dataErrs: nil,
|
dataErrs: nil,
|
||||||
expectedMeta: fi,
|
expectedMeta: ifi,
|
||||||
expectedDangling: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "FileInfoDecided-case2",
|
|
||||||
metaArr: []FileInfo{
|
|
||||||
{},
|
|
||||||
{},
|
|
||||||
{},
|
|
||||||
fi,
|
|
||||||
},
|
|
||||||
errs: []error{
|
|
||||||
errFileNotFound,
|
|
||||||
errFileCorrupt,
|
|
||||||
errFileCorrupt,
|
|
||||||
nil,
|
|
||||||
},
|
|
||||||
dataErrs: nil,
|
|
||||||
expectedMeta: fi,
|
|
||||||
expectedDangling: true,
|
expectedDangling: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -175,8 +225,8 @@ func TestIsObjectDangling(t *testing.T) {
|
|||||||
},
|
},
|
||||||
errs: []error{
|
errs: []error{
|
||||||
errFileNotFound,
|
errFileNotFound,
|
||||||
errFileCorrupt,
|
errFileNotFound,
|
||||||
errFileCorrupt,
|
errFileNotFound,
|
||||||
nil,
|
nil,
|
||||||
},
|
},
|
||||||
dataErrs: nil,
|
dataErrs: nil,
|
||||||
@ -184,26 +234,26 @@ func TestIsObjectDangling(t *testing.T) {
|
|||||||
expectedDangling: true,
|
expectedDangling: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "FileInfoDecided-case2-(duplicate data errors)",
|
name: "FileInfoDecided-case3-(enough data-dir missing)",
|
||||||
metaArr: []FileInfo{
|
metaArr: []FileInfo{
|
||||||
{},
|
{},
|
||||||
{},
|
{},
|
||||||
{},
|
{},
|
||||||
{Deleted: true},
|
fi,
|
||||||
},
|
},
|
||||||
errs: []error{
|
errs: []error{
|
||||||
errFileNotFound,
|
errFileNotFound,
|
||||||
errFileCorrupt,
|
errFileNotFound,
|
||||||
errFileCorrupt,
|
nil,
|
||||||
nil,
|
nil,
|
||||||
},
|
},
|
||||||
dataErrs: []error{
|
dataErrs: []error{
|
||||||
errFileNotFound,
|
errFileNotFound,
|
||||||
errFileCorrupt,
|
errFileNotFound,
|
||||||
nil,
|
|
||||||
nil,
|
nil,
|
||||||
|
errFileNotFound,
|
||||||
},
|
},
|
||||||
expectedMeta: FileInfo{Deleted: true},
|
expectedMeta: fi,
|
||||||
expectedDangling: true,
|
expectedDangling: true,
|
||||||
},
|
},
|
||||||
// Add new cases as seen
|
// Add new cases as seen
|
||||||
|
Loading…
x
Reference in New Issue
Block a user