github.com/siglens/siglens@v0.0.0-20240328180423-f7ce9ae441ed/pkg/segment/writer/unrotatedquery.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 writer 18 19 import ( 20 "errors" 21 "fmt" 22 "sort" 23 "sync" 24 "sync/atomic" 25 26 dtu "github.com/siglens/siglens/pkg/common/dtypeutils" 27 "github.com/siglens/siglens/pkg/segment/pqmr" 28 "github.com/siglens/siglens/pkg/segment/query/metadata/metautils" 29 "github.com/siglens/siglens/pkg/segment/structs" 30 segutils "github.com/siglens/siglens/pkg/segment/utils" 31 "github.com/siglens/siglens/pkg/utils" 32 log "github.com/sirupsen/logrus" 33 ) 34 35 type UnrotatedSegmentInfo struct { 36 blockSummaries []*structs.BlockSummary 37 unrotatedPQSResults map[string]*pqmr.SegmentPQMRResults // maps qid to results 38 blockInfo map[uint16]*structs.BlockMetadataHolder 39 allColumns map[string]bool 40 unrotatedBlockCmis []map[string]*structs.CmiContainer 41 tsRange *dtu.TimeRange 42 TableName string 43 searchMetadataSize uint64 // size of blockSummaries & blockInfo 44 cmiSize uint64 // size of UnrotatedBlockCmis 45 isCmiLoaded bool // is UnrotatedBlockCmis loaded? 46 RecordCount int 47 orgid uint64 48 } 49 50 var UnrotatedInfoLock sync.RWMutex = sync.RWMutex{} 51 var AllUnrotatedSegmentInfo = map[string]*UnrotatedSegmentInfo{} 52 var RecentlyRotatedSegmentFiles = map[string]*SegfileRotateInfo{} 53 var recentlyRotatedSegmentFilesLock sync.RWMutex = sync.RWMutex{} 54 var TotalUnrotatedMetadataSizeBytes uint64 55 56 func GetSizeOfUnrotatedMetadata() uint64 { 57 return TotalUnrotatedMetadataSizeBytes 58 } 59 60 // Removed unrotated metadata from in memory based on the available size and return the new in memory size 61 // Currently, once we remove an entry we have no way of adding it back 62 // TODO: improve on re-loading of unrotated microindices 63 func RebalanceUnrotatedMetadata(totalAvailableSize uint64) uint64 { 64 UnrotatedInfoLock.Lock() 65 defer UnrotatedInfoLock.Unlock() 66 if TotalUnrotatedMetadataSizeBytes <= totalAvailableSize { 67 return TotalUnrotatedMetadataSizeBytes 68 } 69 sizeToRemove := TotalUnrotatedMetadataSizeBytes - totalAvailableSize 70 ss := make([]*UnrotatedSegmentInfo, len(AllUnrotatedSegmentInfo)) 71 idx := 0 72 for _, v := range AllUnrotatedSegmentInfo { 73 ss[idx] = v 74 idx++ 75 } 76 77 sort.Slice(ss, func(i, j int) bool { 78 return ss[i].cmiSize < ss[j].cmiSize 79 }) 80 removedSize := uint64(0) 81 count := 0 82 for i := 0; i < len(ss); i++ { 83 if removedSize >= sizeToRemove { 84 break 85 } 86 87 if ss[i].isCmiLoaded { 88 removedSize += ss[i].removeInMemoryMetadata() 89 count++ 90 } 91 } 92 var finalSize uint64 93 if TotalUnrotatedMetadataSizeBytes > removedSize { 94 finalSize = TotalUnrotatedMetadataSizeBytes - removedSize 95 } else { 96 finalSize = 0 97 } 98 99 atomic.StoreUint64(&TotalUnrotatedMetadataSizeBytes, finalSize) 100 log.Infof("RebalanceUnrotatedMetadata: Unrotated data was allocated %v MB. Removed %+v MB of unrotated metadata after rebalance", 101 segutils.ConvertUintBytesToMB(totalAvailableSize), segutils.ConvertUintBytesToMB(removedSize)) 102 log.Infof("RebalanceUnrotatedMetadata: Final Unrotated metadata in memory size: %v MB", 103 segutils.ConvertUintBytesToMB(TotalUnrotatedMetadataSizeBytes)) 104 return finalSize 105 } 106 107 func removeSegKeyFromUnrotatedInfo(segkey string) { 108 UnrotatedInfoLock.Lock() 109 defer UnrotatedInfoLock.Unlock() 110 var allResults *UnrotatedSegmentInfo 111 var exists bool 112 if allResults, exists = AllUnrotatedSegmentInfo[segkey]; !exists { 113 return 114 } 115 delete(AllUnrotatedSegmentInfo, segkey) 116 removedSize := allResults.getInMemorySize() 117 118 if TotalUnrotatedMetadataSizeBytes > removedSize { 119 atomic.AddUint64(&TotalUnrotatedMetadataSizeBytes, ^uint64(removedSize-1)) 120 } else { 121 atomic.StoreUint64(&TotalUnrotatedMetadataSizeBytes, 0) 122 } 123 } 124 125 func updateRecentlyRotatedSegmentFiles(segkey string, finalKey string) { 126 recentlyRotatedSegmentFilesLock.Lock() 127 RecentlyRotatedSegmentFiles[segkey] = &SegfileRotateInfo{ 128 FinalName: finalKey, 129 TimeRotated: utils.GetCurrentTimeInMs(), 130 } 131 recentlyRotatedSegmentFilesLock.Unlock() 132 } 133 134 func updateUnrotatedBlockInfo(segkey string, virtualTable string, wipBlock *WipBlock, 135 blockMetadata *structs.BlockMetadataHolder, allCols map[string]bool, blockIdx uint16, 136 metadataSize uint64, earliestTs uint64, latestTs uint64, recordCount int, orgid uint64) { 137 UnrotatedInfoLock.Lock() 138 defer UnrotatedInfoLock.Unlock() 139 140 blkSumCpy := wipBlock.blockSummary.Copy() 141 tRange := &dtu.TimeRange{StartEpochMs: earliestTs, EndEpochMs: latestTs} 142 if _, ok := AllUnrotatedSegmentInfo[segkey]; !ok { 143 AllUnrotatedSegmentInfo[segkey] = &UnrotatedSegmentInfo{ 144 blockSummaries: make([]*structs.BlockSummary, 0), 145 blockInfo: make(map[uint16]*structs.BlockMetadataHolder), 146 allColumns: make(map[string]bool), 147 unrotatedBlockCmis: make([]map[string]*structs.CmiContainer, 0), 148 unrotatedPQSResults: make(map[string]*pqmr.SegmentPQMRResults), 149 TableName: virtualTable, 150 isCmiLoaded: true, // default loading is true 151 orgid: orgid, 152 } 153 } 154 AllUnrotatedSegmentInfo[segkey].blockSummaries = append(AllUnrotatedSegmentInfo[segkey].blockSummaries, blkSumCpy) 155 AllUnrotatedSegmentInfo[segkey].blockInfo[blockIdx] = blockMetadata 156 AllUnrotatedSegmentInfo[segkey].tsRange = tRange 157 AllUnrotatedSegmentInfo[segkey].RecordCount = recordCount 158 159 for col := range allCols { 160 AllUnrotatedSegmentInfo[segkey].allColumns[col] = true 161 } 162 163 var pqidSize uint64 164 if AllUnrotatedSegmentInfo[segkey].isCmiLoaded { 165 AllUnrotatedSegmentInfo[segkey].addMicroIndicesToUnrotatedInfo(blockIdx, wipBlock.columnBlooms, wipBlock.columnRangeIndexes) 166 pqidSize = AllUnrotatedSegmentInfo[segkey].addUnrotatedQIDInfo(blockIdx, wipBlock.pqMatches) 167 } 168 169 blkSumSize := blkSumCpy.GetSize() 170 newSearchMetadataSize := blkSumSize + pqidSize 171 AllUnrotatedSegmentInfo[segkey].searchMetadataSize += newSearchMetadataSize 172 AllUnrotatedSegmentInfo[segkey].cmiSize += metadataSize 173 174 totalSizeAdded := newSearchMetadataSize + metadataSize 175 atomic.AddUint64(&TotalUnrotatedMetadataSizeBytes, totalSizeAdded) 176 } 177 178 func GetFileNameForRotatedSegment(seg string) (string, error) { 179 recentlyRotatedSegmentFilesLock.RLock() 180 defer recentlyRotatedSegmentFilesLock.RUnlock() 181 newName, ok := RecentlyRotatedSegmentFiles[seg] 182 if !ok { 183 return "", errors.New("file was not recently rotated") 184 } 185 return newName.FinalName, nil 186 } 187 188 func IsRecentlyRotatedSegKey(key string) bool { 189 recentlyRotatedSegmentFilesLock.RLock() 190 _, ok := RecentlyRotatedSegmentFiles[key] 191 recentlyRotatedSegmentFilesLock.RUnlock() 192 return ok 193 } 194 195 func IsSegKeyUnrotated(key string) bool { 196 UnrotatedInfoLock.RLock() 197 _, ok := AllUnrotatedSegmentInfo[key] 198 UnrotatedInfoLock.RUnlock() 199 return ok 200 } 201 202 // Returns a copy of AllColumns seen for a given key from the unrotated segment infos 203 // If no key exists, returns an error 204 func CheckAndGetColsForUnrotatedSegKey(key string) (map[string]bool, bool) { 205 UnrotatedInfoLock.RLock() 206 defer UnrotatedInfoLock.RUnlock() 207 cols, keyOk := AllUnrotatedSegmentInfo[key] 208 if !keyOk { 209 return nil, false 210 } 211 colsCopy := make(map[string]bool, len(cols.allColumns)) 212 for colName := range cols.allColumns { 213 colsCopy[colName] = true 214 } 215 return colsCopy, true 216 } 217 218 // returns a copy of the unrotated block search info. This is to prevent concurrent modification 219 func GetBlockSearchInfoForKey(key string) (map[uint16]*structs.BlockMetadataHolder, error) { 220 UnrotatedInfoLock.RLock() 221 defer UnrotatedInfoLock.RUnlock() 222 223 segInfo, keyOk := AllUnrotatedSegmentInfo[key] 224 if !keyOk { 225 return nil, errors.New("failed to get block search info for key") 226 } 227 228 retVal := make(map[uint16]*structs.BlockMetadataHolder) 229 for blkNum, blkInfo := range segInfo.blockInfo { 230 retVal[blkNum] = blkInfo 231 } 232 return retVal, nil 233 } 234 235 // returns the block summary for a segment key 236 func GetBlockSummaryForKey(key string) ([]*structs.BlockSummary, error) { 237 UnrotatedInfoLock.RLock() 238 defer UnrotatedInfoLock.RUnlock() 239 240 segInfo, keyOk := AllUnrotatedSegmentInfo[key] 241 if !keyOk { 242 return nil, errors.New("failed to get block search info for key") 243 } 244 return segInfo.blockSummaries, nil 245 } 246 247 func (usi *UnrotatedSegmentInfo) resizeUnrotatedBlockCmis(blkNum uint16) { 248 249 minBlocks := blkNum + 1 250 if numBlks := uint16(len(usi.unrotatedBlockCmis)); minBlocks > numBlks { 251 numToAdd := minBlocks - numBlks 252 newSlice := make([]map[string]*structs.CmiContainer, numToAdd) 253 for i := uint16(0); i < numToAdd; i++ { 254 newSlice[i] = make(map[string]*structs.CmiContainer) 255 } 256 usi.unrotatedBlockCmis = append(usi.unrotatedBlockCmis, newSlice...) 257 } 258 } 259 260 // add microindices to the UnrotatedBlockCmis. This function will only create copies of inputted microindices 261 func (usi *UnrotatedSegmentInfo) addMicroIndicesToUnrotatedInfo(blkNum uint16, columnBlooms map[string]*BloomIndex, 262 columnRangeIndices map[string]*RangeIndex) { 263 264 usi.resizeUnrotatedBlockCmis(blkNum) 265 for colName, colBloom := range columnBlooms { 266 if _, ok := usi.unrotatedBlockCmis[blkNum][colName]; !ok { 267 usi.unrotatedBlockCmis[blkNum][colName] = &structs.CmiContainer{} 268 } 269 usi.unrotatedBlockCmis[blkNum][colName].CmiType = segutils.CMI_BLOOM_INDEX[0] 270 usi.unrotatedBlockCmis[blkNum][colName].Bf = colBloom.Bf.Copy() 271 } 272 for colName, colRange := range columnRangeIndices { 273 if _, ok := usi.unrotatedBlockCmis[blkNum][colName]; !ok { 274 usi.unrotatedBlockCmis[blkNum][colName] = &structs.CmiContainer{} 275 usi.unrotatedBlockCmis[blkNum][colName].CmiType = segutils.CMI_RANGE_INDEX[0] 276 } 277 newRange := colRange.copyRangeIndex() 278 usi.unrotatedBlockCmis[blkNum][colName].Ranges = newRange.Ranges 279 } 280 } 281 282 // add pqs results to UnrotatedSegmentInfo . This function will only create copies of inputted pqs results 283 func (usi *UnrotatedSegmentInfo) addUnrotatedQIDInfo(blkNum uint16, pqMatches map[string]*pqmr.PQMatchResults) uint64 { 284 285 var totalSize uint64 286 for qid, pqMatch := range pqMatches { 287 sResults, ok := usi.unrotatedPQSResults[qid] 288 if !ok { 289 sResults = pqmr.InitSegmentPQMResults() 290 usi.unrotatedPQSResults[qid] = sResults 291 } 292 newSize := sResults.CopyBlockResults(blkNum, pqMatch) 293 totalSize += newSize 294 } 295 return totalSize 296 } 297 298 func (ri *RangeIndex) copyRangeIndex() *RangeIndex { 299 finalRanges := make(map[string]*structs.Numbers) 300 for colName, colRange := range ri.Ranges { 301 finalRanges[colName] = colRange.Copy() 302 } 303 return &RangeIndex{Ranges: finalRanges} 304 } 305 306 // does CMI check on unrotated segment info for inputted request. Assumes UnrotatedInfoLock has been acquired 307 // returns the final blocks to search, total unrotated blocks, num filtered blocks, and errors if any 308 func (usi *UnrotatedSegmentInfo) DoCMICheckForUnrotated(currQuery *structs.SearchQuery, tRange *dtu.TimeRange, 309 blkTracker *structs.BlockTracker, bloomWords map[string]bool, bloomOp segutils.LogicalOperator, rangeFilter map[string]string, 310 rangeOp segutils.FilterOperator, isRange bool, wildcardValue bool, 311 qid uint64) (map[uint16]map[string]bool, uint64, uint64, error) { 312 313 timeFilteredBlocks := metautils.FilterBlocksByTime(usi.blockSummaries, blkTracker, tRange) 314 totalPossibleBlocks := uint64(len(usi.blockSummaries)) 315 316 if len(timeFilteredBlocks) == 0 { 317 return timeFilteredBlocks, totalPossibleBlocks, 0, nil 318 } 319 320 colsToCheck, wildcardColQuery := currQuery.GetAllColumnsInQuery() 321 if wildcardColQuery { 322 colsToCheck = usi.allColumns 323 } 324 var err error 325 if isRange { 326 err = usi.doRangeCheckForCols(timeFilteredBlocks, rangeFilter, rangeOp, colsToCheck, qid) 327 } else if !wildcardValue { 328 err = usi.doBloomCheckForCols(timeFilteredBlocks, bloomWords, bloomOp, colsToCheck, qid) 329 } 330 331 numFinalBlocks := uint64(len(timeFilteredBlocks)) 332 if err != nil { 333 log.Errorf("DoCMICheckForUnrotated: failed to do cmi check for unrotated segments: %v", err) 334 return timeFilteredBlocks, totalPossibleBlocks, numFinalBlocks, err 335 } 336 return timeFilteredBlocks, totalPossibleBlocks, numFinalBlocks, nil 337 } 338 339 func (usi *UnrotatedSegmentInfo) doRangeCheckForCols(timeFilteredBlocks map[uint16]map[string]bool, 340 rangeFilter map[string]string, rangeOp segutils.FilterOperator, 341 colsToCheck map[string]bool, qid uint64) error { 342 343 if !usi.isCmiLoaded { 344 return nil 345 } 346 numUnrotatedBlks := uint16(len(usi.unrotatedBlockCmis)) 347 for blkNum := range timeFilteredBlocks { 348 if blkNum > numUnrotatedBlks { 349 log.Errorf("DoRangeCheckAllCol: tried to check a block that does not exist in unrotated info. blkNum %+v, numBlocks %+v", 350 blkNum, numUnrotatedBlks) 351 continue 352 } 353 currInfo := usi.unrotatedBlockCmis[blkNum] 354 var matchedBlockRange bool 355 for col := range colsToCheck { 356 var cmi *structs.CmiContainer 357 var ok bool 358 if cmi, ok = currInfo[col]; !ok { 359 continue 360 } 361 if cmi.Ranges == nil { 362 continue 363 } 364 matchedBlockRange = metautils.CheckRangeIndex(rangeFilter, cmi.Ranges, rangeOp, qid) 365 if matchedBlockRange { 366 timeFilteredBlocks[blkNum][col] = true 367 } 368 } 369 if !matchedBlockRange { 370 delete(timeFilteredBlocks, blkNum) 371 } 372 } 373 return nil 374 } 375 376 func (usi *UnrotatedSegmentInfo) doBloomCheckForCols(timeFilteredBlocks map[uint16]map[string]bool, 377 bloomKeys map[string]bool, bloomOp segutils.LogicalOperator, 378 colsToCheck map[string]bool, qid uint64) error { 379 380 if !usi.isCmiLoaded { 381 return nil 382 } 383 numUnrotatedBlks := uint16(len(usi.unrotatedBlockCmis)) 384 for blkNum := range timeFilteredBlocks { 385 if blkNum > numUnrotatedBlks { 386 log.Errorf("doBloomCheckForCols: tried to check a block that does not exist in unrotated info. blkNum %+v, numBlocks %+v", 387 blkNum, numUnrotatedBlks) 388 continue 389 } 390 currInfo := usi.unrotatedBlockCmis[blkNum] 391 var matchedNeedleInBlock = true 392 var allEntriesMissing bool = false 393 for entry := range bloomKeys { 394 var atLeastOneFound bool 395 for col := range colsToCheck { 396 cmi, ok := currInfo[col] 397 if !ok { 398 continue 399 } 400 if cmi.Bf == nil { 401 continue 402 } 403 needleExists := cmi.Bf.TestString(entry) 404 if needleExists { 405 atLeastOneFound = true 406 timeFilteredBlocks[blkNum][col] = true 407 } 408 } 409 if !atLeastOneFound && bloomOp == segutils.And { 410 matchedNeedleInBlock = false 411 break 412 } else if atLeastOneFound && bloomOp == segutils.Or { 413 allEntriesMissing = false 414 matchedNeedleInBlock = true 415 break 416 } else if !atLeastOneFound && bloomOp == segutils.Or { 417 allEntriesMissing = true 418 matchedNeedleInBlock = false 419 } 420 } 421 422 // Or only early exits when it sees true. If all entries are false, we need to handle it here 423 if bloomOp == segutils.Or && allEntriesMissing && !matchedNeedleInBlock { 424 matchedNeedleInBlock = false 425 } 426 427 if !matchedNeedleInBlock { 428 delete(timeFilteredBlocks, blkNum) 429 } 430 } 431 return nil 432 } 433 434 /* 435 For a unrotated segment info, return []blockSummaries, map[uint16]*structs.BlockMetadataHolder, and map[string]bool (all columns) 436 This information will be used for unrotated queries. This will return copies of in memory metadata to avoid race conditions 437 438 # A copy needs to be returned here as usi.BlockSummaries and usi.BlockInfo may have concurrent writes 439 440 This assumes the caller has already acquired the lock on UnrotatedInfoLock 441 */ 442 func (usi *UnrotatedSegmentInfo) GetUnrotatedBlockInfoForQuery() ([]*structs.BlockSummary, map[uint16]*structs.BlockMetadataHolder, map[string]bool) { 443 444 retBlkSum := make([]*structs.BlockSummary, len(usi.blockSummaries)) 445 for i := 0; i < len(usi.blockSummaries); i++ { 446 retBlkSum[i] = usi.blockSummaries[i].Copy() 447 } 448 449 retBlkInfo := make(map[uint16]*structs.BlockMetadataHolder, len(usi.blockInfo)) 450 for k, v := range usi.blockInfo { 451 retBlkInfo[k] = v 452 } 453 retBlkCols := make(map[string]bool, len(usi.allColumns)) 454 for k, v := range usi.allColumns { 455 retBlkCols[k] = v 456 } 457 458 return retBlkSum, retBlkInfo, retBlkCols 459 } 460 461 /* 462 For a unrotated segment info, remove all microindices from in memory and set usi.loaded = False 463 Only usi.UnrotatedBlockCmis will be removed from in memory 464 465 # This function does not use locks so it is up to the caller to protect concurrent access 466 467 Returns the size removed 468 */ 469 func (usi *UnrotatedSegmentInfo) removeInMemoryMetadata() uint64 { 470 471 if usi.isCmiLoaded { 472 usi.isCmiLoaded = false 473 usi.unrotatedBlockCmis = make([]map[string]*structs.CmiContainer, 0) 474 return usi.cmiSize 475 } 476 return 0 477 } 478 479 /* 480 Returns the in memory size of a UnrotatedSegmentInfo 481 */ 482 func (usi *UnrotatedSegmentInfo) getInMemorySize() uint64 { 483 484 size := uint64(0) 485 if usi.isCmiLoaded { 486 size += usi.cmiSize 487 } 488 size += usi.searchMetadataSize 489 490 return size 491 } 492 493 /* 494 Returns number of loaded unrotated metadata, and total number of unrotated metadata 495 */ 496 func GetUnrotatedMetadataInfo() (uint64, uint64) { 497 UnrotatedInfoLock.RLock() 498 defer UnrotatedInfoLock.RUnlock() 499 loaded := uint64(0) 500 for _, usi := range AllUnrotatedSegmentInfo { 501 if usi.isCmiLoaded { 502 loaded++ 503 } 504 } 505 return loaded, uint64(len(AllUnrotatedSegmentInfo)) 506 } 507 508 // returns map[table]->map[segKey]->timeRange that pass index & time range check, total checked, total passed 509 func FilterUnrotatedSegmentsInQuery(timeRange *dtu.TimeRange, indexNames []string, orgid uint64) (map[string]map[string]*dtu.TimeRange, uint64, uint64) { 510 totalCount := uint64(0) 511 totalChecked := uint64(0) 512 retVal := make(map[string]map[string]*dtu.TimeRange) 513 514 UnrotatedInfoLock.RLock() 515 defer UnrotatedInfoLock.RUnlock() 516 for segKey, usi := range AllUnrotatedSegmentInfo { 517 var foundIndex bool 518 for _, idxName := range indexNames { 519 if idxName == usi.TableName { 520 foundIndex = true 521 break 522 } 523 } 524 if !foundIndex { 525 continue 526 } 527 totalChecked++ 528 if !timeRange.CheckRangeOverLap(usi.tsRange.StartEpochMs, usi.tsRange.EndEpochMs) || usi.orgid != orgid { 529 continue 530 } 531 if _, ok := retVal[usi.TableName]; !ok { 532 retVal[usi.TableName] = make(map[string]*dtu.TimeRange) 533 } 534 retVal[usi.TableName][segKey] = usi.tsRange 535 totalCount++ 536 } 537 return retVal, totalChecked, totalCount 538 } 539 540 func DoesSegKeyHavePqidResults(segKey string, pqid string) bool { 541 UnrotatedInfoLock.RLock() 542 defer UnrotatedInfoLock.RUnlock() 543 usi, ok := AllUnrotatedSegmentInfo[segKey] 544 if !ok { 545 return false 546 } 547 if _, ok := usi.unrotatedPQSResults[pqid]; !ok { 548 return false 549 } 550 return true 551 } 552 553 func GetAllPersistentQueryResults(segKey string, pqid string) (*pqmr.SegmentPQMRResults, error) { 554 UnrotatedInfoLock.RLock() 555 defer UnrotatedInfoLock.RUnlock() 556 usi, ok := AllUnrotatedSegmentInfo[segKey] 557 if !ok { 558 return nil, fmt.Errorf("segkey %+v does not exist in unrotated info", segKey) 559 } 560 spqmr, ok := usi.unrotatedPQSResults[pqid] 561 if !ok { 562 return nil, fmt.Errorf("pqid %+v does not exist for segment %+v", pqid, segKey) 563 } 564 return spqmr, nil 565 } 566 567 func (usi *UnrotatedSegmentInfo) GetTimeRange() *dtu.TimeRange { 568 return usi.tsRange 569 } 570 571 // returns the time range of the blocks in the segment that do not exist in spqmr 572 // if the timeRange is nil, no blocks were found in unrotated metadata that donot exist in spqmr 573 func GetTSRangeForMissingBlocks(segKey string, tRange *dtu.TimeRange, spqmr *pqmr.SegmentPQMRResults) *dtu.TimeRange { 574 UnrotatedInfoLock.RLock() 575 defer UnrotatedInfoLock.RUnlock() 576 usi, ok := AllUnrotatedSegmentInfo[segKey] 577 if !ok { 578 log.Errorf("GetTSRangeForMissingBlocks: segKey %+v does not exist in unrotated", segKey) 579 return nil 580 } 581 582 var fRange *dtu.TimeRange 583 for i, blockSummary := range usi.blockSummaries { 584 if tRange.CheckRangeOverLap(blockSummary.LowTs, blockSummary.HighTs) && 585 !spqmr.DoesBlockExist(uint16(i)) { 586 if fRange == nil { 587 fRange = &dtu.TimeRange{} 588 fRange.StartEpochMs = blockSummary.LowTs 589 fRange.EndEpochMs = blockSummary.HighTs 590 } else { 591 if blockSummary.LowTs < fRange.StartEpochMs { 592 fRange.StartEpochMs = blockSummary.LowTs 593 } 594 if blockSummary.HighTs < fRange.EndEpochMs { 595 fRange.EndEpochMs = blockSummary.HighTs 596 } 597 } 598 } 599 } 600 return fRange 601 } 602 603 // returns block search info, block summaries, and any errors encountered 604 // block search info will be loaded for all possible columns 605 func GetSearchInfoForPQSQuery(key string, spqmr *pqmr.SegmentPQMRResults) (map[uint16]*structs.BlockMetadataHolder, 606 []*structs.BlockSummary, error) { 607 UnrotatedInfoLock.RLock() 608 defer UnrotatedInfoLock.RUnlock() 609 610 usi, ok := AllUnrotatedSegmentInfo[key] 611 if !ok { 612 return nil, nil, errors.New("failed to find key in all block micro") 613 } 614 615 retSearchInfo := make(map[uint16]*structs.BlockMetadataHolder) 616 for _, blkNum := range spqmr.GetAllBlocks() { 617 if blkMetadata, ok := usi.blockInfo[blkNum]; ok { 618 retSearchInfo[blkNum] = blkMetadata 619 } 620 } 621 622 retBlkSum := make([]*structs.BlockSummary, len(usi.blockSummaries)) 623 copy(retBlkSum, usi.blockSummaries) 624 625 return retSearchInfo, retBlkSum, nil 626 } 627 628 func GetNumOfSearchedRecordsUnRotated(segKey string) uint64 { 629 UnrotatedInfoLock.RLock() 630 defer UnrotatedInfoLock.RUnlock() 631 usi, ok := AllUnrotatedSegmentInfo[segKey] 632 if !ok { 633 log.Debugf("GetNumOfSearchedRecordsUnRotated: segKey %+v does not exist in unrotated", segKey) 634 return 0 635 } 636 return uint64(usi.RecordCount) 637 }