github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/query/storage/m3/consolidators/step_accumulator.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 consolidators
    22  
    23  import (
    24  	"time"
    25  
    26  	"github.com/m3db/m3/src/dbnode/ts"
    27  	xts "github.com/m3db/m3/src/query/ts"
    28  	xtime "github.com/m3db/m3/src/x/time"
    29  )
    30  
    31  // StepLookbackAccumulator is a helper for accumulating series in a step-wise
    32  // fashion. It takes a 'step' of values, which represents a vertical
    33  // slice of time across a list of series, and accumulates them when a
    34  // valid step has been reached.
    35  type StepLookbackAccumulator struct {
    36  	lookbackDuration time.Duration
    37  	stepSize         time.Duration
    38  	earliestLookback xtime.UnixNano
    39  	datapoints       []xts.Datapoint
    40  	buffer           [][]xts.Datapoint
    41  	unconsumed       [][]xts.Datapoint
    42  }
    43  
    44  // Ensure StepLookbackAccumulator satisfies StepCollector.
    45  var _ StepCollector = (*StepLookbackAccumulator)(nil)
    46  
    47  // NewStepLookbackAccumulator creates an accumulator used for
    48  // step iteration across a series list with a given lookback.
    49  func NewStepLookbackAccumulator(
    50  	lookbackDuration, stepSize time.Duration,
    51  	startTime xtime.UnixNano,
    52  ) *StepLookbackAccumulator {
    53  	datapoints := make([]xts.Datapoint, 0, initLength)
    54  	buffer := make([][]xts.Datapoint, BufferSteps)
    55  	return &StepLookbackAccumulator{
    56  		lookbackDuration: lookbackDuration,
    57  		stepSize:         stepSize,
    58  		earliestLookback: startTime.Add(-1 * lookbackDuration),
    59  		datapoints:       datapoints,
    60  		buffer:           buffer,
    61  		unconsumed:       buffer[:0],
    62  	}
    63  }
    64  
    65  // AddPoint adds a datapoint to a given step if it's within the valid
    66  // time period; otherwise drops it silently, which is fine for accumulation.
    67  func (c *StepLookbackAccumulator) AddPoint(dp ts.Datapoint) {
    68  	if dp.TimestampNanos.Before(c.earliestLookback) {
    69  		// this datapoint is too far in the past, it can be dropped.
    70  		return
    71  	}
    72  
    73  	c.datapoints = append(c.datapoints, xts.Datapoint{
    74  		Timestamp: dp.TimestampNanos,
    75  		Value:     dp.Value,
    76  	})
    77  }
    78  
    79  // BufferStep adds viable points to the next unconsumed buffer step.
    80  func (c *StepLookbackAccumulator) BufferStep() {
    81  	// Update earliest lookback then remove stale values for the next
    82  	// iteration of the datapoint set.
    83  	c.earliestLookback = c.earliestLookback.Add(c.stepSize)
    84  	if len(c.datapoints) == 0 {
    85  		c.unconsumed = append(c.unconsumed, nil)
    86  		return
    87  	}
    88  
    89  	accumulated := make([]xts.Datapoint, len(c.datapoints))
    90  	copy(accumulated, c.datapoints)
    91  	c.datapoints = c.datapoints[:0]
    92  	c.unconsumed = append(c.unconsumed, accumulated)
    93  }
    94  
    95  // BufferStepCount indicates how many accumulated points are still unconsumed.
    96  func (c *StepLookbackAccumulator) BufferStepCount() int {
    97  	return len(c.unconsumed)
    98  }
    99  
   100  // AccumulateAndMoveToNext consolidates the current values and moves the
   101  // consolidator to the next given value, purging stale values.
   102  func (c *StepLookbackAccumulator) AccumulateAndMoveToNext() []xts.Datapoint {
   103  	if len(c.unconsumed) == 0 {
   104  		return nil
   105  	}
   106  
   107  	val := c.unconsumed[0]
   108  	remaining := c.unconsumed[1:]
   109  
   110  	if len(remaining) > 0 {
   111  		// Move any unconsumed values to the front of unconsumed.
   112  		c.unconsumed = c.buffer[:len(remaining)]
   113  		copy(c.unconsumed, remaining)
   114  	} else {
   115  		// Otherwise just repoint to the start of the buffer.
   116  		c.unconsumed = c.buffer[:0]
   117  	}
   118  
   119  	return val
   120  }