github.com/siglens/siglens@v0.0.0-20240328180423-f7ce9ae441ed/pkg/segment/query/metadata/metricsmetadata.go (about) 1 /* 2 Copyright 2023. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package metadata 18 19 import ( 20 "fmt" 21 "sort" 22 "sync" 23 "time" 24 25 "github.com/siglens/siglens/pkg/config" 26 "github.com/siglens/siglens/pkg/segment/reader/microreader" 27 "github.com/siglens/siglens/pkg/segment/structs" 28 "github.com/siglens/siglens/pkg/segment/utils" 29 log "github.com/sirupsen/logrus" 30 ) 31 32 /* 33 This file defines holder structs and rebalancing methods for metrics semgments 34 35 Note, the memory allocation of metricssegment is idependent of metadata.go 36 */ 37 38 /* 39 Holder 40 */ 41 type allMetricsSegmentMetadata struct { 42 43 // sortedMetricsSegmentMeta in sorted order of descending latest time, used for global memory limiting 44 sortedMetricsSegmentMeta []*MetricsSegmentMetadata 45 46 // metricsSegmentMetaMap maps a metrics base dir to its metadata 47 metricsSegmentMetaMap map[string]*MetricsSegmentMetadata 48 49 // metadata update lock 50 updateLock *sync.RWMutex 51 } 52 53 var globalMetricsMetadata *allMetricsSegmentMetadata = &allMetricsSegmentMetadata{ 54 sortedMetricsSegmentMeta: make([]*MetricsSegmentMetadata, 0), 55 metricsSegmentMetaMap: make(map[string]*MetricsSegmentMetadata), 56 updateLock: &sync.RWMutex{}, 57 } 58 59 type MetricsSegmentMetadata struct { 60 structs.MetricsMeta // original read metrics meta 61 MetricsSegmentSearchMetadata 62 } 63 64 type MetricsSegmentSearchMetadata struct { 65 mBlockSummary []*structs.MBlockSummary 66 mBlockSize uint64 67 loadedSearchMetadata bool 68 } 69 70 func BulkAddMetricsSegment(allMMetadata []*MetricsSegmentMetadata) { 71 globalMetricsMetadata.bulkAddMetricsMicroIndex(allMMetadata) 72 } 73 74 func InitMetricsMicroIndex(mMeta *structs.MetricsMeta) *MetricsSegmentMetadata { 75 76 mm := &MetricsSegmentMetadata{ 77 MetricsMeta: *mMeta, 78 } 79 mm.loadedSearchMetadata = false 80 mm.initMetadataSize() 81 return mm 82 } 83 84 func (mm *MetricsSegmentMetadata) initMetadataSize() { 85 searchMetadataSize := uint64(0) 86 searchMetadataSize += uint64(mm.NumBlocks * structs.SIZE_OF_MBSUM) // block summaries 87 mm.mBlockSize = searchMetadataSize 88 } 89 90 func (mm *allMetricsSegmentMetadata) bulkAddMetricsMicroIndex(allMMetadata []*MetricsSegmentMetadata) { 91 mm.updateLock.Lock() 92 defer mm.updateLock.Unlock() 93 94 for _, newMMeta := range allMMetadata { 95 if _, ok := mm.metricsSegmentMetaMap[newMMeta.MSegmentDir]; ok { 96 continue 97 } 98 mm.sortedMetricsSegmentMeta = append(mm.sortedMetricsSegmentMeta, newMMeta) 99 mm.metricsSegmentMetaMap[newMMeta.MSegmentDir] = newMMeta 100 GlobalSegStoreSummary.IncrementTotalMetricsSegmentCount() 101 } 102 sort.Slice(mm.sortedMetricsSegmentMeta, func(i, j int) bool { 103 return mm.sortedMetricsSegmentMeta[i].LatestEpochSec > mm.sortedMetricsSegmentMeta[j].LatestEpochSec 104 }) 105 } 106 107 func (mm *allMetricsSegmentMetadata) rebalanceMetricsSsm(ssmSizeBytes uint64) { 108 109 sTime := time.Now() 110 111 mm.updateLock.RLock() 112 defer mm.updateLock.RUnlock() 113 searchIndex := mm.getSsmMaxIndicesToLoad(ssmSizeBytes) 114 evicted := mm.evictSsmPastIndices(searchIndex) 115 116 inMemSize, inMemSearchMetaCount, newloaded := mm.loadSsmUntilIndex(searchIndex) 117 118 log.Infof("rebalanceMetricsSsm SSM, inMem: %+v SSM, allocated: %+v MB, evicted: %v, newloaded: %v, took: %vms", 119 inMemSearchMetaCount, utils.ConvertUintBytesToMB(inMemSize), 120 evicted, newloaded, int(time.Since(sTime).Milliseconds())) 121 122 GlobalSegStoreSummary.SetInMemoryMetricsSearchmetadataCount(uint64(inMemSearchMetaCount)) 123 GlobalSegStoreSummary.SetInMemoryMetricsSsmSizeMB(utils.ConvertUintBytesToMB(inMemSize)) 124 } 125 126 /* 127 Returns the max indices that should have SSM(segmentsearchmeta) loaded 128 */ 129 func (mm *allMetricsSegmentMetadata) getSsmMaxIndicesToLoad(totalMem uint64) int { 130 131 numBlocks := len(mm.sortedMetricsSegmentMeta) 132 totalMBAllocated := uint64(0) 133 maxSearchIndex := 0 134 for ; maxSearchIndex < numBlocks; maxSearchIndex++ { 135 searchMetadataSize := mm.sortedMetricsSegmentMeta[maxSearchIndex].mBlockSize 136 if searchMetadataSize+totalMBAllocated > totalMem { 137 break 138 } 139 totalMBAllocated += searchMetadataSize 140 } 141 142 return maxSearchIndex 143 } 144 145 func (mm *allMetricsSegmentMetadata) evictSsmPastIndices(searchIndex int) int { 146 147 idxToClear := make([]int, 0) 148 for i := searchIndex; i < len(mm.sortedMetricsSegmentMeta); i++ { 149 if mm.sortedMetricsSegmentMeta[i].loadedSearchMetadata { 150 idxToClear = append(idxToClear, i) 151 } 152 } 153 154 if len(idxToClear) > 0 { 155 for _, idx := range idxToClear { 156 mm.sortedMetricsSegmentMeta[idx].clearSearchMetadata() 157 } 158 } 159 return len(idxToClear) 160 } 161 162 // Returns total in memory size in bytes, total search metadata in memory 163 func (mm *allMetricsSegmentMetadata) loadSsmUntilIndex(searchMetaIdx int) (uint64, int, int) { 164 totalSize := uint64(0) 165 totalSearchMetaCount := int(0) 166 167 idxToLoad := make([]int, 0) 168 for i := 0; i < searchMetaIdx; i++ { 169 if !mm.sortedMetricsSegmentMeta[i].loadedSearchMetadata { 170 idxToLoad = append(idxToLoad, i) 171 } else { 172 totalSize += mm.sortedMetricsSegmentMeta[i].mBlockSize 173 totalSearchMetaCount += 1 174 } 175 } 176 177 if len(idxToLoad) > 0 { 178 a, b := mm.loadParallel(idxToLoad) 179 totalSize += a 180 totalSearchMetaCount += b 181 } 182 183 return totalSize, totalSearchMetaCount, len(idxToLoad) 184 } 185 186 func (mm *MetricsSegmentMetadata) LoadSearchMetadata() error { 187 if mm.loadedSearchMetadata { 188 return nil 189 } 190 bSumFname := fmt.Sprintf("%s.mbsu", mm.MSegmentDir) 191 blockSum, err := microreader.ReadMetricsBlockSummaries(bSumFname) 192 if err != nil { 193 mm.clearSearchMetadata() 194 log.Errorf("LoadSearchMetadata: unable to read the metrics block summaries. Error: %v", err) 195 return err 196 } 197 mm.loadedSearchMetadata = true 198 mm.mBlockSummary = blockSum 199 return nil 200 } 201 202 func (mm *MetricsSegmentMetadata) clearSearchMetadata() { 203 mm.loadedSearchMetadata = false 204 mm.mBlockSummary = nil 205 } 206 207 /* 208 Caller is responsible for acquiring the right read locks 209 210 Parameters: 211 212 idxToLoad: indices in the sortedMetricsSegmentMeta to load metrics block summary for 213 214 Returns: 215 216 totalSize: 217 totalEntities: 218 */ 219 func (mm *allMetricsSegmentMetadata) loadParallel(idxToLoad []int) (uint64, int) { 220 totalSize := uint64(0) 221 totalEntities := int(0) 222 223 wg := &sync.WaitGroup{} 224 var err error 225 parallelism := int(config.GetParallelism()) 226 227 for i, idx := range idxToLoad { 228 wg.Add(1) 229 go func(myIdx int, rbufIdx int) { 230 err = mm.sortedMetricsSegmentMeta[myIdx].LoadSearchMetadata() 231 if err != nil { 232 log.Errorf("loadParallel: failed to load SSM at index %d. Error: %v", myIdx, err) 233 } 234 wg.Done() 235 }(idx, i%parallelism) 236 if i%parallelism == 0 { 237 wg.Wait() 238 } 239 } 240 wg.Wait() 241 242 for _, idx := range idxToLoad { 243 if mm.sortedMetricsSegmentMeta[idx].loadedSearchMetadata { 244 totalSize += mm.sortedMetricsSegmentMeta[idx].mBlockSize 245 totalEntities += 1 246 } 247 } 248 return totalSize, totalEntities 249 } 250 251 func DeleteMetricsSegmentKey(dirPath string) error { 252 err := globalMetricsMetadata.deleteMetricsSegmentKey(dirPath) 253 if err != nil { 254 log.Errorf("DeleteMetricsSegmentKey: err deleting the metrics segment %v. Error:%v", dirPath, err) 255 return err 256 } 257 return nil 258 } 259 260 func (hm *allMetricsSegmentMetadata) deleteMetricsSegmentKey(dirPath string) error { 261 hm.updateLock.Lock() 262 defer hm.updateLock.Unlock() 263 264 segment, ok := globalMetricsMetadata.metricsSegmentMetaMap[dirPath] 265 if !ok { 266 return fmt.Errorf("deleteSegmentKey: metrics segment directory %s not found", dirPath) 267 } 268 269 for i, m := range globalMetricsMetadata.sortedMetricsSegmentMeta { 270 if m == segment { 271 globalMetricsMetadata.sortedMetricsSegmentMeta = append(globalMetricsMetadata.sortedMetricsSegmentMeta[:i], globalMetricsMetadata.sortedMetricsSegmentMeta[i+1:]...) 272 break 273 } 274 } 275 delete(globalMetricsMetadata.metricsSegmentMetaMap, dirPath) 276 277 GlobalSegStoreSummary.DecrementTotalMetricsSegmentCount() 278 279 return nil 280 }