fix: CompactMap race condition causing runtime panic (#8029)
Fixed critical race condition in CompactMap where Set(), Delete(), and Get() methods had issues with concurrent map access. Root cause: segmentForKey() can create new map segments, which modifies the cm.segments map. Calling this under a read lock caused concurrent map write panics when multiple goroutines accessed the map simultaneously (e.g., during VolumeNeedleStatus gRPC calls). Changes: - Set() method: Changed RLock/RUnlock to Lock/Unlock - Delete() method: Changed RLock/RUnlock to Lock/Unlock, optimized to avoid creating empty segments when key doesn't exist - Get() method: Removed segmentForKey() call to avoid race condition, now checks segment existence directly and returns early if segment doesn't exist (optimization: avoids unnecessary segment creation) This fix resolves the runtime/maps.fatal panic that occurred under concurrent load. Tested with race detector: go test -v -race ./weed/storage/needle_map/...
This commit is contained in:
@@ -236,8 +236,8 @@ func (cm *CompactMap) segmentForKey(key types.NeedleId) *CompactMapSegment {
|
||||
// Set inserts/updates a NeedleValue.
|
||||
// If the operation is an update, returns the overwritten value's previous offset and size.
|
||||
func (cm *CompactMap) Set(key types.NeedleId, offset types.Offset, size types.Size) (oldOffset types.Offset, oldSize types.Size) {
|
||||
cm.RLock()
|
||||
defer cm.RUnlock()
|
||||
cm.Lock()
|
||||
defer cm.Unlock()
|
||||
|
||||
cs := cm.segmentForKey(key)
|
||||
return cs.set(key, offset, size)
|
||||
@@ -248,7 +248,11 @@ func (cm *CompactMap) Get(key types.NeedleId) (*NeedleValue, bool) {
|
||||
cm.RLock()
|
||||
defer cm.RUnlock()
|
||||
|
||||
cs := cm.segmentForKey(key)
|
||||
chunk := Chunk(key / SegmentChunkSize)
|
||||
cs, ok := cm.segments[chunk]
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
if cnv, found := cs.get(key); found {
|
||||
nv := cnv.NeedleValue(cs.chunk)
|
||||
return &nv, true
|
||||
@@ -258,10 +262,14 @@ func (cm *CompactMap) Get(key types.NeedleId) (*NeedleValue, bool) {
|
||||
|
||||
// Delete deletes a map entry by key. Returns the entries' previous Size, if available.
|
||||
func (cm *CompactMap) Delete(key types.NeedleId) types.Size {
|
||||
cm.RLock()
|
||||
defer cm.RUnlock()
|
||||
cm.Lock()
|
||||
defer cm.Unlock()
|
||||
|
||||
cs := cm.segmentForKey(key)
|
||||
chunk := Chunk(key / SegmentChunkSize)
|
||||
cs, ok := cm.segments[chunk]
|
||||
if !ok {
|
||||
return types.Size(0)
|
||||
}
|
||||
return cs.delete(key)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user