package topology import ( "testing" "github.com/seaweedfs/seaweedfs/weed/pb/master_pb" "github.com/seaweedfs/seaweedfs/weed/storage/needle" ) func diskMoveNode(t *testing.T) (*Topology, *DataNode) { t.Helper() topo := NewTopology("move", nil, 32*1024*1024*1024, 5, false) dn := topo.GetOrCreateDataCenter("dc1").GetOrCreateRack("rack1"). GetOrCreateDataNode("127.0.0.1", 8080, 18080, "", "", map[string]uint32{"": 100, "ssd": 100}) return topo, dn } func diskMoveVolume(id uint32, diskType string, diskId uint32) *master_pb.VolumeInformationMessage { return &master_pb.VolumeInformationMessage{ Id: id, Size: 1024, Collection: "c", Version: 3, DiskType: diskType, DiskId: diskId, } } func heldCopies(dn *DataNode) int { total := 0 for _, c := range dn.Children() { total += c.(*Disk).VolumeCount() } return total } func TestVolumeMovedBetweenDisks(t *testing.T) { for _, tc := range []struct { name string from, to string }{ {"SameDiskType", "", ""}, {"DifferentDiskType", "", "ssd"}, {"BackAgain", "ssd", ""}, } { t.Run(tc.name, func(t *testing.T) { topo, dn := diskMoveNode(t) topo.SyncDataNodeRegistration( []*master_pb.VolumeInformationMessage{diskMoveVolume(1, tc.from, 0)}, dn) topo.SyncDataNodeRegistration( []*master_pb.VolumeInformationMessage{diskMoveVolume(1, tc.to, 1)}, dn) if got := heldCopies(dn); got != 1 { t.Errorf("master holds %d copies of a volume that moved, want 1", got) } stored, err := dn.GetVolumesById(needle.VolumeId(1)) if err != nil { t.Fatalf("moved volume is no longer on the node: %v", err) } if stored.DiskType != tc.to || stored.DiskId != 1 { t.Errorf("master has the volume on disk %q/%d, server reports %q/1", stored.DiskType, stored.DiskId, tc.to) } if !dn.HasConsistentVolumeIndex() { t.Error("the move left the lookup index disagreeing with the disks") } }) } } // A stale twin is two reports, not a move: dropping one would tell the master a // replica vanished. func TestVolumeReportedOnTwoDiskTypesIsKept(t *testing.T) { topo, dn := diskMoveNode(t) topo.SyncDataNodeRegistration([]*master_pb.VolumeInformationMessage{ diskMoveVolume(1, "", 0), diskMoveVolume(1, "ssd", 1), }, dn) if got := heldCopies(dn); got != 2 { t.Errorf("master holds %d copies of a volume reported on two disks, want 2", got) } if dn.HasDuplicateVolumeIds() { t.Error("a volume on two disk types is representable, so it should not disable digest comparison") } topo.SyncDataNodeRegistration([]*master_pb.VolumeInformationMessage{diskMoveVolume(1, "ssd", 1)}, dn) if got := heldCopies(dn); got != 1 { t.Errorf("master holds %d copies after the twin was unmounted, want 1", got) } } func TestVolumeReportedTwiceOnOneDiskTypeIsFlagged(t *testing.T) { topo, dn := diskMoveNode(t) first := diskMoveVolume(1, "ssd", 0) second := diskMoveVolume(1, "ssd", 1) topo.SyncDataNodeRegistration([]*master_pb.VolumeInformationMessage{first, second}, dn) if !dn.HasDuplicateVolumeIds() { t.Error("a volume reported twice on one disk type went unflagged") } } func TestVolumeDigestSurvivesADiskMove(t *testing.T) { topo, dn := diskMoveNode(t) topo.SyncDataNodeRegistration([]*master_pb.VolumeInformationMessage{diskMoveVolume(1, "", 0)}, dn) moved := diskMoveVolume(1, "ssd", 1) topo.SyncDataNodeRegistration([]*master_pb.VolumeInformationMessage{moved}, dn) reference, referenceNode := diskMoveNode(t) reference.SyncDataNodeRegistration([]*master_pb.VolumeInformationMessage{moved}, referenceNode) if dn.VolumeDigest() != referenceNode.VolumeDigest() { t.Errorf("after a disk move the digest is %d, a server holding only the moved volume reports %d", dn.VolumeDigest(), referenceNode.VolumeDigest()) } }