minio/cmd/xl-storage-free-version_test.go
Harshavardhana b0c84e3de7
fix: deleteVersions causing xl.meta to have empty Versions[] slice (#14483)
This is a side-affect of the optimization done in PR #13544 which
causes a certain type of delete operations on given object versions
can cause lastVersion indication to be skipped, which leads to
an `xl.meta` where Versions[] slice is empty while the entire
file is intact by itself.

This PR tries to ensure that such files are visible and deletable
by regular means of listing as null 'delete-marker' and also
avoid the situation where this potential issue might arise.
2022-03-04 20:01:26 -08:00

174 lines
4.5 KiB
Go

// Copyright (c) 2015-2021 MinIO, Inc.
//
// This file is part of MinIO Object Storage stack
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package cmd
import (
"testing"
"time"
"github.com/minio/minio/internal/bucket/lifecycle"
)
func (x xlMetaV2) listFreeVersions(volume, path string) ([]FileInfo, error) {
fivs, err := x.ListVersions(volume, path)
if err != nil {
return nil, err
}
n := 0
for _, fiv := range fivs {
if fiv.TierFreeVersion() {
fivs[n] = fiv
n++
}
}
fivs = fivs[:n]
return fivs, nil
}
func TestFreeVersion(t *testing.T) {
fatalErr := func(err error) {
t.Helper()
if err != nil {
t.Fatal(err)
}
}
// Add a version with tiered content, one with local content
xl := xlMetaV2{}
counter := 1
report := func() {
t.Helper()
// t.Logf("versions (%d): len = %d", counter, len(xl.versions))
counter++
}
fi := FileInfo{
Volume: "volume",
Name: "object-name",
VersionID: "00000000-0000-0000-0000-000000000001",
IsLatest: true,
Deleted: false,
TransitionStatus: "",
DataDir: "bffea160-ca7f-465f-98bc-9b4f1c3ba1ef",
XLV1: false,
ModTime: time.Now(),
Size: 0,
Mode: 0,
Metadata: nil,
Parts: nil,
Erasure: ErasureInfo{
Algorithm: ReedSolomon.String(),
DataBlocks: 4,
ParityBlocks: 2,
BlockSize: 10000,
Index: 1,
Distribution: []int{1, 2, 3, 4, 5, 6, 7, 8},
Checksums: []ChecksumInfo{{
PartNumber: 1,
Algorithm: HighwayHash256S,
Hash: nil,
}},
},
MarkDeleted: false,
// DeleteMarkerReplicationStatus: "",
// VersionPurgeStatus: "",
NumVersions: 1,
SuccessorModTime: time.Time{},
}
// Add a version with local content
fatalErr(xl.AddVersion(fi))
report()
// Add null version with tiered content
tierfi := fi
tierfi.VersionID = ""
fatalErr(xl.AddVersion(tierfi))
report()
tierfi.TransitionStatus = lifecycle.TransitionComplete
tierfi.TransitionedObjName = mustGetUUID()
tierfi.TransitionTier = "MINIOTIER-1"
var err error
_, err = xl.DeleteVersion(tierfi)
fatalErr(err)
report()
fvIDs := []string{
"00000000-0000-0000-0000-0000000000f1",
"00000000-0000-0000-0000-0000000000f2",
}
// Simulate overwrite of null version
newtierfi := tierfi
newtierfi.SetTierFreeVersionID(fvIDs[0])
fatalErr(xl.AddFreeVersion(newtierfi))
report()
fatalErr(xl.AddVersion(newtierfi))
report()
// Simulate removal of null version
newtierfi.TransitionTier = ""
newtierfi.TransitionedObjName = ""
newtierfi.TransitionStatus = ""
newtierfi.SetTierFreeVersionID(fvIDs[1])
report()
_, err = xl.DeleteVersion(newtierfi)
report()
fatalErr(err)
// Check number of free-versions
freeVersions, err := xl.listFreeVersions(newtierfi.Volume, newtierfi.Name)
if err != nil {
t.Fatalf("failed to list free versions %v", err)
}
if len(freeVersions) != 2 {
t.Fatalf("Expected two free versions but got %d", len(freeVersions))
}
// Simulate scanner removing free-version
freefi := newtierfi
for _, fvID := range fvIDs {
freefi.VersionID = fvID
_, err = xl.DeleteVersion(freefi)
fatalErr(err)
}
report()
// Check number of free-versions
freeVersions, err = xl.listFreeVersions(newtierfi.Volume, newtierfi.Name)
if err != nil {
t.Fatalf("failed to list free versions %v", err)
}
if len(freeVersions) != 0 {
t.Fatalf("Expected zero free version but got %d", len(freeVersions))
}
report()
// Adding a free version to a version with no tiered content.
newfi := fi
newfi.SetTierFreeVersionID("00000000-0000-0000-0000-0000000000f3")
fatalErr(xl.AddFreeVersion(newfi)) // this shouldn't add a free-version
report()
// Check number of free-versions
freeVersions, err = xl.listFreeVersions(newtierfi.Volume, newtierfi.Name)
if err != nil {
t.Fatalf("failed to list free versions %v", err)
}
if len(freeVersions) != 0 {
t.Fatalf("Expected zero free version but got %d", len(freeVersions))
}
}