mirror of
https://github.com/pgsty/minio.git
synced 2026-10-18 15:36:11 +03:00
test(listing): cover mixed object and directory recovery groups
Extend the pending-directory collision test with absent recovery and a smaller
key. Add all six normal-object, directory, and pending-object channel orders,
checking complete-group recovery and preservation of trailing entries.
Test changes adapted from commit 557bc274a32330321fa5e5ac7636068c7bd98eb0.
Retain the complete-group directory handling from 3dd53eb81f.
Signed-off-by: Feng Ruohang <rh@vonng.com>
This commit is contained in:
@@ -179,3 +179,65 @@ func TestListingRecoveryDirectoryCollisionOrdering(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A regular walk result may be stale when a pending peer forces current
|
||||||
|
// recovery. Keep sibling prefixes until the complete object group is resolved.
|
||||||
|
func TestListingRecoveryMixedDirectoryCollision(t *testing.T) {
|
||||||
|
orders := [][3]int{{0, 1, 2}, {0, 2, 1}, {1, 0, 2}, {1, 2, 0}, {2, 0, 1}, {2, 1, 0}}
|
||||||
|
for _, order := range orders {
|
||||||
|
for _, present := range []bool{false, true} {
|
||||||
|
t.Run(fmt.Sprintf("order=%v/present=%t", order, present), func(t *testing.T) {
|
||||||
|
entries := []metaCacheEntry{
|
||||||
|
{name: "dir/", metadata: []byte{1}},
|
||||||
|
{name: "dir/"},
|
||||||
|
{name: "dir/", listingSet: &erasureObjects{}},
|
||||||
|
}
|
||||||
|
in := make([]chan metaCacheEntry, len(order))
|
||||||
|
for i, index := range order {
|
||||||
|
in[i] = make(chan metaCacheEntry, 2)
|
||||||
|
in[i] <- entries[index]
|
||||||
|
in[i] <- metaCacheEntry{name: fmt.Sprintf("tail-%d", i), metadata: []byte{1}}
|
||||||
|
close(in[i])
|
||||||
|
}
|
||||||
|
out := make(chan metaCacheEntry, 6)
|
||||||
|
calls := 0
|
||||||
|
err := mergeEntryChannelsWithResolver(t.Context(), in, out, 1, nil, func(group []*metaCacheEntry) (*metaCacheEntry, error) {
|
||||||
|
calls++
|
||||||
|
if len(group) != 2 {
|
||||||
|
t.Fatalf("incomplete object group: %+v", group)
|
||||||
|
}
|
||||||
|
pending := 0
|
||||||
|
for _, entry := range group {
|
||||||
|
if entry.name != "dir/" || entry.isDir() {
|
||||||
|
t.Fatalf("unexpected recovery entry: %+v", entry)
|
||||||
|
}
|
||||||
|
if entry.listingSet != nil {
|
||||||
|
pending++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if pending != 1 {
|
||||||
|
t.Fatalf("pending entries=%d, want 1", pending)
|
||||||
|
}
|
||||||
|
if !present {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return &metaCacheEntry{name: "dir/", metadata: []byte{1}}, nil
|
||||||
|
})
|
||||||
|
if err != nil || calls != 1 {
|
||||||
|
t.Fatalf("err=%v recovery calls=%d", err, calls)
|
||||||
|
}
|
||||||
|
var names []string
|
||||||
|
for entry := range out {
|
||||||
|
names = append(names, entry.name)
|
||||||
|
if entry.listingSet != nil || entry.name == "dir/" && entry.isObject() != present {
|
||||||
|
t.Fatalf("unexpected merged entry: %+v", entry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
want := []string{"dir/", "tail-0", "tail-1", "tail-2"}
|
||||||
|
if !slices.Equal(names, want) {
|
||||||
|
t.Fatalf("names=%v, want %v", names, want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -221,35 +221,52 @@ func TestListingRecoveryNormalPathDoesNotReadOrLock(t *testing.T) {
|
|||||||
|
|
||||||
func TestListingPendingDirectoryCollision(t *testing.T) {
|
func TestListingPendingDirectoryCollision(t *testing.T) {
|
||||||
for _, objectFirst := range []bool{false, true} {
|
for _, objectFirst := range []bool{false, true} {
|
||||||
t.Run(fmt.Sprint(objectFirst), func(t *testing.T) {
|
for _, present := range []bool{true, false} {
|
||||||
in := []chan metaCacheEntry{make(chan metaCacheEntry, 1), make(chan metaCacheEntry, 1)}
|
t.Run(fmt.Sprintf("objectFirst=%v/present=%v", objectFirst, present), func(t *testing.T) {
|
||||||
|
in := []chan metaCacheEntry{make(chan metaCacheEntry, 2), make(chan metaCacheEntry, 2), make(chan metaCacheEntry, 2)}
|
||||||
objectIndex := 1
|
objectIndex := 1
|
||||||
if objectFirst {
|
if objectFirst {
|
||||||
objectIndex = 0
|
objectIndex = 0
|
||||||
}
|
}
|
||||||
in[objectIndex] <- metaCacheEntry{name: "dir/", listingSet: &erasureObjects{}}
|
in[objectIndex] <- metaCacheEntry{name: "dir/", listingSet: &erasureObjects{}}
|
||||||
in[1-objectIndex] <- metaCacheEntry{name: "dir/"}
|
in[1-objectIndex] <- metaCacheEntry{name: "dir/"}
|
||||||
|
in[2] <- metaCacheEntry{name: "a", metadata: []byte{1}}
|
||||||
for _, c := range in {
|
for _, c := range in {
|
||||||
close(c)
|
close(c)
|
||||||
}
|
}
|
||||||
out := make(chan metaCacheEntry, 2)
|
out := make(chan metaCacheEntry, 4)
|
||||||
calls := 0
|
calls := 0
|
||||||
err := mergeEntryChannelsWithResolver(t.Context(), in, out, 1, nil, func(group []*metaCacheEntry) (*metaCacheEntry, error) {
|
err := mergeEntryChannelsWithResolver(t.Context(), in, out, 1, nil, func(group []*metaCacheEntry) (*metaCacheEntry, error) {
|
||||||
calls++
|
calls++
|
||||||
if len(group) != 1 || group[0].listingSet == nil {
|
if len(group) != 1 || group[0].listingSet == nil {
|
||||||
t.Fatalf("wrong pending group: %+v", group)
|
t.Fatalf("wrong pending group: %+v", group)
|
||||||
}
|
}
|
||||||
|
if !present {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
return &metaCacheEntry{name: "dir/", metadata: []byte{1}}, nil
|
return &metaCacheEntry{name: "dir/", metadata: []byte{1}}, nil
|
||||||
})
|
})
|
||||||
if err != nil || calls != 1 || len(out) != 1 {
|
if err != nil || calls != 1 || len(out) != 2 {
|
||||||
t.Fatalf("err=%v recoveries=%d outputs=%d", err, calls, len(out))
|
t.Fatalf("err=%v recoveries=%d outputs=%d", err, calls, len(out))
|
||||||
}
|
}
|
||||||
if entry := <-out; entry.isDir() || !entry.isObjectDir() {
|
first := <-out
|
||||||
|
if first.name != "a" {
|
||||||
|
t.Fatalf("expected smaller key first, got %+v", first)
|
||||||
|
}
|
||||||
|
entry := <-out
|
||||||
|
if entry.name != "dir/" || entry.listingSet != nil {
|
||||||
|
t.Fatalf("unexpected recovered entry: %+v", entry)
|
||||||
|
}
|
||||||
|
if present && (entry.isDir() || !entry.isObjectDir()) {
|
||||||
t.Fatalf("object became directory: %+v", entry)
|
t.Fatalf("object became directory: %+v", entry)
|
||||||
}
|
}
|
||||||
|
if !present && (!entry.isDir() || entry.name != "dir/") {
|
||||||
|
t.Fatalf("absent pending object dropped live directory prefix: %+v", entry)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestListingRecoveryRejectsUnexpectedSinglePoolSet(t *testing.T) {
|
func TestListingRecoveryRejectsUnexpectedSinglePoolSet(t *testing.T) {
|
||||||
sets := []*erasureObjects{{}, {}}
|
sets := []*erasureObjects{{}, {}}
|
||||||
|
|||||||
Reference in New Issue
Block a user