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  }