github.com/m3db/m3@v1.5.0/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 }