master: unit tests for WritableAvgFill and FindVolumeLayout

Covers the average-fill computation (mixed fills, no writables, a
writable volume without a size-tracking entry yet) and that
FindVolumeLayout resolves existing layouts without creating missing
collections.
This commit is contained in:
bruce-zzz
2026-10-12 15:34:12 +08:00
parent 643338f9b5
commit bb18d86822
@@ -0,0 +1,138 @@
package topology
import (
"math"
"testing"
"github.com/seaweedfs/seaweedfs/weed/storage/needle"
"github.com/seaweedfs/seaweedfs/weed/storage/super_block"
"github.com/seaweedfs/seaweedfs/weed/storage/types"
)
func TestWritableAvgFill(t *testing.T) {
// 3 writable volumes at 20%, 50%, 80% fill (sizes 2000, 5000, 8000 of
// limit 10000): average fill is 50%.
layout := `
{
"dc1":{
"rack1":{
"server1":{
"volumes":[
{"id":1, "size":2000, "replication":"000"},
{"id":2, "size":5000, "replication":"000"},
{"id":3, "size":8000, "replication":"000"}
],
"limit":10
}
}
}
}
`
_, vl := setupPickTest(t, layout, 10000)
avgFill, writableCount := vl.WritableAvgFill()
if writableCount != 3 {
t.Fatalf("expected 3 writable volumes, got %d", writableCount)
}
if math.Abs(avgFill-0.5) > 1e-9 {
t.Errorf("expected avg fill 0.5, got %v", avgFill)
}
}
func TestWritableAvgFillNoWritables(t *testing.T) {
layout := `
{
"dc1":{
"rack1":{
"server1":{
"volumes":[
{"id":1, "size":2000, "replication":"000"}
],
"limit":10
}
}
}
}
`
_, vl := setupPickTest(t, layout, 10000)
vl.accessLock.Lock()
vl.removeFromWritable(1)
vl.accessLock.Unlock()
avgFill, writableCount := vl.WritableAvgFill()
if writableCount != 0 || avgFill != 0 {
t.Errorf("expected (0, 0) with no writables, got (%v, %d)", avgFill, writableCount)
}
}
// A writable volume without a size-tracking entry yet — freshly created, no
// heartbeat size report so far — counts as empty, not as an error.
func TestWritableAvgFillUntrackedVolumeCountsAsEmpty(t *testing.T) {
layout := `
{
"dc1":{
"rack1":{
"server1":{
"volumes":[
{"id":1, "size":5000, "replication":"000"},
{"id":2, "size":5000, "replication":"000"}
],
"limit":10
}
}
}
}
`
_, vl := setupPickTest(t, layout, 10000)
vl.accessLock.Lock()
delete(vl.sizeTracking, 2)
vl.accessLock.Unlock()
avgFill, writableCount := vl.WritableAvgFill()
if writableCount != 2 {
t.Fatalf("expected 2 writable volumes, got %d", writableCount)
}
if math.Abs(avgFill-0.25) > 1e-9 {
t.Errorf("expected avg fill 0.25 (one of two volumes empty), got %v", avgFill)
}
}
func TestFindVolumeLayout(t *testing.T) {
layout := `
{
"dc1":{
"rack1":{
"server1":{
"volumes":[
{"id":1, "size":2000, "collection":"colA", "replication":"000"}
],
"limit":10
}
}
}
}
`
topo := setupWithLimit(t, layout, 10000)
rp, _ := super_block.NewReplicaPlacementFromString("000")
// Existing layout is found.
if vl := topo.FindVolumeLayout("colA", rp, needle.EMPTY_TTL, types.HardDriveType); vl == nil {
t.Error("expected layout for colA to be found")
}
// A missing collection resolves to nil and must not be created.
if vl := topo.FindVolumeLayout("noSuchCollection", rp, needle.EMPTY_TTL, types.HardDriveType); vl != nil {
t.Error("expected nil layout for unknown collection")
}
if _, ok := topo.collectionMap.Find("noSuchCollection"); ok {
t.Error("FindVolumeLayout must not create the collection")
}
// A missing layout within an existing collection also resolves to nil.
rpOther, _ := super_block.NewReplicaPlacementFromString("001")
if vl := topo.FindVolumeLayout("colA", rpOther, needle.EMPTY_TTL, types.HardDriveType); vl != nil {
t.Error("expected nil layout for unused replication")
}
}