github.com/siglens/siglens@v0.0.0-20240328180423-f7ce9ae441ed/pkg/segment/query/microindexcheck.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 query 18 19 import ( 20 "sync" 21 "sync/atomic" 22 23 dtu "github.com/siglens/siglens/pkg/common/dtypeutils" 24 "github.com/siglens/siglens/pkg/config" 25 "github.com/siglens/siglens/pkg/segment/query/metadata" 26 "github.com/siglens/siglens/pkg/segment/query/summary" 27 "github.com/siglens/siglens/pkg/segment/structs" 28 . "github.com/siglens/siglens/pkg/segment/structs" 29 "github.com/siglens/siglens/pkg/segment/utils" 30 . "github.com/siglens/siglens/pkg/segment/utils" 31 log "github.com/sirupsen/logrus" 32 ) 33 34 type ResultSegmentSearchRequestMap struct { 35 Result map[string]*SegmentSearchRequest 36 Err error 37 } 38 39 /* 40 Top level micro index checking function. For a filter, input segkeys, timeRange, indexNames will do the following: 41 1. blockbloom/ blockrange filtering 42 2. search request generation 43 44 Assumes that filesToSearch has been time filtered 45 Returns a map[string]*SegmentSearchRequest mapping a segment key to the corresponding search request and any errors 46 */ 47 func MicroIndexCheck(currQuery *SearchQuery, filesToSearch map[string]map[string]*BlockTracker, timeRange *dtu.TimeRange, 48 indexNames []string, querySummary *summary.QuerySummary, qid uint64, isQueryPersistent bool, pqid string) (map[string]*SegmentSearchRequest, error) { 49 50 rangeFilter, rangeOp, isRange := currQuery.ExtractRangeFilterFromQuery(qid) 51 bloomWords, wildcardBloom, bloomOp := currQuery.GetAllBlockBloomKeysToSearch() 52 53 finalFilteredRequest, blocksChecked, blockCount := filterViaMicroIndices(currQuery, indexNames, timeRange, 54 filesToSearch, bloomWords, bloomOp, rangeFilter, rangeOp, wildcardBloom, isRange, qid, isQueryPersistent, pqid) 55 querySummary.UpdateCMIResults(blocksChecked, blockCount) 56 return finalFilteredRequest, nil 57 } 58 59 // returns final SSRs, count of total blocks checked, count of blocks that passed 60 func filterViaMicroIndices(currQuery *structs.SearchQuery, indexNames []string, timeRange *dtu.TimeRange, 61 filesToSearch map[string]map[string]*BlockTracker, bloomWords map[string]bool, bloomOp LogicalOperator, 62 rangeFilter map[string]string, rangeOp utils.FilterOperator, wildCardValue bool, 63 isRange bool, qid uint64, isQueryPersistent bool, pqid string) (map[string]*SegmentSearchRequest, uint64, uint64) { 64 65 finalResults := make(map[string]*SegmentSearchRequest) 66 67 serResults, totalBlocks, finalBlockCount, errors := getAllSearchRequestsFromCmi(currQuery, timeRange, 68 filesToSearch, bloomWords, bloomOp, rangeFilter, rangeOp, isRange, wildCardValue, qid, isQueryPersistent, pqid) 69 70 if len(errors) > 0 { 71 for _, err := range errors { 72 log.Errorf("qid=%d filterViaMicroIndices: Failed to get search request from microindices: %+v", qid, err) 73 } 74 } 75 76 for _, sReq := range serResults { 77 finalResults[sReq.SegmentKey] = sReq 78 } 79 return finalResults, totalBlocks, finalBlockCount 80 } 81 82 // returns a list of search request, max possible number of blocks, num blocks to be searched, error 83 func getAllSearchRequestsFromCmi(currQuery *structs.SearchQuery, timeRange *dtu.TimeRange, 84 segkeysToCheck map[string]map[string]*BlockTracker, bloomKeys map[string]bool, bloomOp utils.LogicalOperator, 85 rangeFilter map[string]string, rangeOp utils.FilterOperator, isRange bool, wildCardValue bool, 86 qid uint64, isQueryPersistent bool, pqid string) ([]*structs.SegmentSearchRequest, uint64, uint64, []error) { 87 88 sizeChannel := 0 89 for _, segKeys := range segkeysToCheck { 90 sizeChannel += len(segKeys) 91 } 92 finalTotalBlockCount := uint64(0) 93 finalFilteredBlockCount := uint64(0) 94 finalSearchRequests := make([]*structs.SegmentSearchRequest, 0) 95 finalSearchRequestErrors := make([]error, 0) 96 searchRequestResults := make(chan *structs.SegmentSearchRequest, sizeChannel) 97 searchRequestErrors := make(chan error, sizeChannel) 98 99 colsToCheck, wildcardColQuery := currQuery.GetAllColumnsInQuery() 100 delete(colsToCheck, config.GetTimeStampKey()) // timestamp should not be checked in cmi 101 var blockWG sync.WaitGroup 102 for indexName, segKeys := range segkeysToCheck { 103 for segkey, blockTracker := range segKeys { 104 blockWG.Add(1) 105 go func(key, indName string, blkT *BlockTracker) { 106 defer blockWG.Done() 107 finalReq, totalBlockCount, filteredBlockCount, err := metadata.RunCmiCheck(key, indName, timeRange, blkT, bloomKeys, bloomOp, 108 rangeFilter, rangeOp, isRange, wildCardValue, currQuery, colsToCheck, wildcardColQuery, qid, isQueryPersistent, pqid) 109 if err != nil { 110 log.Errorf("qid=%d, getAllSearchRequestsFromCmi: Failed to get search request from cmi: %+v", qid, err) 111 searchRequestErrors <- err 112 } else { 113 searchRequestResults <- finalReq 114 } 115 atomic.AddUint64(&finalTotalBlockCount, totalBlockCount) 116 atomic.AddUint64(&finalFilteredBlockCount, filteredBlockCount) 117 }(segkey, indexName, blockTracker) 118 } 119 } 120 go func() { 121 blockWG.Wait() 122 close(searchRequestResults) 123 close(searchRequestErrors) 124 }() 125 126 for req := range searchRequestResults { 127 if req != nil { 128 finalSearchRequests = append(finalSearchRequests, req) 129 } 130 } 131 132 for err := range searchRequestErrors { 133 finalSearchRequestErrors = append(finalSearchRequestErrors, err) 134 } 135 return finalSearchRequests, finalTotalBlockCount, finalFilteredBlockCount, finalSearchRequestErrors 136 }