github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/query/storage/m3/consolidators/multi_fetch_result.go (about) 1 // Copyright (c) 2018 Uber Technologies, Inc. 2 // 3 // Permission is hereby granted, free of charge, to any person obtaining a copy 4 // of this software and associated documentation files (the "Software"), to deal 5 // in the Software without restriction, including without limitation the rights 6 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 // copies of the Software, and to permit persons to whom the Software is 8 // furnished to do so, subject to the following conditions: 9 // 10 // The above copyright notice and this permission notice shall be included in 11 // all copies or substantial portions of the Software. 12 // 13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 // THE SOFTWARE. 20 21 package consolidators 22 23 import ( 24 "errors" 25 "fmt" 26 "sync" 27 28 "github.com/m3db/m3/src/dbnode/encoding" 29 terrors "github.com/m3db/m3/src/dbnode/network/server/tchannelthrift/errors" 30 "github.com/m3db/m3/src/query/block" 31 "github.com/m3db/m3/src/query/models" 32 "github.com/m3db/m3/src/query/storage/m3/storagemetadata" 33 xerrors "github.com/m3db/m3/src/x/errors" 34 ) 35 36 type fetchDedupeMap interface { 37 add(iter encoding.SeriesIterator, attrs storagemetadata.Attributes) error 38 update(iter encoding.SeriesIterator, attrs storagemetadata.Attributes) (bool, error) 39 list() []multiResultSeries 40 len() int 41 close() 42 } 43 44 type multiResult struct { 45 sync.Mutex 46 metadata block.ResultMetadata 47 fanout QueryFanoutType 48 seenFirstAttrs storagemetadata.Attributes 49 50 all []MultiFetchResults 51 seenIters []encoding.SeriesIterators // track known iterators to avoid leaking 52 mergedIterators encoding.MutableSeriesIterators 53 mergedTags []*models.Tags 54 dedupeMap fetchDedupeMap 55 err xerrors.MultiError 56 matchOpts MatchOptions 57 tagOpts models.TagOptions 58 limitOpts LimitOptions 59 } 60 61 // LimitOptions specifies the limits when accumulating results in consolidators. 62 type LimitOptions struct { 63 Limit int 64 RequireExhaustive bool 65 } 66 67 // NewMultiFetchResult builds a new multi fetch result. 68 func NewMultiFetchResult( 69 fanout QueryFanoutType, 70 opts MatchOptions, 71 tagOpts models.TagOptions, 72 limitOpts LimitOptions, 73 ) MultiFetchResult { 74 return &multiResult{ 75 metadata: block.NewResultMetadata(), 76 fanout: fanout, 77 matchOpts: opts, 78 tagOpts: tagOpts, 79 limitOpts: limitOpts, 80 } 81 } 82 83 type multiResultSeries struct { 84 attrs storagemetadata.Attributes 85 iter encoding.SeriesIterator 86 tags models.Tags 87 } 88 89 func (r *multiResult) Close() error { 90 r.Lock() 91 defer r.Unlock() 92 93 for _, iters := range r.seenIters { 94 if iters != nil { 95 iters.Close() 96 } 97 } 98 r.seenIters = nil 99 100 if r.mergedIterators != nil { 101 // NB(r): Since all the series iterators in the final result are held onto 102 // by the original iters in the seenIters slice we allow those iterators 103 // to free iterators held onto by final result, and reset the slice for 104 // the final result to zero so we avoid double returning the iterators 105 // themselves. 106 r.mergedIterators.Reset(0) 107 r.mergedIterators.Close() 108 r.mergedIterators = nil 109 } 110 111 r.dedupeMap = nil 112 r.err = xerrors.NewMultiError() 113 114 return nil 115 } 116 117 func (r *multiResult) FinalResultWithAttrs() ( 118 SeriesFetchResult, []storagemetadata.Attributes, error, 119 ) { 120 r.Lock() 121 defer r.Unlock() 122 123 result, dedupedList, err := r.finalResultWithLock() 124 if err != nil { 125 return result, nil, err 126 } 127 128 var attrs []storagemetadata.Attributes 129 seriesData := result.seriesData 130 if iters := seriesData.seriesIterators; iters != nil { 131 l := iters.Len() 132 attrs = make([]storagemetadata.Attributes, 0, l) 133 if r.dedupeMap == nil { 134 for i := 0; i < l; i++ { 135 attrs = append(attrs, r.seenFirstAttrs) 136 } 137 } else { 138 for _, res := range dedupedList { 139 attrs = append(attrs, res.attrs) 140 } 141 } 142 } 143 144 return result, attrs, nil 145 } 146 147 func (r *multiResult) FinalResult() (SeriesFetchResult, error) { 148 r.Lock() 149 defer r.Unlock() 150 151 res, _, err := r.finalResultWithLock() 152 153 return res, err 154 } 155 156 func (r *multiResult) finalResultWithLock() (SeriesFetchResult, []multiResultSeries, error) { 157 err := r.err.LastError() 158 if err != nil { 159 return NewEmptyFetchResult(r.metadata), nil, err 160 } 161 162 if r.mergedIterators != nil { 163 res, err := NewSeriesFetchResult(r.mergedIterators, nil, r.metadata) 164 return res, nil, err 165 } 166 167 if len(r.seenIters) == 0 { 168 res, err := NewSeriesFetchResult(encoding.EmptySeriesIterators, nil, r.metadata) 169 return res, nil, err 170 } 171 172 // otherwise have to create a new seriesiters 173 dedupedList := r.dedupeMap.list() 174 numSeries := len(dedupedList) 175 r.mergedIterators = encoding.NewSizedSeriesIterators(numSeries) 176 if r.mergedTags == nil { 177 r.mergedTags = make([]*models.Tags, numSeries) 178 } 179 180 lenCurr, lenNext := len(r.mergedTags), len(dedupedList) 181 if lenCurr < lenNext { 182 // If incoming list is longer, expand the stored list. 183 r.mergedTags = append(r.mergedTags, make([]*models.Tags, lenNext-lenCurr)...) 184 } else if lenCurr > lenNext { 185 // If incoming list somehow shorter, shrink stored list. 186 r.mergedTags = r.mergedTags[:lenNext] 187 } 188 189 for i, res := range dedupedList { 190 r.mergedIterators.SetAt(i, res.iter) 191 r.mergedTags[i] = &dedupedList[i].tags 192 } 193 194 res, err := NewSeriesFetchResult(r.mergedIterators, r.mergedTags, r.metadata) 195 196 return res, dedupedList, err 197 } 198 199 func (r *multiResult) Results() []MultiFetchResults { 200 r.Lock() 201 defer r.Unlock() 202 return r.all 203 } 204 205 func (r *multiResult) Add(add MultiFetchResults) { 206 var ( 207 newIterators = add.SeriesIterators 208 metadata = add.Metadata 209 attrs = add.Attrs 210 ) 211 212 r.Lock() 213 defer r.Unlock() 214 215 r.all = append(r.all, add) 216 217 if err := add.Err; err != nil { 218 r.err = r.err.Add(err) 219 return 220 } 221 222 if newIterators == nil || newIterators.Len() == 0 { 223 return 224 } 225 226 nsID := "" 227 if newIterators.Iters()[0].Namespace() != nil { 228 nsID = newIterators.Iters()[0].Namespace().String() // sometimes the namespace ID is empty 229 } 230 231 r.seenIters = append(r.seenIters, newIterators) 232 233 // the series limit was reached within this namespace. 234 if !metadata.Exhaustive && r.limitOpts.RequireExhaustive { 235 r.err = r.err.Add(NewLimitError(fmt.Sprintf("series limit exceeded for namespace %s", nsID))) 236 return 237 } 238 239 if len(r.seenIters) == 1 { 240 // store the first attributes seen 241 r.seenFirstAttrs = attrs 242 } else if !r.metadata.Exhaustive { 243 // a previous namespace result already hit the limit, so bail. this handles the case of RequireExhaustive=false 244 // and there is no error to short circuit. 245 return 246 } 247 248 // NB: any non-exhaustive result set added makes the entire 249 // result non-exhaustive 250 // Note: must never override metadata and always use CombineMetadata 251 // in case warnings were first set with call to AddWarnings(..) and 252 // then must be combined before first result is ever set. 253 r.metadata = r.metadata.CombineMetadata(metadata) 254 255 // Need to check the error to bail early after accumulating the iterators 256 // otherwise when we close the the multi fetch result 257 if !r.err.Empty() { 258 // don't need to do anything if the final result is going to be an error 259 return 260 } 261 262 var added bool 263 if len(r.seenIters) == 1 { 264 // need to backfill the dedupe map from the first result first 265 opts := dedupeMapOpts{ 266 fanout: r.fanout, 267 size: newIterators.Len(), 268 tagOpts: r.tagOpts, 269 } 270 271 if r.matchOpts.MatchType == MatchIDs { 272 r.dedupeMap = newIDDedupeMap(opts) 273 } else { 274 r.dedupeMap = newTagDedupeMap(opts) 275 } 276 277 added = r.addOrUpdateDedupeMap(r.seenFirstAttrs, newIterators) 278 } else { 279 // Now de-duplicate 280 added = r.addOrUpdateDedupeMap(attrs, newIterators) 281 } 282 283 // the series limit was reached by adding the results of this namespace to the existing results. 284 if !added && r.err.Empty() { 285 r.metadata.Exhaustive = false 286 if r.limitOpts.RequireExhaustive { 287 r.err = r.err.Add( 288 NewLimitError(fmt.Sprintf("series limit exceeded adding namespace %s to results", nsID))) 289 } 290 } 291 } 292 293 func (r *multiResult) AddWarnings(warnings ...block.Warning) { 294 r.Lock() 295 defer r.Unlock() 296 r.metadata.AddWarnings(warnings...) 297 } 298 299 // NewLimitError returns a limit error so that it's the same type as the query 300 // limit error returned from a single database instance to receive the same 301 // error behavior as a database limit error. 302 func NewLimitError(msg string) error { 303 return terrors.NewResourceExhaustedError(errors.New(msg)) 304 } 305 306 func (r *multiResult) addOrUpdateDedupeMap( 307 attrs storagemetadata.Attributes, 308 newIterators encoding.SeriesIterators, 309 ) bool { 310 for _, iter := range newIterators.Iters() { 311 tagIter := iter.Tags() 312 shouldFilter, err := filterTagIterator(tagIter, r.tagOpts.Filters()) 313 if err != nil { 314 r.err = r.err.Add(err) 315 return false 316 } 317 318 if shouldFilter { 319 // NB: skip here, the closer will free the series iterator regardless. 320 continue 321 } 322 323 if r.dedupeMap.len() == r.limitOpts.Limit { 324 updated, err := r.dedupeMap.update(iter, attrs) 325 if err != nil { 326 r.err = r.err.Add(err) 327 return false 328 } 329 if !updated { 330 return false 331 } 332 } else if err := r.dedupeMap.add(iter, attrs); err != nil { 333 r.err = r.err.Add(err) 334 return false 335 } 336 } 337 return true 338 }