mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-08-16 04:06:44 +00:00
master: count only writable volumes as crowded when deciding growth (#10522)
The crowded map keeps volumes that later became unwritable, so their state survives transient writability flips without flapping. But volumes packed to capacity (fs.mergeVolumes) or turned read-only stay above the crowded threshold and get re-marked on every heartbeat, so the raw map size can permanently exceed the writable count. ShouldGrowVolumes then returns true forever, every assign-path grow request passes the gate, and the periodic grow loop fires too, creating volumes without bound -- worse with -volumePreallocate. Count crowded as the intersection with writables instead: growth checks and the layout gauges only see crowded volumes that can still take writes.
This commit is contained in:
@@ -783,7 +783,17 @@ func ceilDiv(a, b uint32) uint32 {
|
||||
func (vl *VolumeLayout) GetWritableVolumeCount() (active, crowded int) {
|
||||
vl.accessLock.RLock()
|
||||
defer vl.accessLock.RUnlock()
|
||||
return len(vl.writables), len(vl.crowded)
|
||||
// The crowded map retains volumes that later became unwritable (full,
|
||||
// read-only), so their state survives transient writability flips. Count
|
||||
// only the writable ones: growth decisions compare crowded against
|
||||
// writables, and a raw len(vl.crowded) can exceed len(vl.writables)
|
||||
// permanently, demanding growth forever.
|
||||
for _, vid := range vl.writables {
|
||||
if _, ok := vl.crowded[vid]; ok {
|
||||
crowded++
|
||||
}
|
||||
}
|
||||
return len(vl.writables), crowded
|
||||
}
|
||||
|
||||
func (vl *VolumeLayout) CloneWritableVolumes() (writables []needle.VolumeId) {
|
||||
|
||||
@@ -240,6 +240,35 @@ func TestPlanRackAwareGrowth_EvenDistributionAcrossUnevenDCs(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// Volumes packed to capacity (e.g. by fs.mergeVolumes) go crowded and then
|
||||
// unwritable, but stay in the crowded map. ShouldGrowVolumes must count only
|
||||
// writable crowded volumes, or those leftovers keep writable <= crowded true
|
||||
// forever and every assign-path grow request passes the gate.
|
||||
func TestShouldGrowVolumes_UnwritableCrowdedVolumes(t *testing.T) {
|
||||
rp, _ := super_block.NewReplicaPlacementFromString("000")
|
||||
vl := NewVolumeLayout(rp, needle.EMPTY_TTL, types.HardDriveType, 30000, false)
|
||||
|
||||
vl.accessLock.Lock()
|
||||
vl.setVolumeWritable(1)
|
||||
vl.setVolumeWritable(2)
|
||||
vl.accessLock.Unlock()
|
||||
|
||||
vl.SetVolumeCrowded(1)
|
||||
vl.SetVolumeCapacityFull(1)
|
||||
|
||||
if _, crowded := vl.GetWritableVolumeCount(); crowded != 0 {
|
||||
t.Fatalf("expected 0 writable crowded volumes, got %d", crowded)
|
||||
}
|
||||
if vl.ShouldGrowVolumes() {
|
||||
t.Fatal("volume 2 still has room, growth is not needed")
|
||||
}
|
||||
|
||||
vl.SetVolumeCrowded(2)
|
||||
if !vl.ShouldGrowVolumes() {
|
||||
t.Fatal("every writable volume is crowded, growth is needed")
|
||||
}
|
||||
}
|
||||
|
||||
func restoreCopyCounts(copy1, copy2 uint32) {
|
||||
VolumeGrowStrategy.Copy1Count = copy1
|
||||
VolumeGrowStrategy.Copy2Count = copy2
|
||||
|
||||
Reference in New Issue
Block a user