fix overflow in window iterator and holt winters roundTime
parent
f4fc905fa9
commit
9314ae8e80
|
@ -339,6 +339,8 @@ type FloatHoltWintersReducer struct {
|
|||
|
||||
// Interval between points
|
||||
interval int64
|
||||
// interval / 2 -- used to perform rounding
|
||||
halfInterval int64
|
||||
|
||||
// Whether to include all data or only future values
|
||||
includeFitData bool
|
||||
|
@ -376,6 +378,7 @@ func NewFloatHoltWintersReducer(h, m int, includeFitData bool, interval time.Dur
|
|||
seasonal: seasonal,
|
||||
includeFitData: includeFitData,
|
||||
interval: int64(interval),
|
||||
halfInterval: int64(interval) / 2,
|
||||
optim: neldermead.New(),
|
||||
epsilon: defaultEpsilon,
|
||||
}
|
||||
|
@ -399,7 +402,15 @@ func (r *FloatHoltWintersReducer) AggregateInteger(p *IntegerPoint) {
|
|||
}
|
||||
|
||||
func (r *FloatHoltWintersReducer) roundTime(t int64) int64 {
|
||||
return r.interval * ((t + r.interval/2) / r.interval)
|
||||
// Overflow safe round function
|
||||
remainder := t % r.interval
|
||||
if remainder > r.halfInterval {
|
||||
// Round up
|
||||
return (t/r.interval + 1) * r.interval
|
||||
} else {
|
||||
// Round down
|
||||
return (t / r.interval) * r.interval
|
||||
}
|
||||
}
|
||||
|
||||
func (r *FloatHoltWintersReducer) Emit() []FloatPoint {
|
||||
|
|
|
@ -3,6 +3,7 @@ package influxql_test
|
|||
import (
|
||||
"math"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/influxdata/influxdb/influxql"
|
||||
)
|
||||
|
@ -318,3 +319,68 @@ func TestHoltWinters_USPopulation_Missing(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
func TestHoltWinters_RoundTime(t *testing.T) {
|
||||
maxTime := time.Unix(0, influxql.MaxTime).Round(time.Second).UnixNano()
|
||||
data := []influxql.FloatPoint{
|
||||
{Time: maxTime - int64(5*time.Second+50*time.Millisecond), Value: 1},
|
||||
{Time: maxTime - int64(4*time.Second+103*time.Millisecond), Value: 10},
|
||||
{Time: maxTime - int64(3*time.Second+223*time.Millisecond), Value: 2},
|
||||
{Time: maxTime - int64(2*time.Second+481*time.Millisecond), Value: 11},
|
||||
}
|
||||
hw := influxql.NewFloatHoltWintersReducer(2, 2, true, time.Second)
|
||||
for _, p := range data {
|
||||
hw.AggregateFloat(&p)
|
||||
}
|
||||
points := hw.Emit()
|
||||
|
||||
forecasted := []influxql.FloatPoint{
|
||||
{Time: maxTime - int64(5*time.Second), Value: 1},
|
||||
{Time: maxTime - int64(4*time.Second), Value: 10.499068390422073},
|
||||
{Time: maxTime - int64(3*time.Second), Value: 2.002458220927272},
|
||||
{Time: maxTime - int64(2*time.Second), Value: 10.499826428426315},
|
||||
{Time: maxTime - int64(1*time.Second), Value: 2.898110014107811},
|
||||
{Time: maxTime - int64(0*time.Second), Value: 10.499786614238138},
|
||||
}
|
||||
|
||||
if exp, got := len(forecasted), len(points); exp != got {
|
||||
t.Fatalf("unexpected number of points emitted: got %d exp %d", got, exp)
|
||||
}
|
||||
for i := range forecasted {
|
||||
if exp, got := forecasted[i].Time, points[i].Time; got != exp {
|
||||
t.Errorf("unexpected time on points[%d] got %v exp %v", i, got, exp)
|
||||
}
|
||||
if exp, got := forecasted[i].Value, points[i].Value; !almostEqual(got, exp) {
|
||||
t.Errorf("unexpected value on points[%d] got %v exp %v", i, got, exp)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestHoltWinters_MaxTime(t *testing.T) {
|
||||
data := []influxql.FloatPoint{
|
||||
{Time: influxql.MaxTime - 1, Value: 1},
|
||||
{Time: influxql.MaxTime, Value: 2},
|
||||
}
|
||||
hw := influxql.NewFloatHoltWintersReducer(1, 0, true, 1)
|
||||
for _, p := range data {
|
||||
hw.AggregateFloat(&p)
|
||||
}
|
||||
points := hw.Emit()
|
||||
|
||||
forecasted := []influxql.FloatPoint{
|
||||
{Time: influxql.MaxTime - 1, Value: 1},
|
||||
{Time: influxql.MaxTime, Value: 2.0058478778784132},
|
||||
{Time: influxql.MaxTime + 1, Value: 3.9399400964478106},
|
||||
}
|
||||
|
||||
if exp, got := len(forecasted), len(points); exp != got {
|
||||
t.Fatalf("unexpected number of points emitted: got %d exp %d", got, exp)
|
||||
}
|
||||
for i := range forecasted {
|
||||
if exp, got := forecasted[i].Time, points[i].Time; got != exp {
|
||||
t.Errorf("unexpected time on points[%d] got %v exp %v", i, got, exp)
|
||||
}
|
||||
if exp, got := forecasted[i].Value, points[i].Value; !almostEqual(got, exp) {
|
||||
t.Errorf("unexpected value on points[%d] got %v exp %v", i, got, exp)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ const (
|
|||
|
||||
// MaxTime is used as the maximum time value when computing an unbounded range.
|
||||
// This time is Jan 1, 2050 at midnight UTC.
|
||||
MaxTime = models.MaxNanoTime
|
||||
MaxTime = models.MaxNanoTime - 1
|
||||
)
|
||||
|
||||
// Iterator represents a generic interface for all Iterators.
|
||||
|
|
Loading…
Reference in New Issue