package storage import ( "runtime" "sync" "testing" "unsafe" "github.com/seaweedfs/seaweedfs/weed/pb/master_pb" ) func stringData(s string) uintptr { if s == "" { return 0 } return uintptr(unsafe.Pointer(unsafe.StringData(s))) } // Every heartbeat decodes a fresh string for values a cluster repeats across // all its volumes, so a master holding a million volumes would otherwise hold a // million copies of the same handful of names. func TestRepeatedVolumeStringsAreShared(t *testing.T) { message := func() *master_pb.VolumeInformationMessage { return &master_pb.VolumeInformationMessage{ Id: 1, Version: 3, // Built from bytes so each is a distinct allocation, as decoding is. Collection: string([]byte("somecollection")), DiskType: string([]byte("ssd")), RemoteStorageName: string([]byte("s3cold")), } } first, err := NewVolumeInfo(message()) if err != nil { t.Fatal(err) } second, err := NewVolumeInfo(message()) if err != nil { t.Fatal(err) } for _, tc := range []struct { name string a, b string share bool }{ {"Collection", first.Collection, second.Collection, true}, {"DiskType", first.DiskType, second.DiskType, true}, {"RemoteStorageName", first.RemoteStorageName, second.RemoteStorageName, true}, } { if tc.a != tc.b { t.Fatalf("%s: values differ, %q vs %q", tc.name, tc.a, tc.b) } if shared := stringData(tc.a) == stringData(tc.b); shared != tc.share { t.Errorf("%s: shared=%v, want %v", tc.name, shared, tc.share) } } } func TestShortVolumeInfoSharesTheSameStrings(t *testing.T) { message := func() *master_pb.VolumeShortInformationMessage { return &master_pb.VolumeShortInformationMessage{ Id: 1, Version: 3, Collection: string([]byte("somecollection")), DiskType: string([]byte("ssd")), } } first, err := NewVolumeInfoFromShort(message()) if err != nil { t.Fatal(err) } second, err := NewVolumeInfoFromShort(message()) if err != nil { t.Fatal(err) } if stringData(first.Collection) != stringData(second.Collection) { t.Error("collection is not shared between volumes reported as a delta") } if stringData(first.DiskType) != stringData(second.DiskType) { t.Error("disk type is not shared between volumes reported as a delta") } } func TestEmptyVolumeStringsStayEmpty(t *testing.T) { vi, err := NewVolumeInfo(&master_pb.VolumeInformationMessage{Id: 1, Version: 3}) if err != nil { t.Fatal(err) } if vi.Collection != "" || vi.DiskType != "" || vi.RemoteStorageName != "" { t.Errorf("expected empty strings to survive, got %+v", vi) } if vi.IsRemote() { t.Error("a volume with no remote backend reads as remote") } } // Sharing has to survive collection: volumes are interned when they are first // reported, and in a cluster sending only what changed most are never reported // again. A table that let its entries be collected would hand the next volume // a second copy of a name the rest of the cluster already shares. func TestVolumeStringsStaySharedAcrossCollection(t *testing.T) { first, err := NewVolumeInfo(&master_pb.VolumeInformationMessage{ Id: 1, Version: 3, Collection: string([]byte("somecollection")), }) if err != nil { t.Fatal(err) } original := stringData(first.Collection) for i := 0; i < 5; i++ { runtime.GC() } later, err := NewVolumeInfo(&master_pb.VolumeInformationMessage{ Id: 2, Version: 3, Collection: string([]byte("somecollection")), }) if err != nil { t.Fatal(err) } if stringData(later.Collection) != original { t.Error("a volume reported after a collection got its own copy of the name") } runtime.KeepAlive(first) } func TestInterningIsSafeUnderConcurrentReports(t *testing.T) { var wg sync.WaitGroup shared := make([]uintptr, 16) for i := range shared { wg.Add(1) go func(i int) { defer wg.Done() vi, err := NewVolumeInfo(&master_pb.VolumeInformationMessage{ Id: uint32(i), Version: 3, Collection: string([]byte("concurrent")), }) if err != nil { t.Error(err) return } shared[i] = stringData(vi.Collection) }(i) } wg.Wait() for i, got := range shared { if got != shared[0] { t.Fatalf("report %d got its own copy of the name", i) } } }