Files
seaweedfs/weed/storage/volume_info_intern_test.go
Chris LuandGitHub f09e8345c6 storage: stop keeping the remote storage key on the master (#10672)
A master decides nothing from it. Every caller that read it was asking whether
a volume is remote, which the backend name answers, and the value itself is
reported on demand by the server holding the volume, through the volume info in
ReadVolumeFileStatus.

It is also the one string here that cannot be shared: unique per volume, so
unlike the collection and backend names it carries its own characters for every
volume a master tracks.

VolumeInfo goes from 136 bytes to 120. 800k volumes registered from a heartbeat
that has been over the wire go from 214 to 163 B/volume when tiered.

The volume server's own status page keeps showing the key, now read from the
volume it holds rather than relayed through a master, which is also where the
other volume server implementation reads it.

The heartbeat digest drops it on the same grounds: a change to something the
master does not hold cannot make its copy stale. Both implementations and their
shared vectors move together, and the field-coverage test now names what is
deliberately not retained rather than being loosened.
2026-08-09 12:43:31 -07:00

150 lines
4.1 KiB
Go

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)
}
}
}