github.com/m3db/m3@v1.5.0/src/query/block/container.go (about) 1 // Copyright (c) 2019 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 block 22 23 import ( 24 "errors" 25 "fmt" 26 27 xerrors "github.com/m3db/m3/src/x/errors" 28 ) 29 30 var ( 31 errMismatchedStepIter = errors.New( 32 "container step iter has mismatched step size") 33 errMismatchedUcStepIter = errors.New( 34 "unconsolidated container step iter has mismatched step size") 35 errNoContainerBlocks = errors.New( 36 "no blocks provided to initialize container block") 37 ) 38 39 type containerBlock struct { 40 err error 41 meta Metadata 42 blocks []Block 43 } 44 45 func newContainerBlock(blocks []Block) (AccumulatorBlock, error) { 46 if len(blocks) == 0 { 47 return nil, errNoContainerBlocks 48 } 49 50 meta := blocks[0].Meta() 51 for _, b := range blocks[1:] { 52 m := b.Meta() 53 if !m.Equals(meta) { 54 return nil, fmt.Errorf("mismatched metadata in container block: "+ 55 "expected %s, got %s", meta.String(), m.String()) 56 } 57 58 meta.ResultMetadata = meta.ResultMetadata.CombineMetadata(m.ResultMetadata) 59 } 60 61 return &containerBlock{ 62 blocks: blocks, 63 meta: meta, 64 }, nil 65 } 66 67 // NewContainerBlock creates a container block, which allows iterating across 68 // blocks incoming from multiple data sources, provided they have the same 69 // bounds. 70 func NewContainerBlock(blocks ...Block) (AccumulatorBlock, error) { 71 return newContainerBlock(blocks) 72 } 73 74 func (b *containerBlock) Meta() Metadata { 75 return b.meta 76 } 77 78 func (b *containerBlock) AddBlock(bl Block) error { 79 if b.err != nil { 80 return b.err 81 } 82 83 m, blockMeta := b.Meta(), bl.Meta() 84 if !m.Equals(blockMeta) { 85 return fmt.Errorf("mismatched metadata adding block to container block: "+ 86 "expected %s, got %s", m.String(), blockMeta.String()) 87 } 88 89 b.meta.ResultMetadata = b.meta.ResultMetadata. 90 CombineMetadata(blockMeta.ResultMetadata) 91 b.blocks = append(b.blocks, bl) 92 return nil 93 } 94 95 func (b *containerBlock) Info() BlockInfo { 96 return NewBlockInfo(BlockContainer) 97 } 98 99 func (b *containerBlock) Close() error { 100 multiErr := xerrors.NewMultiError() 101 multiErr = multiErr.Add(b.err) 102 for _, bl := range b.blocks { 103 multiErr = multiErr.Add(bl.Close()) 104 } 105 106 if err := multiErr.FinalError(); err != nil { 107 b.err = err 108 return err 109 } 110 111 return nil 112 } 113 114 func (b *containerBlock) StepIter() (StepIter, error) { 115 if b.err != nil { 116 return nil, b.err 117 } 118 119 it := &containerStepIter{its: make([]StepIter, 0, len(b.blocks))} 120 for _, bl := range b.blocks { 121 iter, err := bl.StepIter() 122 if err != nil { 123 b.err = err 124 return nil, err 125 } 126 127 it.its = append(it.its, iter) 128 } 129 130 return it, nil 131 } 132 133 // NB: step iterators are constructed "sideways" 134 type containerStepIter struct { 135 err error 136 its []StepIter 137 } 138 139 func (it *containerStepIter) Close() { 140 for _, iter := range it.its { 141 iter.Close() 142 } 143 } 144 145 func (it *containerStepIter) Err() error { 146 if it.err != nil { 147 return it.err 148 } 149 150 for _, iter := range it.its { 151 if it.err = iter.Err(); it.err != nil { 152 return it.err 153 } 154 } 155 156 return nil 157 } 158 159 func (it *containerStepIter) StepCount() int { 160 // NB: when using a step iterator, step count doesn't change, but the length 161 // of each step does. 162 if len(it.its) == 0 { 163 return 0 164 } 165 166 return it.its[0].StepCount() 167 } 168 169 func (it *containerStepIter) SeriesMeta() []SeriesMeta { 170 length := 0 171 for _, iter := range it.its { 172 length += len(iter.SeriesMeta()) 173 } 174 175 metas := make([]SeriesMeta, 0, length) 176 for _, iter := range it.its { 177 metas = append(metas, iter.SeriesMeta()...) 178 } 179 180 return metas 181 } 182 183 func (it *containerStepIter) Next() bool { 184 if it.err != nil { 185 return false 186 } 187 188 // advance all the contained iterators; if any have size mismatches, set an 189 // error and stop traversal. 190 var next bool 191 for i, iter := range it.its { 192 n := iter.Next() 193 194 if it.err = iter.Err(); it.err != nil { 195 return false 196 } 197 198 if i == 0 { 199 next = n 200 } else if next != n { 201 it.err = errMismatchedStepIter 202 return false 203 } 204 } 205 206 return next 207 } 208 209 func (it *containerStepIter) Current() Step { 210 if len(it.its) == 0 { 211 return ColStep{ 212 time: 0, 213 values: []float64{}, 214 } 215 } 216 217 curr := it.its[0].Current() 218 // NB: to get Current for contained step iterators, append results from all 219 // contained step iterators in order. 220 accumulatorStep := ColStep{ 221 time: curr.Time(), 222 values: curr.Values(), 223 } 224 225 for _, iter := range it.its[1:] { 226 curr := iter.Current() 227 accumulatorStep.values = append(accumulatorStep.values, curr.Values()...) 228 } 229 230 return accumulatorStep 231 } 232 233 func (b *containerBlock) SeriesIter() (SeriesIter, error) { 234 if b.err != nil { 235 return nil, b.err 236 } 237 238 it := &containerSeriesIter{ 239 its: make([]SeriesIter, 0, len(b.blocks)), 240 } 241 242 for _, bl := range b.blocks { 243 iter, err := bl.SeriesIter() 244 if err != nil { 245 b.err = err 246 return nil, err 247 } 248 249 it.its = append(it.its, iter) 250 } 251 252 return it, nil 253 } 254 255 type containerSeriesIter struct { 256 err error 257 idx int 258 its []SeriesIter 259 } 260 261 func (it *containerSeriesIter) Close() { 262 for _, iter := range it.its { 263 iter.Close() 264 } 265 } 266 267 func (it *containerSeriesIter) Err() error { 268 if it.err != nil { 269 return it.err 270 } 271 272 for _, iter := range it.its { 273 if it.err = iter.Err(); it.err != nil { 274 return it.err 275 } 276 } 277 278 return nil 279 } 280 281 func (it *containerSeriesIter) SeriesCount() int { 282 count := 0 283 for _, iter := range it.its { 284 count += iter.SeriesCount() 285 } 286 287 return count 288 } 289 290 func (it *containerSeriesIter) SeriesMeta() []SeriesMeta { 291 length := 0 292 for _, iter := range it.its { 293 length += len(iter.SeriesMeta()) 294 } 295 296 metas := make([]SeriesMeta, 0, length) 297 for _, iter := range it.its { 298 metas = append(metas, iter.SeriesMeta()...) 299 } 300 301 return metas 302 } 303 304 func (it *containerSeriesIter) Next() bool { 305 if it.err != nil { 306 return false 307 } 308 309 for ; it.idx < len(it.its); it.idx++ { 310 iter := it.its[it.idx] 311 if iter.Next() { 312 // the active iterator has been successfully incremented. 313 return true 314 } 315 316 // active iterator errored. 317 if it.err = iter.Err(); it.err != nil { 318 return false 319 } 320 } 321 322 // all iterators expanded. 323 return false 324 } 325 326 func (it *containerSeriesIter) Current() UnconsolidatedSeries { 327 return it.its[it.idx].Current() 328 } 329 330 func (b *containerBlock) MultiSeriesIter( 331 concurrency int, 332 ) ([]SeriesIterBatch, error) { 333 if b.err != nil { 334 return nil, b.err 335 } 336 337 if len(b.blocks) == 0 { 338 return nil, nil 339 } 340 341 multiBatches := make([][]SeriesIterBatch, 0, len(b.blocks)) 342 for _, bl := range b.blocks { 343 batch, err := bl.MultiSeriesIter(concurrency) 344 if err != nil { 345 // NB: do not have to set the iterator error here, since not all 346 // contained blocks necessarily allow multi series iteration. 347 return nil, err 348 } 349 350 multiBatches = append(multiBatches, batch) 351 } 352 353 // NB: create a batch and merge into it rather than merging 354 // into an existing batch, in case sizes don't line up across blocks 355 // (e.g. if some contained blocks have fewer than `concurrency` series.) 356 batches := make([]SeriesIterBatch, 0, concurrency) 357 // init batch sizes. 358 for i := 0; i < concurrency; i++ { 359 // Determine container iter size. 360 size := 0 361 for _, b := range multiBatches { 362 if i >= len(b) { 363 // NB: the current batch has been exhausted, but batches from other 364 // contained blocks may still have values. 365 continue 366 } 367 368 size += b[i].Size 369 } 370 371 iters := make([]SeriesIter, 0, size) 372 for _, b := range multiBatches { 373 if i >= len(b) { 374 continue 375 } 376 377 iters = append(iters, b[i].Iter) 378 } 379 380 batches = append(batches, SeriesIterBatch{ 381 Size: size, 382 Iter: &containerSeriesIter{its: iters}, 383 }) 384 } 385 386 return batches, nil 387 }