Commit Graph
2 Commits
Author SHA1 Message Date
Chris Lu 5d5ea18287 topology: fix fatal concurrent map read/write on VolumeLayout.crowded (#11216)
SetVolumeCrowded mutated the crowded map under accessLock.RLock(), while
GetWritableVolumeCount reads the same map under RLock() on the Assign hot
path. Two concurrent RLock holders with one writing and one reading the
map triggers a fatal "concurrent map read and map write" that kills the
master process (unrecoverable, bypasses recover).

Take the write lock in SetVolumeCrowded instead. This event path is a
low-frequency single consumer driven by the crowded-volume event loop,
and every other mutation of crowded already holds Lock(); setVolumeCrowded
takes no nested locks, so there is no deadlock path. The hot readers
(GetWritableVolumeCount, CloneWritableVolumes) keep using RLock.

Adds a -race regression test that fails (race detected) on the old RLock
and passes with the write lock.

Fixes #11211
2026-09-07 13:47:22 -07:00
Chris Lu 25d7f62749 topology: mark a volume crowded only if it can take writes (#10655)
* topology: mark a volume crowded only if it can take writes

Crowding asks for more room to write into, and the writable-volume refresh loop
marked anything past the threshold regardless of whether writes could land
there. Growth already discounts those by intersecting the crowded set with the
writable list, so the entries changed no decision and only took space -- in a
tiered cluster, one for nearly every volume.

* topology: wait for the crowded-volume collector before reading what it saw

Closing the stop channel does not order the collector's writes against the
test's reads.

* topology: drop the sleep from the crowded-volume test

The channels are unbuffered, so every send has been received by the time the
sweep returns, and waiting for the collector covers the recording. The sleep
only suggested the result turned on timing.
2026-08-08 18:44:15 -07:00