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 }