github.com/m3db/m3@v1.5.0/src/query/ts/values.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 ts
    22  
    23  import (
    24  	"time"
    25  
    26  	"github.com/m3db/m3/src/query/models"
    27  	"github.com/m3db/m3/src/query/util"
    28  	xtime "github.com/m3db/m3/src/x/time"
    29  )
    30  
    31  // Values holds the values for a timeseries.  It provides a minimal interface
    32  // for storing and retrieving values in the series, with Series providing a
    33  // more convenient interface for applications to build on top of.  Values
    34  // objects are not specific to a given time, allowing them to be
    35  // pre-allocated, pooled, and re-used across multiple series.  There are
    36  // multiple implementations of Values so that we can optimize storage based on
    37  // the density of the series.
    38  type Values interface {
    39  	// Len returns the number of values present
    40  	Len() int
    41  
    42  	// ValueAt returns the value at the nth element
    43  	ValueAt(n int) float64
    44  
    45  	// DatapointAt returns the datapoint at the nth element
    46  	DatapointAt(n int) Datapoint
    47  
    48  	// Datapoints returns all the datapoints
    49  	Datapoints() []Datapoint
    50  
    51  	// AlignToBounds returns values aligned to given bounds. To belong to a step,
    52  	// values should be <= stepTime and not stale. Takes an optional buffer to
    53  	// allow for memory re-use.
    54  	AlignToBounds(
    55  		bounds models.Bounds,
    56  		lookbackDuration time.Duration,
    57  		buffer AlignedDatapoints,
    58  	) AlignedDatapoints
    59  
    60  	// AlignToBoundsNoWriteForward returns values aligned to the start time
    61  	// and duration, but does not write points forward after aligning them. This
    62  	// differs from AlignToBounds which will write points forwards if no
    63  	// additional values are found in the values, adding an empty point instead.
    64  	// Takes an optional buffer to allow for memory re-use.
    65  	AlignToBoundsNoWriteForward(
    66  		bounds models.Bounds,
    67  		lookbackDuration time.Duration,
    68  		buffer AlignedDatapoints,
    69  	) AlignedDatapoints
    70  }
    71  
    72  // A Datapoint is a single data value reported at a given time.
    73  type Datapoint struct {
    74  	Timestamp xtime.UnixNano
    75  	Value     float64
    76  }
    77  
    78  // AlignedDatapoints is a list of aligned datapoints.
    79  type AlignedDatapoints []Datapoints
    80  
    81  // Datapoints is a list of datapoints.
    82  type Datapoints []Datapoint
    83  
    84  // Len is the length of the array.
    85  func (d Datapoints) Len() int { return len(d) }
    86  
    87  // ValueAt returns the value at the nth element.
    88  func (d Datapoints) ValueAt(n int) float64 { return d[n].Value }
    89  
    90  // DatapointAt returns the value at the nth element.
    91  func (d Datapoints) DatapointAt(n int) Datapoint { return d[n] }
    92  
    93  // Datapoints returns all the datapoints.
    94  func (d Datapoints) Datapoints() []Datapoint { return d }
    95  
    96  // Values returns the values representation.
    97  func (d Datapoints) Values() []float64 {
    98  	values := make([]float64, len(d))
    99  	for i, dp := range d {
   100  		values[i] = dp.Value
   101  	}
   102  
   103  	return values
   104  }
   105  
   106  // Reset resets the passed in value slice with the current value representation.
   107  func (d Datapoints) Reset(values []float64) []float64 {
   108  	if values == nil {
   109  		values = make([]float64, 0, len(d))
   110  	} else {
   111  		values = values[:0]
   112  	}
   113  
   114  	for _, dp := range d {
   115  		values = append(values, dp.Value)
   116  	}
   117  
   118  	return values
   119  }
   120  
   121  func (d Datapoints) alignToBounds(
   122  	bounds models.Bounds,
   123  	lookbackDuration time.Duration,
   124  	stepValues AlignedDatapoints,
   125  	writeForward bool,
   126  ) AlignedDatapoints {
   127  	numDatapoints := d.Len()
   128  	steps := bounds.Steps()
   129  	if stepValues == nil {
   130  		stepValues = make(AlignedDatapoints, steps)
   131  	}
   132  
   133  	dpIdx := 0
   134  	stepSize := bounds.StepSize
   135  	t := bounds.Start
   136  	for i := 0; i < steps; i++ {
   137  		if stepValues[i] == nil {
   138  			stepValues[i] = make(Datapoints, 0, 10)
   139  		} else {
   140  			stepValues[i] = stepValues[i][:0]
   141  		}
   142  
   143  		staleThreshold := lookbackDuration
   144  		if stepSize > lookbackDuration {
   145  			staleThreshold = stepSize
   146  		}
   147  
   148  		for dpIdx < numDatapoints && !d[dpIdx].Timestamp.After(t) {
   149  			point := d[dpIdx]
   150  			dpIdx++
   151  			// Skip stale values
   152  			if t.Sub(point.Timestamp) > staleThreshold {
   153  				continue
   154  			}
   155  
   156  			stepValues[i] = append(stepValues[i], point)
   157  		}
   158  
   159  		// If writeForward is enabled and there is no point found for this
   160  		// interval, reuse the last point as long as its not stale
   161  		if writeForward {
   162  			if len(stepValues[i]) == 0 && dpIdx > 0 {
   163  				prevPoint := d[dpIdx-1]
   164  				if t.Sub(prevPoint.Timestamp) <= staleThreshold {
   165  					stepValues[i] = Datapoints{prevPoint}
   166  				}
   167  			}
   168  		}
   169  
   170  		t = t.Add(stepSize)
   171  	}
   172  
   173  	return stepValues
   174  }
   175  
   176  // AlignToBoundsNoWriteForward returns values aligned to the start time
   177  // and duration, but does not write points forward after aligning them. This
   178  // differs from AlignToBounds which will write points forwards if no additional
   179  // values are found in the values, adding an empty point instead.
   180  func (d Datapoints) AlignToBoundsNoWriteForward(
   181  	bounds models.Bounds,
   182  	lookbackDuration time.Duration,
   183  	buffer AlignedDatapoints,
   184  ) AlignedDatapoints {
   185  	return d.alignToBounds(bounds, lookbackDuration, buffer, false)
   186  }
   187  
   188  // AlignToBounds returns values aligned to given bounds. To belong to a step,
   189  // values should be <= stepTime and not stale.
   190  func (d Datapoints) AlignToBounds(
   191  	bounds models.Bounds,
   192  	lookbackDuration time.Duration,
   193  	buffer AlignedDatapoints,
   194  ) AlignedDatapoints {
   195  	return d.alignToBounds(bounds, lookbackDuration, buffer, true)
   196  }
   197  
   198  // MutableValues is the interface for values that can be updated
   199  type MutableValues interface {
   200  	Values
   201  
   202  	// Sets the value at the given entry
   203  	SetValueAt(n int, v float64)
   204  }
   205  
   206  // FixedResolutionMutableValues are mutable values with fixed resolution between steps
   207  type FixedResolutionMutableValues interface {
   208  	MutableValues
   209  	Resolution() time.Duration
   210  	StepAtTime(t xtime.UnixNano) int
   211  	StartTimeForStep(n int) xtime.UnixNano
   212  	// Time when the series starts
   213  	StartTime() xtime.UnixNano
   214  }
   215  
   216  type fixedResolutionValues struct {
   217  	resolution time.Duration
   218  	numSteps   int
   219  	values     []float64
   220  	startTime  xtime.UnixNano
   221  }
   222  
   223  func (b *fixedResolutionValues) Len() int                  { return b.numSteps }
   224  func (b *fixedResolutionValues) ValueAt(point int) float64 { return b.values[point] }
   225  func (b *fixedResolutionValues) DatapointAt(point int) Datapoint {
   226  	return Datapoint{
   227  		Timestamp: b.StartTimeForStep(point),
   228  		Value:     b.ValueAt(point),
   229  	}
   230  }
   231  func (b *fixedResolutionValues) Datapoints() []Datapoint {
   232  	datapoints := make([]Datapoint, 0, len(b.values))
   233  	for i := range b.values {
   234  		datapoints = append(datapoints, b.DatapointAt(i))
   235  	}
   236  	return datapoints
   237  }
   238  
   239  func (b *fixedResolutionValues) AlignToBounds(
   240  	_ models.Bounds,
   241  	_ time.Duration,
   242  	values AlignedDatapoints,
   243  ) AlignedDatapoints {
   244  	if values == nil {
   245  		values = make(AlignedDatapoints, 0, len(b.values))
   246  	} else {
   247  		values = values[:0]
   248  	}
   249  
   250  	for i := 0; i < b.Len(); i++ {
   251  		values = append(values, Datapoints{b.DatapointAt(i)})
   252  	}
   253  
   254  	return values
   255  }
   256  
   257  func (b *fixedResolutionValues) AlignToBoundsNoWriteForward(
   258  	bb models.Bounds,
   259  	d time.Duration,
   260  	buffer AlignedDatapoints,
   261  ) AlignedDatapoints {
   262  	return b.AlignToBounds(bb, d, buffer)
   263  }
   264  
   265  // StartTime returns the time the values start
   266  func (b *fixedResolutionValues) StartTime() xtime.UnixNano {
   267  	return b.startTime
   268  }
   269  
   270  // Resolution returns resolution per step
   271  func (b *fixedResolutionValues) Resolution() time.Duration {
   272  	return b.resolution
   273  }
   274  
   275  // StepAtTime returns the step within the block containing the given time
   276  func (b *fixedResolutionValues) StepAtTime(t xtime.UnixNano) int {
   277  	return int(t.Sub(b.StartTime()) / b.Resolution())
   278  }
   279  
   280  // StartTimeForStep returns the time at which the given step starts
   281  func (b *fixedResolutionValues) StartTimeForStep(n int) xtime.UnixNano {
   282  	return b.startTime.Add(time.Duration(n) * b.Resolution())
   283  }
   284  
   285  // SetValueAt sets the value at the given entry
   286  func (b *fixedResolutionValues) SetValueAt(n int, v float64) {
   287  	b.values[n] = v
   288  }
   289  
   290  // NewFixedStepValues returns mutable values with fixed resolution
   291  // TODO: remove this.
   292  func NewFixedStepValues(
   293  	resolution time.Duration,
   294  	numSteps int,
   295  	initialValue float64,
   296  	startTime xtime.UnixNano,
   297  ) FixedResolutionMutableValues {
   298  	return newFixedStepValues(resolution, numSteps, initialValue, startTime)
   299  }
   300  
   301  func newFixedStepValues(
   302  	resolution time.Duration,
   303  	numSteps int,
   304  	initialValue float64,
   305  	startTime xtime.UnixNano,
   306  ) *fixedResolutionValues {
   307  	values := make([]float64, numSteps)
   308  	util.Memset(values, initialValue)
   309  	return &fixedResolutionValues{
   310  		resolution: resolution,
   311  		numSteps:   numSteps,
   312  		startTime:  startTime,
   313  		values:     values,
   314  	}
   315  }