github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/query/storage/m3/encoded_block_builder.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 m3
    22  
    23  import (
    24  	"bytes"
    25  	"sort"
    26  
    27  	"github.com/m3db/m3/src/dbnode/encoding"
    28  	"github.com/m3db/m3/src/query/block"
    29  	"github.com/m3db/m3/src/query/models"
    30  	"github.com/m3db/m3/src/query/storage/m3/consolidators"
    31  	"github.com/m3db/m3/src/x/ident"
    32  	xtime "github.com/m3db/m3/src/x/time"
    33  )
    34  
    35  const initBlockLength = 10
    36  
    37  type blockAtTime struct {
    38  	time  xtime.UnixNano
    39  	block encodedBlock
    40  }
    41  
    42  type blocksAtTime []blockAtTime
    43  
    44  type encodedBlockBuilder struct {
    45  	resultMeta   block.ResultMetadata
    46  	blocksAtTime blocksAtTime
    47  	options      Options
    48  }
    49  
    50  func newEncodedBlockBuilder(
    51  	result consolidators.SeriesFetchResult,
    52  	options Options,
    53  ) *encodedBlockBuilder {
    54  	return &encodedBlockBuilder{
    55  		resultMeta:   result.Metadata,
    56  		blocksAtTime: make(blocksAtTime, 0, initBlockLength),
    57  		options:      options,
    58  	}
    59  }
    60  
    61  func (b *encodedBlockBuilder) add(
    62  	iter encoding.SeriesIterator,
    63  	tags models.Tags,
    64  	meta block.ResultMetadata,
    65  	bounds models.Bounds,
    66  	lastBlock bool,
    67  ) error {
    68  	start := bounds.Start
    69  	consolidation := consolidationSettings{
    70  		consolidationFn: b.options.ConsolidationFunc(),
    71  		currentTime:     start,
    72  		bounds:          bounds,
    73  	}
    74  
    75  	seriesMeta := block.SeriesMeta{
    76  		Name: iter.ID().Bytes(),
    77  		Tags: tags,
    78  	}
    79  
    80  	for idx, bl := range b.blocksAtTime {
    81  		if bl.time.Equal(start) {
    82  			block := bl.block
    83  			block.seriesBlockIterators = append(block.seriesBlockIterators, iter)
    84  			block.seriesMetas = append(block.seriesMetas, seriesMeta)
    85  			b.blocksAtTime[idx].block = block
    86  			return nil
    87  		}
    88  	}
    89  
    90  	bl, err := newEncodedBlock(
    91  		consolidators.NewEmptyFetchResult(meta),
    92  		consolidation,
    93  		lastBlock,
    94  		b.options,
    95  	)
    96  
    97  	if err != nil {
    98  		return err
    99  	}
   100  
   101  	bl.seriesBlockIterators = append(bl.seriesBlockIterators, iter)
   102  	bl.seriesMetas = append(bl.seriesMetas, seriesMeta)
   103  	b.blocksAtTime = append(b.blocksAtTime, blockAtTime{
   104  		time:  start,
   105  		block: *bl,
   106  	})
   107  
   108  	return nil
   109  }
   110  
   111  func (b *encodedBlockBuilder) build() ([]block.Block, error) {
   112  	if err := b.backfillMissing(); err != nil {
   113  		return nil, err
   114  	}
   115  
   116  	// Sort blocks by ID.
   117  	for _, bl := range b.blocksAtTime {
   118  		sort.Sort(seriesIteratorByID(bl.block.seriesBlockIterators))
   119  	}
   120  
   121  	blocks := make([]block.Block, 0, len(b.blocksAtTime))
   122  	for _, bl := range b.blocksAtTime {
   123  		block := bl.block
   124  		blocks = append(blocks, &block)
   125  	}
   126  
   127  	return blocks, nil
   128  }
   129  
   130  // Since some series can be missing if there are no values in composing blocks,
   131  // need to backfill these, since downstream functions rely on series order.
   132  //
   133  // TODO: this will be removed after https://github.com/m3db/m3/issues/1281 is
   134  // completed, which will allow temporal functions, and final read accumulator
   135  // to empty series, and do its own matching rather than relying on matching
   136  // series order. This is functionally throwaway code and should be regarded
   137  // as such.
   138  func (b *encodedBlockBuilder) backfillMissing() error {
   139  	// map series to indeces of b.blocksAtTime at which they are seen
   140  	seenMap := make(map[string]seriesIteratorDetails, initBlockReplicaLength)
   141  	for idx, bl := range b.blocksAtTime {
   142  		block := bl.block
   143  		for i, iter := range block.seriesBlockIterators {
   144  			id := iter.ID().String()
   145  			if seen, found := seenMap[id]; !found {
   146  				seenMap[id] = seriesIteratorDetails{
   147  					start:   iter.Start(),
   148  					end:     iter.End(),
   149  					id:      iter.ID(),
   150  					ns:      iter.Namespace(),
   151  					tags:    block.seriesMetas[i].Tags,
   152  					present: []int{idx},
   153  				}
   154  			} else {
   155  				seen.present = append(seen.present, idx)
   156  				seenMap[id] = seen
   157  			}
   158  		}
   159  	}
   160  
   161  	// make sure that each seen series exists in every block.
   162  	blockLen := len(b.blocksAtTime)
   163  	for _, iterDetails := range seenMap {
   164  		present := iterDetails.present
   165  		if len(present) == blockLen {
   166  			// This series exists in every block already, thus no backfilling necessary.
   167  			continue
   168  		}
   169  
   170  		for blockIdx, bl := range b.blocksAtTime {
   171  			found := false
   172  			for _, presentVal := range present {
   173  				if presentVal == blockIdx {
   174  					found = true
   175  					break
   176  				}
   177  			}
   178  
   179  			// this series exists in the current block.
   180  			if found {
   181  				continue
   182  			}
   183  
   184  			iter := encoding.NewSeriesIterator(encoding.SeriesIteratorOptions{
   185  				ID:             ident.StringID(iterDetails.id.String()),
   186  				Namespace:      ident.StringID(iterDetails.ns.String()),
   187  				StartInclusive: iterDetails.start,
   188  				EndExclusive:   iterDetails.end,
   189  			}, nil)
   190  
   191  			newBl := bl.block
   192  			newBl.seriesBlockIterators = append(newBl.seriesBlockIterators, iter)
   193  			newBl.seriesMetas = append(newBl.seriesMetas, block.SeriesMeta{
   194  				Name: iter.ID().Bytes(),
   195  				Tags: iterDetails.tags,
   196  			})
   197  
   198  			b.blocksAtTime[blockIdx].block = newBl
   199  		}
   200  	}
   201  
   202  	return nil
   203  }
   204  
   205  type seriesIteratorDetails struct {
   206  	start, end xtime.UnixNano
   207  	id, ns     ident.ID
   208  	tags       models.Tags
   209  	// NB: the indices that this series iterator exists in already.
   210  	present []int
   211  }
   212  
   213  type seriesIteratorByID []encoding.SeriesIterator
   214  
   215  func (s seriesIteratorByID) Len() int      { return len(s) }
   216  func (s seriesIteratorByID) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
   217  func (s seriesIteratorByID) Less(i, j int) bool {
   218  	return bytes.Compare(s[i].ID().Bytes(), s[j].ID().Bytes()) == -1
   219  }