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  }