From 32428959992779f01171e37486e3469d3ca0b5cb Mon Sep 17 00:00:00 2001 From: wei liu Date: Wed, 7 Jan 2026 10:33:25 +0800 Subject: [PATCH] fix: resolve data race in indexMeta (#46763) issue: #46762 Copy the fieldIndexes map while holding the read lock to prevent data race. The original code released the lock before iterating over the map, which could cause concurrent access issues. Affected methods: - GetSegmentIndexState - GetIndexedSegments - IsUnIndexedSegment - GetSegmentIndexedFields Signed-off-by: Wei Liu --- internal/datacoord/index_meta.go | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/internal/datacoord/index_meta.go b/internal/datacoord/index_meta.go index 982bd6769f..26ba99b804 100644 --- a/internal/datacoord/index_meta.go +++ b/internal/datacoord/index_meta.go @@ -519,12 +519,17 @@ func (m *indexMeta) GetSegmentIndexState(collID, segmentID UniqueID, indexID Uni } m.fieldIndexLock.RLock() - fieldIndexes, ok := m.indexes[collID] + fieldIndexesMap, ok := m.indexes[collID] if !ok { state.FailReason = fmt.Sprintf("collection not exist with ID: %d", collID) m.fieldIndexLock.RUnlock() return state } + // Copy the map to avoid data race after releasing the lock + fieldIndexes := make(map[UniqueID]*model.Index, len(fieldIndexesMap)) + for id, index := range fieldIndexesMap { + fieldIndexes[id] = index + } m.fieldIndexLock.RUnlock() indexes, ok := m.segmentIndexes.Get(segmentID) @@ -551,11 +556,16 @@ func (m *indexMeta) GetSegmentIndexState(collID, segmentID UniqueID, indexID Uni func (m *indexMeta) GetIndexedSegments(collectionID int64, segmentIDs, fieldIDs []UniqueID) []int64 { m.fieldIndexLock.RLock() - fieldIndexes, ok := m.indexes[collectionID] + fieldIndexesMap, ok := m.indexes[collectionID] if !ok { m.fieldIndexLock.RUnlock() return nil } + // Copy the map to avoid data race after releasing the lock + fieldIndexes := make(map[UniqueID]*model.Index, len(fieldIndexesMap)) + for id, index := range fieldIndexesMap { + fieldIndexes[id] = index + } m.fieldIndexLock.RUnlock() fieldIDSet := typeutil.NewUniqueSet(fieldIDs...) @@ -684,11 +694,16 @@ func (m *indexMeta) MarkIndexAsDeleted(ctx context.Context, collID UniqueID, ind func (m *indexMeta) IsUnIndexedSegment(collectionID UniqueID, segID UniqueID) bool { m.fieldIndexLock.RLock() - fieldIndexes, ok := m.indexes[collectionID] + fieldIndexesMap, ok := m.indexes[collectionID] if !ok { m.fieldIndexLock.RUnlock() return false } + // Copy the map to avoid data race after releasing the lock + fieldIndexes := make(map[UniqueID]*model.Index, len(fieldIndexesMap)) + for id, index := range fieldIndexesMap { + fieldIndexes[id] = index + } m.fieldIndexLock.RUnlock() // the segment should be unindexed status if the fieldIndexes is not nil @@ -1189,12 +1204,17 @@ func (m *indexMeta) GetIndexJSON(collectionID int64) string { func (m *indexMeta) GetSegmentIndexedFields(collectionID UniqueID, segmentID UniqueID) (bool, []*metricsinfo.IndexedField) { m.fieldIndexLock.RLock() - fieldIndexes, ok := m.indexes[collectionID] + fieldIndexesMap, ok := m.indexes[collectionID] if !ok { // the segment should be unindexed status if the collection has no indexes m.fieldIndexLock.RUnlock() return false, []*metricsinfo.IndexedField{} } + // Copy the map to avoid data race after releasing the lock + fieldIndexes := make(map[UniqueID]*model.Index, len(fieldIndexesMap)) + for id, index := range fieldIndexesMap { + fieldIndexes[id] = index + } m.fieldIndexLock.RUnlock() // the segment should be unindexed status if the segment indexes is not found