fix overflow in window iterator and holt winters roundTime

pull/6675/head
Nathaniel Cook 2016-05-26 13:57:15 -06:00 committed by Edd Robinson
parent f4fc905fa9
commit 9314ae8e80
3 changed files with 79 additions and 2 deletions

View File

@ -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 {

View File

@ -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)
}
}
}

View File

@ -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.