github.com/siglens/siglens@v0.0.0-20240328180423-f7ce9ae441ed/pkg/segment/query/metadata/metadata.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  	"errors"
    21  	"sort"
    22  	"sync"
    23  	"time"
    24  
    25  	dtu "github.com/siglens/siglens/pkg/common/dtypeutils"
    26  	"github.com/siglens/siglens/pkg/config"
    27  	"github.com/siglens/siglens/pkg/querytracker"
    28  	"github.com/siglens/siglens/pkg/segment/pqmr"
    29  	"github.com/siglens/siglens/pkg/segment/structs"
    30  	"github.com/siglens/siglens/pkg/segment/utils"
    31  	log "github.com/sirupsen/logrus"
    32  )
    33  
    34  var GlobalSegStoreSummary = &structs.AllSegStoreSummary{}
    35  
    36  // Holder struct for all rotated Metadata that exists in the server
    37  type allSegmentMetadata struct {
    38  
    39  	// all SegmentMicroIndex in sorted order of descending latest time, used for global memory limiting
    40  	allSegmentMicroIndex []*SegmentMicroIndex
    41  
    42  	// reverse index which maps a segment key to the corresponding SegmentMicroIndex for quick access (RRC generation/search/etc.)
    43  	segmentMetadataReverseIndex map[string]*SegmentMicroIndex
    44  
    45  	// maps a tableName to the sorted list of SegmentMicroIndex (descending by latest time) used for initial time query filtering
    46  	tableSortedMetadata map[string][]*SegmentMicroIndex
    47  
    48  	// metadata update lock
    49  	updateLock *sync.RWMutex
    50  }
    51  
    52  var globalMetadata *allSegmentMetadata = &allSegmentMetadata{
    53  	allSegmentMicroIndex:        make([]*SegmentMicroIndex, 0),
    54  	segmentMetadataReverseIndex: make(map[string]*SegmentMicroIndex),
    55  	tableSortedMetadata:         make(map[string][]*SegmentMicroIndex),
    56  	updateLock:                  &sync.RWMutex{},
    57  }
    58  
    59  func BulkAddSegmentMicroIndex(allMetadata []*SegmentMicroIndex) {
    60  	globalMetadata.bulkAddSegmentMicroIndex(allMetadata)
    61  }
    62  
    63  func (hm *allSegmentMetadata) bulkAddSegmentMicroIndex(allMetadata []*SegmentMicroIndex) {
    64  	hm.updateLock.Lock()
    65  	defer hm.updateLock.Unlock()
    66  
    67  	for _, newSegMeta := range allMetadata {
    68  		if segMeta, ok := hm.segmentMetadataReverseIndex[newSegMeta.SegmentKey]; ok {
    69  			res, err := mergeSegmentMicroIndex(segMeta, newSegMeta)
    70  			if err != nil {
    71  				log.Errorf("BulkAddSegmentInfo: Failed to do union for segKey=%v err=%v", segMeta.SegmentKey, err)
    72  				continue
    73  			}
    74  			hm.segmentMetadataReverseIndex[newSegMeta.SegmentKey] = res
    75  			continue
    76  		}
    77  		hm.allSegmentMicroIndex = append(hm.allSegmentMicroIndex, newSegMeta)
    78  		hm.segmentMetadataReverseIndex[newSegMeta.SegmentKey] = newSegMeta
    79  		GlobalSegStoreSummary.IncrementTotalSegmentCount()
    80  
    81  		if _, ok := hm.tableSortedMetadata[newSegMeta.VirtualTableName]; !ok {
    82  			GlobalSegStoreSummary.IncrementTotalTableCount()
    83  			hm.tableSortedMetadata[newSegMeta.VirtualTableName] = make([]*SegmentMicroIndex, 0)
    84  		}
    85  		tableMetadata := hm.tableSortedMetadata[newSegMeta.VirtualTableName]
    86  		tableMetadata = append(tableMetadata, newSegMeta)
    87  		hm.tableSortedMetadata[newSegMeta.VirtualTableName] = tableMetadata
    88  	}
    89  	sort.Slice(hm.allSegmentMicroIndex, func(i, j int) bool {
    90  		return hm.allSegmentMicroIndex[i].LatestEpochMS > hm.allSegmentMicroIndex[j].LatestEpochMS
    91  	})
    92  
    93  	for tName, tableSegmentMeta := range hm.tableSortedMetadata {
    94  		sort.Slice(tableSegmentMeta, func(i, j int) bool {
    95  			return tableSegmentMeta[i].LatestEpochMS > tableSegmentMeta[j].LatestEpochMS
    96  		})
    97  		hm.tableSortedMetadata[tName] = tableSegmentMeta
    98  	}
    99  }
   100  
   101  func mergeSegmentMicroIndex(left *SegmentMicroIndex, right *SegmentMicroIndex) (*SegmentMicroIndex, error) {
   102  
   103  	if left.SegmentKey != right.SegmentKey {
   104  		return left, errors.New("left and right keys were not same")
   105  	}
   106  	if right.EarliestEpochMS != 0 && left.EarliestEpochMS == 0 {
   107  		left.EarliestEpochMS = right.EarliestEpochMS
   108  	}
   109  	if right.LatestEpochMS != 0 && left.LatestEpochMS == 0 {
   110  		left.LatestEpochMS = right.LatestEpochMS
   111  	}
   112  	if right.SegbaseDir != "" && left.SegbaseDir == "" {
   113  		left.SegbaseDir = right.SegbaseDir
   114  	}
   115  	if len(right.ColumnNames) > 0 && len(left.ColumnNames) == 0 {
   116  		left.ColumnNames = right.ColumnNames
   117  	}
   118  	if right.NumBlocks > 0 && left.NumBlocks == 0 {
   119  		left.NumBlocks = right.NumBlocks
   120  	}
   121  
   122  	if right.MicroIndexSize != 0 && left.MicroIndexSize == 0 {
   123  		left.MicroIndexSize = right.MicroIndexSize
   124  	}
   125  
   126  	if right.SearchMetadataSize != 0 && left.SearchMetadataSize == 0 {
   127  		left.SearchMetadataSize = right.SearchMetadataSize
   128  	}
   129  
   130  	if right.RecordCount > 0 && left.RecordCount == 0 {
   131  		left.RecordCount = right.RecordCount
   132  	}
   133  
   134  	return left, nil
   135  }
   136  
   137  func RebalanceInMemoryCmi(metadataSizeBytes uint64) {
   138  	globalMetadata.rebalanceCmi(metadataSizeBytes)
   139  }
   140  
   141  // Entry point to rebalance what is loaded in memory depending on BLOCK_MICRO_MEM_SIZE
   142  func (hm *allSegmentMetadata) rebalanceCmi(metadataSizeBytes uint64) {
   143  
   144  	sTime := time.Now()
   145  
   146  	hm.updateLock.RLock()
   147  	cmiIndex := hm.getCmiMaxIndicesToLoad(metadataSizeBytes)
   148  	evicted := hm.evictCmiPastIndices(cmiIndex)
   149  	inMemSize, inMemCMI, newloaded := hm.loadCmiUntilIndex(cmiIndex)
   150  
   151  	log.Infof("rebalanceCmi: CMI, inMem: %+v, allocated: %+v MB, evicted: %v, newloaded: %v, took: %vms",
   152  		inMemCMI, utils.ConvertUintBytesToMB(inMemSize),
   153  		evicted, newloaded, int(time.Since(sTime).Milliseconds()))
   154  	GlobalSegStoreSummary.SetInMemoryBlockMicroIndexCount(uint64(inMemCMI))
   155  	GlobalSegStoreSummary.SetInMemoryBlockMicroIndexSizeMB(utils.ConvertUintBytesToMB(inMemSize))
   156  	hm.updateLock.RUnlock()
   157  }
   158  
   159  /*
   160  Returns the max indices that should have metadata loaded
   161  
   162  First value is the max index where CMIs should be loaded
   163  */
   164  func (hm *allSegmentMetadata) getCmiMaxIndicesToLoad(totalMem uint64) int {
   165  	// 1. get max index to load assuming both CMI & search metadata will be loaded
   166  	// 2. with remaining size, load whatever search metadata that fits in memory
   167  
   168  	numBlocks := len(hm.allSegmentMicroIndex)
   169  	totalMBAllocated := uint64(0)
   170  	maxCmiIndex := 0
   171  	for ; maxCmiIndex < numBlocks; maxCmiIndex++ {
   172  		cmiSize := hm.allSegmentMicroIndex[maxCmiIndex].MicroIndexSize + hm.allSegmentMicroIndex[maxCmiIndex].SearchMetadataSize
   173  		if cmiSize+totalMBAllocated > totalMem {
   174  			break
   175  		}
   176  		totalMBAllocated += cmiSize
   177  	}
   178  
   179  	return maxCmiIndex
   180  }
   181  
   182  func (hm *allSegmentMetadata) evictCmiPastIndices(cmiIndex int) int {
   183  	idxToClear := make([]int, 0)
   184  	for i := cmiIndex; i < len(hm.allSegmentMicroIndex); i++ {
   185  		if hm.allSegmentMicroIndex[i].loadedMicroIndices {
   186  			idxToClear = append(idxToClear, i)
   187  		}
   188  	}
   189  
   190  	if len(idxToClear) > 0 {
   191  		for _, idx := range idxToClear {
   192  			hm.allSegmentMicroIndex[idx].clearMicroIndices()
   193  		}
   194  	}
   195  	return len(idxToClear)
   196  }
   197  
   198  // Returns total in memory size in bytes, total cmis in memory, total search metadata in memory
   199  func (hm *allSegmentMetadata) loadCmiUntilIndex(cmiIdx int) (uint64, int, int) {
   200  
   201  	totalSize := uint64(0)
   202  	totalCMICount := int(0)
   203  
   204  	idxToLoad := make([]int, 0)
   205  
   206  	for i := 0; i < cmiIdx; i++ {
   207  		if !hm.allSegmentMicroIndex[i].loadedMicroIndices {
   208  			idxToLoad = append(idxToLoad, i)
   209  		} else {
   210  			totalSize += hm.allSegmentMicroIndex[i].MicroIndexSize
   211  			totalCMICount += 1
   212  		}
   213  	}
   214  
   215  	if len(idxToLoad) > 0 {
   216  		a, b := hm.loadParallel(idxToLoad, true)
   217  		totalSize += a
   218  		totalCMICount += b
   219  	}
   220  
   221  	return totalSize, totalCMICount, len(idxToLoad)
   222  }
   223  
   224  /*
   225  Parameters:
   226  
   227  	idxToLoad: indices in the allSegmentMicroIndex to be loaded
   228  	cmi: whether to load cmi (true) or ssm (false)
   229  
   230  Returns:
   231  
   232  	totalSize:
   233  	totalEntities:
   234  */
   235  func (hm *allSegmentMetadata) loadParallel(idxToLoad []int, cmi bool) (uint64, int) {
   236  
   237  	totalSize := uint64(0)
   238  	totalEntities := int(0)
   239  
   240  	wg := &sync.WaitGroup{}
   241  	var err error
   242  	var ssmBufs [][]byte
   243  	parallelism := int(config.GetParallelism())
   244  	if !cmi {
   245  		ssmBufs = make([][]byte, parallelism)
   246  		for i := 0; i < parallelism; i++ {
   247  			ssmBufs[i] = make([]byte, 0)
   248  		}
   249  	}
   250  
   251  	for i, idx := range idxToLoad {
   252  		wg.Add(1)
   253  		go func(myIdx int, rbufIdx int) {
   254  			if cmi {
   255  				pqsCols, err := querytracker.GetPersistentColumns(hm.allSegmentMicroIndex[myIdx].VirtualTableName, hm.allSegmentMicroIndex[idx].OrgId)
   256  				if err != nil {
   257  					log.Errorf("loadParallel: error getting persistent columns: %v", err)
   258  				} else {
   259  					err = hm.allSegmentMicroIndex[myIdx].loadMicroIndices(map[uint16]map[string]bool{}, true, pqsCols, true)
   260  					if err != nil {
   261  						log.Errorf("loadParallel: failed to load SSM at index %d. Error %v",
   262  							myIdx, err)
   263  					}
   264  				}
   265  			} else {
   266  				ssmBufs[rbufIdx], err = hm.allSegmentMicroIndex[myIdx].LoadSearchMetadata(ssmBufs[rbufIdx])
   267  				if err != nil {
   268  					log.Errorf("loadParallel: failed to load SSM at index %d. Error: %v", myIdx, err)
   269  				}
   270  			}
   271  			wg.Done()
   272  		}(idx, i%parallelism)
   273  		if i%parallelism == 0 {
   274  			wg.Wait()
   275  		}
   276  	}
   277  	wg.Wait()
   278  
   279  	for _, idx := range idxToLoad {
   280  		if cmi {
   281  			if hm.allSegmentMicroIndex[idx].loadedMicroIndices {
   282  				totalSize += hm.allSegmentMicroIndex[idx].MicroIndexSize
   283  				totalEntities += 1
   284  			}
   285  		} else {
   286  			if hm.allSegmentMicroIndex[idx].loadedSearchMetadata {
   287  				totalSize += hm.allSegmentMicroIndex[idx].SearchMetadataSize
   288  				totalEntities += 1
   289  			}
   290  		}
   291  	}
   292  	return totalSize, totalEntities
   293  }
   294  
   295  func (hm *allSegmentMetadata) deleteSegmentKey(key string) {
   296  	hm.updateLock.Lock()
   297  	defer hm.updateLock.Unlock()
   298  	hm.deleteSegmentKeyInternal(key)
   299  }
   300  
   301  func (hm *allSegmentMetadata) deleteTable(table string, orgid uint64) {
   302  	hm.updateLock.Lock()
   303  	defer hm.updateLock.Unlock()
   304  
   305  	tableSegments, ok := hm.tableSortedMetadata[table]
   306  	if !ok {
   307  		return
   308  	}
   309  
   310  	allSegKeysInTable := make(map[string]bool, len(tableSegments))
   311  	for _, segment := range tableSegments {
   312  		if segment.OrgId == orgid {
   313  			allSegKeysInTable[segment.SegmentKey] = true
   314  		}
   315  	}
   316  	for segKey := range allSegKeysInTable {
   317  		hm.deleteSegmentKeyInternal(segKey)
   318  	}
   319  	delete(hm.tableSortedMetadata, table)
   320  	GlobalSegStoreSummary.DecrementTotalTableCount()
   321  }
   322  
   323  // internal function to delete segment key from all SiglensMetadata structs
   324  // caller is responsible for acquiring locks
   325  func (hm *allSegmentMetadata) deleteSegmentKeyInternal(key string) {
   326  	var tName string
   327  	for i, sMetadata := range hm.allSegmentMicroIndex {
   328  		if sMetadata.SegmentKey == key {
   329  			hm.allSegmentMicroIndex = append(hm.allSegmentMicroIndex[:i], hm.allSegmentMicroIndex[i+1:]...)
   330  			tName = sMetadata.VirtualTableName
   331  			break
   332  		}
   333  	}
   334  	delete(hm.segmentMetadataReverseIndex, key)
   335  	if tName == "" {
   336  		log.Debugf("DeleteSegmentKey key %+v was not found in metadata", key)
   337  		return
   338  	}
   339  	sortedTableSlice, ok := hm.tableSortedMetadata[tName]
   340  	if !ok {
   341  		return
   342  	}
   343  
   344  	for i, sMetadata := range sortedTableSlice {
   345  		if sMetadata.SegmentKey == key {
   346  			sortedTableSlice = append(sortedTableSlice[:i], sortedTableSlice[i+1:]...)
   347  			break
   348  		}
   349  	}
   350  	hm.tableSortedMetadata[tName] = sortedTableSlice
   351  	GlobalSegStoreSummary.DecrementTotalSegKeyCount()
   352  
   353  }
   354  
   355  func (hm *allSegmentMetadata) getMicroIndex(segKey string) (*SegmentMicroIndex, bool) {
   356  	blockMicroIndex, ok := hm.segmentMetadataReverseIndex[segKey]
   357  	return blockMicroIndex, ok
   358  }
   359  
   360  func DeleteSegmentKey(segKey string) {
   361  	globalMetadata.deleteSegmentKey(segKey)
   362  }
   363  
   364  func DeleteVirtualTable(vTable string, orgid uint64) {
   365  	globalMetadata.deleteTable(vTable, orgid)
   366  }
   367  
   368  /*
   369  Internally, this will allocate 30% of the SSM size to metrics and the remaining to logs
   370  */
   371  func RebalanceInMemorySsm(ssmSizeBytes uint64) {
   372  	logsSSM := uint64(float64(ssmSizeBytes) * 0.30)
   373  	metricsSSM := uint64(float64(ssmSizeBytes) * 0.70)
   374  	globalMetadata.rebalanceSsm(logsSSM)
   375  	globalMetricsMetadata.rebalanceMetricsSsm(metricsSSM)
   376  }
   377  
   378  func (hm *allSegmentMetadata) rebalanceSsm(ssmSizeBytes uint64) {
   379  
   380  	sTime := time.Now()
   381  
   382  	hm.updateLock.RLock()
   383  	searchIndex := hm.getSsmMaxIndicesToLoad(ssmSizeBytes)
   384  	evicted := hm.evictSsmPastIndices(searchIndex)
   385  
   386  	inMemSize, inMemSearchMetaCount, newloaded := hm.loadSsmUntilIndex(searchIndex)
   387  
   388  	log.Infof("rebalanceSsm SSM, inMem: %+v SSM, allocated: %+v MB, evicted: %v, newloaded: %v, took: %vms",
   389  		inMemSearchMetaCount, utils.ConvertUintBytesToMB(inMemSize),
   390  		evicted, newloaded, int(time.Since(sTime).Milliseconds()))
   391  
   392  	GlobalSegStoreSummary.SetInMemorySearchmetadataCount(uint64(inMemSearchMetaCount))
   393  	GlobalSegStoreSummary.SetInMemorySsmSizeMB(utils.ConvertUintBytesToMB(inMemSize))
   394  	hm.updateLock.RUnlock()
   395  }
   396  
   397  /*
   398  Returns the max indices that should have SSM(segmentsearchmeta) loaded
   399  */
   400  func (hm *allSegmentMetadata) getSsmMaxIndicesToLoad(totalMem uint64) int {
   401  
   402  	numBlocks := len(hm.allSegmentMicroIndex)
   403  	totalMBAllocated := uint64(0)
   404  	maxSearchIndex := 0
   405  	for ; maxSearchIndex < numBlocks; maxSearchIndex++ {
   406  		searchMetadataSize := hm.allSegmentMicroIndex[maxSearchIndex].SearchMetadataSize
   407  		if searchMetadataSize+totalMBAllocated > totalMem {
   408  			break
   409  		}
   410  		totalMBAllocated += searchMetadataSize
   411  	}
   412  
   413  	return maxSearchIndex
   414  }
   415  
   416  func (hm *allSegmentMetadata) evictSsmPastIndices(searchIndex int) int {
   417  
   418  	idxToClear := make([]int, 0)
   419  	for i := searchIndex; i < len(hm.allSegmentMicroIndex); i++ {
   420  		if hm.allSegmentMicroIndex[i].loadedSearchMetadata {
   421  			idxToClear = append(idxToClear, i)
   422  		}
   423  	}
   424  
   425  	if len(idxToClear) > 0 {
   426  		for _, idx := range idxToClear {
   427  			hm.allSegmentMicroIndex[idx].clearSearchMetadata()
   428  		}
   429  	}
   430  	return len(idxToClear)
   431  }
   432  
   433  // Returns total in memory size in bytes, total search metadata in memory
   434  func (hm *allSegmentMetadata) loadSsmUntilIndex(searchMetaIdx int) (uint64, int, int) {
   435  	totalSize := uint64(0)
   436  	totalSearchMetaCount := int(0)
   437  
   438  	idxToLoad := make([]int, 0)
   439  	for i := 0; i < searchMetaIdx; i++ {
   440  		if !hm.allSegmentMicroIndex[i].loadedSearchMetadata {
   441  			idxToLoad = append(idxToLoad, i)
   442  		} else {
   443  			totalSize += hm.allSegmentMicroIndex[i].SearchMetadataSize
   444  			totalSearchMetaCount += 1
   445  		}
   446  	}
   447  
   448  	if len(idxToLoad) > 0 {
   449  		a, b := hm.loadParallel(idxToLoad, false)
   450  		totalSize += a
   451  		totalSearchMetaCount += b
   452  	}
   453  
   454  	return totalSize, totalSearchMetaCount, len(idxToLoad)
   455  }
   456  
   457  func GetTotalSMICount() int64 {
   458  	return int64(len(globalMetadata.allSegmentMicroIndex))
   459  }
   460  
   461  func GetNumOfSearchedRecordsRotated(segKey string) uint64 {
   462  	globalMetadata.updateLock.RLock()
   463  	smi, segKeyOk := globalMetadata.segmentMetadataReverseIndex[segKey]
   464  	globalMetadata.updateLock.RUnlock()
   465  	if !segKeyOk {
   466  		return 0
   467  	}
   468  	return uint64(smi.RecordCount)
   469  }
   470  
   471  // returns the time range of the blocks in the segment that do not exist in spqmr
   472  // if the timeRange is nil, no blocks were found in metadata that do not exist in spqmr
   473  func GetTSRangeForMissingBlocks(segKey string, tRange *dtu.TimeRange, spqmr *pqmr.SegmentPQMRResults) *dtu.TimeRange {
   474  	globalMetadata.updateLock.RLock()
   475  	defer globalMetadata.updateLock.RUnlock()
   476  	sMicroIdx, ok := globalMetadata.segmentMetadataReverseIndex[segKey]
   477  	if !ok {
   478  		log.Errorf("SegKey %+v does not exist in metadata yet existed for pqs!", segKey)
   479  		return nil
   480  	}
   481  
   482  	if !sMicroIdx.loadedSearchMetadata {
   483  		_, err := sMicroIdx.LoadSearchMetadata([]byte{})
   484  		if err != nil {
   485  			log.Errorf("Error loading search metadata: %+v", err)
   486  			return nil
   487  		}
   488  		defer sMicroIdx.clearSearchMetadata()
   489  	}
   490  
   491  	var fRange *dtu.TimeRange
   492  	for i, blockSummary := range sMicroIdx.BlockSummaries {
   493  		if tRange.CheckRangeOverLap(blockSummary.LowTs, blockSummary.HighTs) && !spqmr.DoesBlockExist(uint16(i)) {
   494  
   495  			if fRange == nil {
   496  				fRange = &dtu.TimeRange{}
   497  				fRange.StartEpochMs = blockSummary.LowTs
   498  				fRange.EndEpochMs = blockSummary.HighTs
   499  			} else {
   500  				if blockSummary.LowTs < fRange.StartEpochMs {
   501  					fRange.StartEpochMs = blockSummary.LowTs
   502  				}
   503  				if blockSummary.HighTs < fRange.EndEpochMs {
   504  					fRange.EndEpochMs = blockSummary.HighTs
   505  				}
   506  			}
   507  		}
   508  	}
   509  	return fRange
   510  }
   511  
   512  func IsUnrotatedQueryNeeded(timeRange *dtu.TimeRange, indexNames []string) bool {
   513  	globalMetadata.updateLock.RLock()
   514  	defer globalMetadata.updateLock.RUnlock()
   515  	for _, index := range indexNames {
   516  		if sortedTableMetadata, ok := globalMetadata.tableSortedMetadata[index]; !ok {
   517  			return true // if table doesn't exist in segstore, assume it exists in unrotated
   518  		} else {
   519  			// as this list is decreasing by LatestEpochMS, we only need to check index 0
   520  			if timeRange.EndEpochMs > sortedTableMetadata[0].LatestEpochMS {
   521  				return true
   522  			}
   523  		}
   524  	}
   525  	return false
   526  }
   527  
   528  /*
   529  	 Returns:
   530  		 1. map of tableName -> map segKey -> segKey time range
   531  		 2. []string of segKeys that are in the time range. that exist in the map
   532  		 2. final matched count
   533  		 3. total possible count
   534  */
   535  func FilterSegmentsByTime(timeRange *dtu.TimeRange, indexNames []string, orgid uint64) (map[string]map[string]*dtu.TimeRange, uint64, uint64) {
   536  
   537  	globalMetadata.updateLock.RLock()
   538  	defer globalMetadata.updateLock.RUnlock()
   539  	timePassed := uint64(0)
   540  	totalChecked := uint64(0)
   541  	retVal := make(map[string]map[string]*dtu.TimeRange)
   542  	for _, index := range indexNames {
   543  		tableMicroIndices, ok := globalMetadata.tableSortedMetadata[index]
   544  		if !ok {
   545  			continue
   546  		}
   547  
   548  		for _, smi := range tableMicroIndices {
   549  			if timeRange.CheckRangeOverLap(smi.EarliestEpochMS, smi.LatestEpochMS) && smi.OrgId == orgid {
   550  				if _, ok := retVal[index]; !ok {
   551  					retVal[index] = make(map[string]*dtu.TimeRange)
   552  				}
   553  				retVal[index][smi.SegmentKey] = &dtu.TimeRange{
   554  					StartEpochMs: smi.EarliestEpochMS,
   555  					EndEpochMs:   smi.LatestEpochMS,
   556  				}
   557  				timePassed++
   558  			}
   559  		}
   560  		totalChecked += uint64(len(tableMicroIndices))
   561  	}
   562  	return retVal, timePassed, totalChecked
   563  }
   564  
   565  // returns the a map with columns as keys and returns a bool if the segkey/table was found
   566  func CheckAndGetColsForSegKey(segKey string, vtable string) (map[string]bool, bool) {
   567  
   568  	globalMetadata.updateLock.RLock()
   569  	segmentMetadata, segKeyOk := globalMetadata.segmentMetadataReverseIndex[segKey]
   570  	globalMetadata.updateLock.RUnlock()
   571  	if !segKeyOk {
   572  		return nil, false
   573  	}
   574  
   575  	return segmentMetadata.getColumns(), true
   576  }
   577  
   578  func DoesColumnExistForTable(cName string, indices []string) bool {
   579  	globalMetadata.updateLock.RLock()
   580  	defer globalMetadata.updateLock.RUnlock()
   581  	for _, index := range indices {
   582  		tableSMI, ok := globalMetadata.tableSortedMetadata[index]
   583  		if !ok {
   584  			continue
   585  		}
   586  
   587  		for _, smi := range tableSMI {
   588  			_, ok := smi.ColumnNames[cName]
   589  			if ok {
   590  				return true
   591  			}
   592  		}
   593  	}
   594  	return false
   595  }
   596  
   597  func GetAllColNames(indices []string) []string {
   598  	globalMetadata.updateLock.RLock()
   599  	defer globalMetadata.updateLock.RUnlock()
   600  
   601  	colNamesMap := make(map[string]bool)
   602  	for _, index := range indices {
   603  		tableSMI, ok := globalMetadata.tableSortedMetadata[index]
   604  		if !ok {
   605  			continue
   606  		}
   607  		for _, smi := range tableSMI {
   608  			for cName := range smi.ColumnNames {
   609  				colNamesMap[cName] = true
   610  			}
   611  		}
   612  	}
   613  	colNames := make([]string, 0, len(colNamesMap))
   614  	for cName := range colNamesMap {
   615  		colNames = append(colNames, cName)
   616  	}
   617  	return colNames
   618  }