use atomic.Bool for compaction flags to prevent concurrent vacuum races

The isCompacting and isCommitCompacting flags were plain bools
read and written from multiple goroutines without synchronization.
This allowed concurrent vacuums on the same volume to pass the
guard checks and run simultaneously, leading to the nil pointer
crash. Using atomic.Bool with CompareAndSwap ensures only one
compaction or commit can run per volume at a time.

Fixes #8591
This commit is contained in:
Chris Lu committed 2026-03-10 12:31:04 -07:00
1 parent 0b7d5aac94
commit e89768d429
4 files changed
+12 -20

No files matched your search

+1 -1
View File
@@ -383,7 +383,7 @@ func (l *DiskLocation) UnloadVolume(vid needle.VolumeId) error {
func (l *DiskLocation) unmountVolumeByCollection(collectionName string) map[needle.VolumeId]*Volume {
deltaVols := make(map[needle.VolumeId]*Volume, 0)
for k, v := range l.volumes {
if v.Collection == collectionName && !v.isCompacting && !v.isCommitCompacting {
if v.Collection == collectionName && !v.isCompacting.Load() && !v.isCommitCompacting.Load() {
deltaVols[k] = v
}
}
+4 -3
View File
@@ -5,6 +5,7 @@ import (
"path"
"strconv"
"sync"
"sync/atomic"
"time"
"github.com/seaweedfs/seaweedfs/weed/pb/master_pb"
@@ -45,8 +46,8 @@ type Volume struct {
lastCompactRevision uint16
ldbTimeout int64
isCompacting bool
isCommitCompacting bool
isCompacting atomic.Bool
isCommitCompacting atomic.Bool
volumeInfoRWLock sync.RWMutex
volumeInfo *volume_server_pb.VolumeInfo
@@ -231,7 +232,7 @@ func (v *Volume) Close() {
}
func (v *Volume) doClose() {
for v.isCommitCompacting {
for v.isCommitCompacting.Load() {
time.Sleep(521 * time.Millisecond)
glog.Warningf("Volume Close wait for compaction %d", v.Id)
}
+6 -15
View File
@@ -64,14 +64,11 @@ func (v *Volume) CompactByVolumeData(opts *CompactOptions) error {
//v.accessLock.Lock()
//defer v.accessLock.Unlock()
//glog.V(3).Infof("Got Compaction lock...")
if v.isCompacting || v.isCommitCompacting {
if v.isCommitCompacting.Load() || !v.isCompacting.CompareAndSwap(false, true) {
glog.V(0).Infof("Volume %d is already compacting...", v.Id)
return nil
}
v.isCompacting = true
defer func() {
v.isCompacting = false
}()
defer v.isCompacting.Store(false)
v.lastCompactIndexOffset = v.IndexFileSize()
v.lastCompactRevision = v.SuperBlock.CompactionRevision
@@ -102,14 +99,11 @@ func (v *Volume) CompactByIndex(opts *CompactOptions) error {
}
glog.V(3).Infof("Compact2 volume %d ...", v.Id)
if v.isCompacting || v.isCommitCompacting {
if v.isCommitCompacting.Load() || !v.isCompacting.CompareAndSwap(false, true) {
glog.V(0).Infof("Volume %d is already compacting2 ...", v.Id)
return nil
}
v.isCompacting = true
defer func() {
v.isCompacting = false
}()
defer v.isCompacting.Store(false)
v.lastCompactIndexOffset = v.IndexFileSize()
v.lastCompactRevision = v.SuperBlock.CompactionRevision
@@ -142,14 +136,11 @@ func (v *Volume) CommitCompact() error {
}
glog.V(0).Infof("Committing volume %d vacuuming...", v.Id)
if v.isCommitCompacting {
if !v.isCommitCompacting.CompareAndSwap(false, true) {
glog.V(0).Infof("Volume %d is already commit compacting ...", v.Id)
return nil
}
v.isCommitCompacting = true
defer func() {
v.isCommitCompacting = false
}()
defer v.isCommitCompacting.Store(false)
v.dataFileAccessLock.Lock()
defer v.dataFileAccessLock.Unlock()
+1 -1
View File
@@ -70,7 +70,7 @@ func (v *Volume) Destroy(onlyEmpty bool) (err error) {
return
}
}
if v.isCompacting || v.isCommitCompacting {
if v.isCompacting.Load() || v.isCommitCompacting.Load() {
err = fmt.Errorf("volume %d is compacting", v.Id)
return
}