topology: mirror the writable volume list in a set (#11076)

Membership was a linear scan over a slice, and ensureCorrectWritables runs it
for every volume on every heartbeat, so the master's steady-state cost per
volume server is quadratic in that server's volume count.

BenchmarkSyncDataNodeRegistration, median of 3:

  1000 volumes     565.7us -> 535.0us    -5.4%
  100000 volumes    1.665s -> 55.3ms     -96.7%

Allocations are unchanged at both sizes, so the difference is the scan.

Claude-Session: https://claude.ai/code/session_01P3pE6J2UPFp6G3ksfMV4s1
This commit is contained in:
Chris Lu
2026-09-01 14:21:26 -07:00
committed by GitHub
parent 40b3d32fe5
commit 8f2daad338
2 changed files with 29 additions and 28 deletions
+12 -10
View File
@@ -60,6 +60,9 @@ type VolumeLayout struct {
diskType types.DiskType
vid2location map[needle.VolumeId]*VolumeLocationList
writables []needle.VolumeId // transient array of writable volume id
// writableMembers mirrors writables for membership tests, which run for
// every volume on every heartbeat through ensureCorrectWritables.
writableMembers map[needle.VolumeId]struct{}
crowded map[needle.VolumeId]struct{}
vacuumedVolumes map[needle.VolumeId]time.Time
volumeSizeLimit uint64
@@ -86,6 +89,7 @@ func NewVolumeLayout(rp *super_block.ReplicaPlacement, ttl *needle.TTL, diskType
diskType: diskType,
vid2location: make(map[needle.VolumeId]*VolumeLocationList),
writables: *new([]needle.VolumeId),
writableMembers: make(map[needle.VolumeId]struct{}),
crowded: make(map[needle.VolumeId]struct{}),
vacuumedVolumes: make(map[needle.VolumeId]time.Time),
volumeSizeLimit: volumeSizeLimit,
@@ -896,28 +900,26 @@ func (vl *VolumeLayout) CountUnderReplicatedVolumes() int {
}
func (vl *VolumeLayout) removeFromWritable(vid needle.VolumeId) bool {
toDeleteIndex := -1
if _, ok := vl.writableMembers[vid]; !ok {
return false
}
for k, id := range vl.writables {
if id == vid {
toDeleteIndex = k
break
}
}
if toDeleteIndex >= 0 {
glog.V(0).Infoln("Volume", vid, "becomes unwritable")
vl.writables = append(vl.writables[0:toDeleteIndex], vl.writables[toDeleteIndex+1:]...)
vl.writables = append(vl.writables[0:k], vl.writables[k+1:]...)
delete(vl.writableMembers, vid)
return true
}
}
return false
}
func (vl *VolumeLayout) setVolumeWritable(vid needle.VolumeId) bool {
for _, v := range vl.writables {
if v == vid {
if _, ok := vl.writableMembers[vid]; ok {
return false
}
}
glog.V(1).Infoln("Volume", vid, "becomes writable")
vl.writables = append(vl.writables, vid)
vl.writableMembers[vid] = struct{}{}
return true
}
-1
View File
@@ -784,4 +784,3 @@ func TestShouldGrowVolumesByDcAndRack_WithPendingSize(t *testing.T) {
t.Error("should grow after pending pushes volume past crowded threshold")
}
}