Use GetBoundaryValues() for non-counter deltas.

This commit is contained in:
Julius Volz 2013-05-12 02:03:16 +02:00 committed by Julius Volz
parent cc07b9ce74
commit 750f862d9a
7 changed files with 270 additions and 364 deletions

View File

@ -125,8 +125,7 @@ func (v *viewAdapter) GetValueAtTime(fingerprints model.Fingerprints, timestamp
func (v *viewAdapter) GetBoundaryValues(fingerprints model.Fingerprints, interval *model.Interval) (sampleSets []model.SampleSet, err error) { func (v *viewAdapter) GetBoundaryValues(fingerprints model.Fingerprints, interval *model.Interval) (sampleSets []model.SampleSet, err error) {
for _, fingerprint := range fingerprints { for _, fingerprint := range fingerprints {
// TODO: change to GetBoundaryValues() once it has the right return type. samplePairs := v.view.GetBoundaryValues(fingerprint, *interval)
samplePairs := v.view.GetRangeValues(fingerprint, *interval)
if samplePairs == nil { if samplePairs == nil {
continue continue
} }

View File

@ -23,10 +23,8 @@ import (
var testSampleInterval = time.Duration(5) * time.Minute var testSampleInterval = time.Duration(5) * time.Minute
var testStartTime = time.Time{} var testStartTime = time.Time{}
func getTestValueStream(startVal model.SampleValue, func getTestValueStream(startVal model.SampleValue, endVal model.SampleValue, stepVal model.SampleValue, startTime time.Time) (resultValues model.Values) {
endVal model.SampleValue, currentTime := startTime
stepVal model.SampleValue) (resultValues model.Values) {
currentTime := testStartTime
for currentVal := startVal; currentVal <= endVal; currentVal += stepVal { for currentVal := startVal; currentVal <= endVal; currentVal += stepVal {
sample := model.SamplePair{ sample := model.SamplePair{
Value: currentVal, Value: currentVal,
@ -74,7 +72,7 @@ var testMatrix = ast.Matrix{
"instance": "0", "instance": "0",
"group": "production", "group": "production",
}, },
Values: getTestValueStream(0, 100, 10), Values: getTestValueStream(0, 100, 10, testStartTime),
}, },
{ {
Metric: model.Metric{ Metric: model.Metric{
@ -83,7 +81,7 @@ var testMatrix = ast.Matrix{
"instance": "1", "instance": "1",
"group": "production", "group": "production",
}, },
Values: getTestValueStream(0, 200, 20), Values: getTestValueStream(0, 200, 20, testStartTime),
}, },
{ {
Metric: model.Metric{ Metric: model.Metric{
@ -92,7 +90,7 @@ var testMatrix = ast.Matrix{
"instance": "0", "instance": "0",
"group": "canary", "group": "canary",
}, },
Values: getTestValueStream(0, 300, 30), Values: getTestValueStream(0, 300, 30, testStartTime),
}, },
{ {
Metric: model.Metric{ Metric: model.Metric{
@ -101,7 +99,7 @@ var testMatrix = ast.Matrix{
"instance": "1", "instance": "1",
"group": "canary", "group": "canary",
}, },
Values: getTestValueStream(0, 400, 40), Values: getTestValueStream(0, 400, 40, testStartTime),
}, },
{ {
Metric: model.Metric{ Metric: model.Metric{
@ -110,7 +108,7 @@ var testMatrix = ast.Matrix{
"instance": "0", "instance": "0",
"group": "production", "group": "production",
}, },
Values: getTestValueStream(0, 500, 50), Values: getTestValueStream(0, 500, 50, testStartTime),
}, },
{ {
Metric: model.Metric{ Metric: model.Metric{
@ -119,7 +117,7 @@ var testMatrix = ast.Matrix{
"instance": "1", "instance": "1",
"group": "production", "group": "production",
}, },
Values: getTestValueStream(0, 600, 60), Values: getTestValueStream(0, 600, 60, testStartTime),
}, },
{ {
Metric: model.Metric{ Metric: model.Metric{
@ -128,7 +126,7 @@ var testMatrix = ast.Matrix{
"instance": "0", "instance": "0",
"group": "canary", "group": "canary",
}, },
Values: getTestValueStream(0, 700, 70), Values: getTestValueStream(0, 700, 70, testStartTime),
}, },
{ {
Metric: model.Metric{ Metric: model.Metric{
@ -137,7 +135,7 @@ var testMatrix = ast.Matrix{
"instance": "1", "instance": "1",
"group": "canary", "group": "canary",
}, },
Values: getTestValueStream(0, 800, 80), Values: getTestValueStream(0, 800, 80, testStartTime),
}, },
// Single-letter metric and label names. // Single-letter metric and label names.
{ {
@ -145,7 +143,14 @@ var testMatrix = ast.Matrix{
model.MetricNameLabel: "x", model.MetricNameLabel: "x",
"y": "testvalue", "y": "testvalue",
}, },
Values: getTestValueStream(0, 100, 10), Values: getTestValueStream(0, 100, 10, testStartTime),
},
// Counter resets.
{
Metric: model.Metric{
model.MetricNameLabel: "testcounter",
},
Values: append(getTestValueStream(0, 40, 10, testStartTime), getTestValueStream(0, 50, 10, testStartTime.Add(testSampleInterval*5))...),
}, },
} }

View File

@ -318,6 +318,18 @@ func TestExpressions(t *testing.T) {
output: []string{"http_requests{group='canary',instance='1',job='app-server'} => 0.26666666666666666 @[%v]"}, output: []string{"http_requests{group='canary',instance='1',job='app-server'} => 0.26666666666666666 @[%v]"},
fullRanges: 1, fullRanges: 1,
intervalRanges: 0, intervalRanges: 0,
}, {
// Counter resets are ignored by delta() if counter == 1.
expr: "delta(testcounter[50m], 1)",
output: []string{"testcounter{} => 90 @[%v]"},
fullRanges: 1,
intervalRanges: 0,
}, {
// Counter resets are not ignored by delta() if counter == 0.
expr: "delta(testcounter[50m], 0)",
output: []string{"testcounter{} => 50 @[%v]"},
fullRanges: 1,
intervalRanges: 0,
}, { }, {
// Empty expressions shouldn't parse. // Empty expressions shouldn't parse.
expr: "", expr: "",

View File

@ -47,8 +47,12 @@ type MetricPersistence interface {
// Get the metric associated with the provided fingerprint. // Get the metric associated with the provided fingerprint.
GetMetricForFingerprint(*model.Fingerprint) (model.Metric, error) GetMetricForFingerprint(*model.Fingerprint) (model.Metric, error)
// Get the two metric values that are immediately adjacent to a given time.
GetValueAtTime(*model.Fingerprint, time.Time) model.Values GetValueAtTime(*model.Fingerprint, time.Time) model.Values
GetBoundaryValues(*model.Fingerprint, model.Interval) (first model.Values, second model.Values) // Get the boundary values of an interval: the first value older than the
// interval start, and the first value younger than the interval end.
GetBoundaryValues(*model.Fingerprint, model.Interval) model.Values
// Get all values contained within a provided interval.
GetRangeValues(*model.Fingerprint, model.Interval) model.Values GetRangeValues(*model.Fingerprint, model.Interval) model.Values
// Get all label values that are associated with a given label name. // Get all label values that are associated with a given label name.
GetAllValuesForLabel(model.LabelName) (model.LabelValues, error) GetAllValuesForLabel(model.LabelName) (model.LabelValues, error)
@ -58,11 +62,11 @@ type MetricPersistence interface {
// MakeView(builder ViewRequestBuilder, deadline time.Duration) (View, error) // MakeView(builder ViewRequestBuilder, deadline time.Duration) (View, error)
} }
// View provides view of the values in the datastore subject to the request of a // View provides a view of the values in the datastore subject to the request
// preloading operation. // of a preloading operation.
type View interface { type View interface {
GetValueAtTime(*model.Fingerprint, time.Time) model.Values GetValueAtTime(*model.Fingerprint, time.Time) model.Values
GetBoundaryValues(*model.Fingerprint, model.Interval) (first model.Values, second model.Values) GetBoundaryValues(*model.Fingerprint, model.Interval) model.Values
GetRangeValues(*model.Fingerprint, model.Interval) model.Values GetRangeValues(*model.Fingerprint, model.Interval) model.Values
// Destroy this view. // Destroy this view.

View File

@ -793,15 +793,15 @@ func (l *LevelDBMetricPersistence) GetMetricForFingerprint(f *model.Fingerprint)
return return
} }
func (l LevelDBMetricPersistence) GetValueAtTime(f *model.Fingerprint, t time.Time) (samples model.Values) { func (l LevelDBMetricPersistence) GetValueAtTime(f *model.Fingerprint, t time.Time) model.Values {
panic("Not implemented") panic("Not implemented")
} }
func (l LevelDBMetricPersistence) GetBoundaryValues(f *model.Fingerprint, i model.Interval) (first model.Values, second model.Values) { func (l LevelDBMetricPersistence) GetBoundaryValues(f *model.Fingerprint, i model.Interval) model.Values {
panic("Not implemented") panic("Not implemented")
} }
func (l *LevelDBMetricPersistence) GetRangeValues(f *model.Fingerprint, i model.Interval) (samples model.Values) { func (l *LevelDBMetricPersistence) GetRangeValues(f *model.Fingerprint, i model.Interval) model.Values {
panic("Not implemented") panic("Not implemented")
} }

View File

@ -102,8 +102,27 @@ func (s *stream) getValueAtTime(t time.Time) model.Values {
} }
} }
func (s *stream) getBoundaryValues(i model.Interval) (model.Values, model.Values) { func (s *stream) getBoundaryValues(in model.Interval) model.Values {
return s.getValueAtTime(i.OldestInclusive), s.getValueAtTime(i.NewestInclusive) s.RLock()
defer s.RUnlock()
oldest := sort.Search(len(s.values), func(i int) bool {
return !s.values[i].Timestamp.Before(in.OldestInclusive)
})
newest := sort.Search(len(s.values), func(i int) bool {
return s.values[i].Timestamp.After(in.NewestInclusive)
})
resultRange := s.values[oldest:newest]
switch len(resultRange) {
case 0:
return model.Values{}
case 1:
return model.Values{resultRange[0]}
default:
return model.Values{resultRange[0], resultRange[len(resultRange)-1]}
}
} }
func (s *stream) getRangeValues(in model.Interval) model.Values { func (s *stream) getRangeValues(in model.Interval) model.Values {
@ -286,12 +305,12 @@ func (s *memorySeriesStorage) GetValueAtTime(f *model.Fingerprint, t time.Time)
return series.getValueAtTime(t) return series.getValueAtTime(t)
} }
func (s *memorySeriesStorage) GetBoundaryValues(f *model.Fingerprint, i model.Interval) (model.Values, model.Values) { func (s *memorySeriesStorage) GetBoundaryValues(f *model.Fingerprint, i model.Interval) model.Values {
s.RLock() s.RLock()
series, ok := s.fingerprintToSeries[*f] series, ok := s.fingerprintToSeries[*f]
s.RUnlock() s.RUnlock()
if !ok { if !ok {
return nil, nil return nil
} }
return series.getBoundaryValues(i) return series.getBoundaryValues(i)

View File

@ -352,321 +352,7 @@ func GetValueAtTimeTests(persistenceMaker func() (MetricPersistence, test.Closer
} }
} }
func GetBoundaryValuesTests(persistenceMaker func() (MetricPersistence, test.Closer), t test.Tester) { func GetRangeValuesTests(persistenceMaker func() (MetricPersistence, test.Closer), onlyBoundaries bool, t test.Tester) {
type value struct {
year int
month time.Month
day int
hour int
value model.SampleValue
}
type input struct {
openYear int
openMonth time.Month
openDay int
openHour int
endYear int
endMonth time.Month
endDay int
endHour int
}
type output struct {
open []model.SampleValue
end []model.SampleValue
}
type behavior struct {
name string
input input
output output
}
var contexts = []struct {
name string
values []value
behaviors []behavior
}{
{
name: "no values",
values: []value{},
behaviors: []behavior{
{
name: "non-existent interval",
input: input{
openYear: 1984,
openMonth: 3,
openDay: 30,
openHour: 0,
endYear: 1985,
endMonth: 3,
endDay: 30,
endHour: 0,
},
},
},
},
{
name: "single value",
values: []value{
{
year: 1984,
month: 3,
day: 30,
hour: 0,
value: 0,
},
},
behaviors: []behavior{
{
name: "on start but missing end",
input: input{
openYear: 1984,
openMonth: 3,
openDay: 30,
openHour: 0,
endYear: 1985,
endMonth: 3,
endDay: 30,
endHour: 0,
},
output: output{
open: []model.SampleValue{0},
end: []model.SampleValue{0},
},
},
{
name: "non-existent interval after",
input: input{
openYear: 1984,
openMonth: 3,
openDay: 31,
openHour: 0,
endYear: 1985,
endMonth: 3,
endDay: 30,
endHour: 0,
},
output: output{
open: []model.SampleValue{0},
end: []model.SampleValue{0},
},
},
{
name: "non-existent interval before",
input: input{
openYear: 1983,
openMonth: 3,
openDay: 30,
openHour: 0,
endYear: 1984,
endMonth: 3,
endDay: 29,
endHour: 0,
},
output: output{
open: []model.SampleValue{0},
end: []model.SampleValue{0},
},
},
{
name: "on end but not start",
input: input{
openYear: 1983,
openMonth: 3,
openDay: 30,
openHour: 0,
endYear: 1984,
endMonth: 3,
endDay: 30,
endHour: 0,
},
output: output{
open: []model.SampleValue{0},
end: []model.SampleValue{0},
},
},
{
name: "before point",
input: input{
openYear: 1982,
openMonth: 3,
openDay: 30,
openHour: 0,
endYear: 1983,
endMonth: 3,
endDay: 30,
endHour: 0,
},
output: output{
open: []model.SampleValue{0},
end: []model.SampleValue{0},
},
},
{
name: "after point",
input: input{
openYear: 1985,
openMonth: 3,
openDay: 30,
openHour: 0,
endYear: 1986,
endMonth: 3,
endDay: 30,
endHour: 0,
},
output: output{
open: []model.SampleValue{0},
end: []model.SampleValue{0},
},
},
{
name: "spanning point",
input: input{
openYear: 1983,
openMonth: 9,
openDay: 29,
openHour: 12,
endYear: 1984,
endMonth: 9,
endDay: 28,
endHour: 12,
},
output: output{
open: []model.SampleValue{0},
end: []model.SampleValue{0},
},
},
},
},
{
name: "double values",
values: []value{
{
year: 1984,
month: 3,
day: 30,
hour: 0,
value: 0,
},
{
year: 1985,
month: 3,
day: 30,
hour: 0,
value: 1,
},
},
behaviors: []behavior{
{
name: "on points",
input: input{
openYear: 1984,
openMonth: 3,
openDay: 30,
openHour: 0,
endYear: 1985,
endMonth: 3,
endDay: 30,
endHour: 0,
},
output: output{
open: []model.SampleValue{0},
end: []model.SampleValue{1},
},
},
{
name: "on first before second",
input: input{
openYear: 1984,
openMonth: 3,
openDay: 30,
openHour: 0,
endYear: 1984,
endMonth: 6,
endDay: 29,
endHour: 6,
},
output: output{
open: []model.SampleValue{0},
end: []model.SampleValue{0, 1},
},
},
{
name: "on first after second",
input: input{
openYear: 1984,
openMonth: 3,
openDay: 30,
openHour: 0,
endYear: 1985,
endMonth: 6,
endDay: 29,
endHour: 6,
},
output: output{
open: []model.SampleValue{0},
end: []model.SampleValue{1},
},
},
},
},
}
for i, context := range contexts {
// Wrapping in function to enable garbage collection of resources.
func() {
p, closer := persistenceMaker()
defer closer.Close()
defer p.Close()
m := model.Metric{
model.MetricNameLabel: "age_in_years",
}
for _, value := range context.values {
testAppendSample(p, model.Sample{
Value: model.SampleValue(value.value),
Timestamp: time.Date(value.year, value.month, value.day, value.hour, 0, 0, 0, time.UTC),
Metric: m,
}, t)
}
for j, behavior := range context.behaviors {
input := behavior.input
open := time.Date(input.openYear, input.openMonth, input.openDay, input.openHour, 0, 0, 0, time.UTC)
end := time.Date(input.endYear, input.endMonth, input.endDay, input.endHour, 0, 0, 0, time.UTC)
interval := model.Interval{
OldestInclusive: open,
NewestInclusive: end,
}
openValues, endValues := p.GetBoundaryValues(model.NewFingerprintFromMetric(m), interval)
if len(behavior.output.open) != len(openValues) {
t.Fatalf("%d.%d(%s). Expected %d open values but got: %q\n", i, j, behavior.name, len(behavior.output.open), openValues)
}
if len(behavior.output.end) != len(endValues) {
t.Fatalf("%d.%d(%s). Expected %d open values but got: %q\n", i, j, behavior.name, len(behavior.output.open), openValues)
}
for k, samplePair := range openValues {
if samplePair.Value != behavior.output.open[k] {
t.Fatalf("%d.%d.%d(%s). Expected open to be %v but got %v\n", i, j, k, behavior.name, behavior.output.open[k], samplePair.Value)
}
}
for k, samplePair := range endValues {
if samplePair.Value != behavior.output.end[k] {
t.Fatalf("%d.%d.%d(%s). Expected end to be %v but got %v\n", i, j, k, behavior.name, behavior.output.end[k], samplePair.Value)
}
}
}
}()
}
}
func GetRangeValuesTests(persistenceMaker func() (MetricPersistence, test.Closer), t test.Tester) {
type value struct { type value struct {
year int year int
month time.Month month time.Month
@ -949,6 +635,172 @@ func GetRangeValuesTests(persistenceMaker func() (MetricPersistence, test.Closer
}, },
}, },
}, },
{
name: "three values",
values: []value{
{
year: 1984,
month: 3,
day: 30,
hour: 0,
value: 0,
},
{
year: 1985,
month: 3,
day: 30,
hour: 0,
value: 1,
},
{
year: 1986,
month: 3,
day: 30,
hour: 0,
value: 2,
},
},
behaviors: []behavior{
{
name: "start on first value",
input: input{
openYear: 1984,
openMonth: 3,
openDay: 30,
openHour: 0,
endYear: 1985,
endMonth: 3,
endDay: 30,
endHour: 0,
},
output: []output{
{
year: 1984,
month: 3,
day: 30,
hour: 0,
value: 0,
},
{
year: 1985,
month: 3,
day: 30,
hour: 0,
value: 1,
},
},
},
{
name: "start on second value",
input: input{
openYear: 1985,
openMonth: 3,
openDay: 30,
openHour: 0,
endYear: 1986,
endMonth: 3,
endDay: 30,
endHour: 0,
},
output: []output{
{
year: 1985,
month: 3,
day: 30,
hour: 0,
value: 1,
},
{
year: 1986,
month: 3,
day: 30,
hour: 0,
value: 2,
},
},
},
{
name: "end on first value",
input: input{
openYear: 1983,
openMonth: 3,
openDay: 30,
openHour: 0,
endYear: 1984,
endMonth: 3,
endDay: 30,
endHour: 0,
},
output: []output{
{
year: 1984,
month: 3,
day: 30,
hour: 0,
value: 0,
},
},
},
{
name: "end on second value",
input: input{
openYear: 1985,
openMonth: 1,
openDay: 1,
openHour: 0,
endYear: 1985,
endMonth: 3,
endDay: 30,
endHour: 0,
},
output: []output{
{
year: 1985,
month: 3,
day: 30,
hour: 0,
value: 1,
},
},
},
{
name: "overlap on values",
input: input{
openYear: 1983,
openMonth: 3,
openDay: 30,
openHour: 0,
endYear: 1986,
endMonth: 3,
endDay: 30,
endHour: 0,
},
output: []output{
{
year: 1984,
month: 3,
day: 30,
hour: 0,
value: 0,
},
{
year: 1985,
month: 3,
day: 30,
hour: 0,
value: 1,
},
{
year: 1986,
month: 3,
day: 30,
hour: 0,
value: 2,
},
},
},
},
},
} }
for i, context := range contexts { for i, context := range contexts {
@ -980,26 +832,41 @@ func GetRangeValuesTests(persistenceMaker func() (MetricPersistence, test.Closer
NewestInclusive: end, NewestInclusive: end,
} }
values := p.GetRangeValues(model.NewFingerprintFromMetric(m), in) actualValues := model.Values{}
expectedValues := []output{}
if values == nil && len(behavior.output) != 0 { fp := model.NewFingerprintFromMetric(m)
t.Fatalf("%d.%d(%s). Expected %s but got: %s\n", i, j, behavior.name, behavior.output, values) if onlyBoundaries {
} actualValues = p.GetBoundaryValues(fp, in)
l := len(behavior.output)
if behavior.output == nil { if l == 1 {
if values != nil { expectedValues = behavior.output[0:1]
t.Fatalf("%d.%d(%s). Expected nil values but got: %s\n", i, j, behavior.name, values) }
if l > 1 {
expectedValues = append(behavior.output[0:1], behavior.output[l-1])
} }
} else { } else {
if len(behavior.output) != len(values) { actualValues = p.GetRangeValues(fp, in)
t.Fatalf("%d.%d(%s). Expected length %d but got: %d\n", i, j, behavior.name, len(behavior.output), len(values)) expectedValues = behavior.output
}
if actualValues == nil && len(expectedValues) != 0 {
t.Fatalf("%d.%d(%s). Expected %s but got: %s\n", i, j, behavior.name, expectedValues, actualValues)
}
if expectedValues == nil {
if actualValues != nil {
t.Fatalf("%d.%d(%s). Expected nil values but got: %s\n", i, j, behavior.name, actualValues)
}
} else {
if len(expectedValues) != len(actualValues) {
t.Fatalf("%d.%d(%s). Expected length %d but got: %d\n", i, j, behavior.name, len(expectedValues), len(actualValues))
} }
for k, actual := range values { for k, actual := range actualValues {
expected := behavior.output[k] expected := expectedValues[k]
if actual.Value != model.SampleValue(expected.value) { if actual.Value != model.SampleValue(expected.value) {
t.Fatalf("%d.%d.%d(%s). Expected %d but got: %d\n", i, j, k, behavior.name, expected.value, actual.Value) t.Fatalf("%d.%d.%d(%s). Expected %v but got: %v\n", i, j, k, behavior.name, expected.value, actual.Value)
} }
if actual.Timestamp.Year() != expected.year { if actual.Timestamp.Year() != expected.year {
@ -1045,14 +912,6 @@ func BenchmarkMemoryGetValueAtTime(b *testing.B) {
} }
} }
func testMemoryGetBoundaryValues(t test.Tester) {
persistenceMaker := func() (MetricPersistence, test.Closer) {
return NewMemorySeriesStorage(), test.NilCloser
}
GetBoundaryValuesTests(persistenceMaker, t)
}
func TestMemoryGetBoundaryValues(t *testing.T) { func TestMemoryGetBoundaryValues(t *testing.T) {
testMemoryGetBoundaryValues(t) testMemoryGetBoundaryValues(t)
} }
@ -1068,7 +927,15 @@ func testMemoryGetRangeValues(t test.Tester) {
return NewMemorySeriesStorage(), test.NilCloser return NewMemorySeriesStorage(), test.NilCloser
} }
GetRangeValuesTests(persistenceMaker, t) GetRangeValuesTests(persistenceMaker, false, t)
}
func testMemoryGetBoundaryValues(t test.Tester) {
persistenceMaker := func() (MetricPersistence, test.Closer) {
return NewMemorySeriesStorage(), test.NilCloser
}
GetRangeValuesTests(persistenceMaker, true, t)
} }
func TestMemoryGetRangeValues(t *testing.T) { func TestMemoryGetRangeValues(t *testing.T) {