// Copyright 2019 The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package promql
import (
"math"
"testing"
"time"
"github.com/stretchr/testify/require"
"github.com/prometheus/prometheus/model/labels"
"github.com/prometheus/prometheus/tsdb/chunkenc"
)
func TestLazyLoader_WithSamplesTill ( t * testing . T ) {
type testCase struct {
ts time . Time
series [ ] Series // Each series is checked separately. Need not mention all series here.
checkOnlyError bool // If this is true, series is not checked.
}
cases := [ ] struct {
loadString string
// These testCases are run in sequence. So the testCase being run is dependent on the previous testCase.
testCases [ ] testCase
} {
{
loadString : `
load 10 s
metric1 1 + 1 x10
` ,
testCases : [ ] testCase {
{
ts : time . Unix ( 40 , 0 ) ,
series : [ ] Series {
{
Metric : labels . FromStrings ( "__name__" , "metric1" ) ,
promql: Separate `Point` into `FPoint` and `HPoint`
In other words: Instead of having a “polymorphous” `Point` that can
either contain a float value or a histogram value, use an `FPoint` for
floats and an `HPoint` for histograms.
This seemingly small change has a _lot_ of repercussions throughout
the codebase.
The idea here is to avoid the increase in size of `Point` arrays that
happened after native histograms had been added.
The higher-level data structures (`Sample`, `Series`, etc.) are still
“polymorphous”. The same idea could be applied to them, but at each
step the trade-offs needed to be evaluated.
The idea with this change is to do the minimum necessary to get back
to pre-histogram performance for functions that do not touch
histograms. Here are comparisons for the `changes` function. The test
data doesn't include histograms yet. Ideally, there would be no change
in the benchmark result at all.
First runtime v2.39 compared to directly prior to this commit:
```
name old time/op new time/op delta
RangeQuery/expr=changes(a_one[1d]),steps=1-16 391µs ± 2% 542µs ± 1% +38.58% (p=0.000 n=9+8)
RangeQuery/expr=changes(a_one[1d]),steps=10-16 452µs ± 2% 617µs ± 2% +36.48% (p=0.000 n=10+10)
RangeQuery/expr=changes(a_one[1d]),steps=100-16 1.12ms ± 1% 1.36ms ± 2% +21.58% (p=0.000 n=8+10)
RangeQuery/expr=changes(a_one[1d]),steps=1000-16 7.83ms ± 1% 8.94ms ± 1% +14.21% (p=0.000 n=10+10)
RangeQuery/expr=changes(a_ten[1d]),steps=1-16 2.98ms ± 0% 3.30ms ± 1% +10.67% (p=0.000 n=9+10)
RangeQuery/expr=changes(a_ten[1d]),steps=10-16 3.66ms ± 1% 4.10ms ± 1% +11.82% (p=0.000 n=10+10)
RangeQuery/expr=changes(a_ten[1d]),steps=100-16 10.5ms ± 0% 11.8ms ± 1% +12.50% (p=0.000 n=8+10)
RangeQuery/expr=changes(a_ten[1d]),steps=1000-16 77.6ms ± 1% 87.4ms ± 1% +12.63% (p=0.000 n=9+9)
RangeQuery/expr=changes(a_hundred[1d]),steps=1-16 30.4ms ± 2% 32.8ms ± 1% +8.01% (p=0.000 n=10+10)
RangeQuery/expr=changes(a_hundred[1d]),steps=10-16 37.1ms ± 2% 40.6ms ± 2% +9.64% (p=0.000 n=10+10)
RangeQuery/expr=changes(a_hundred[1d]),steps=100-16 105ms ± 1% 117ms ± 1% +11.69% (p=0.000 n=10+10)
RangeQuery/expr=changes(a_hundred[1d]),steps=1000-16 783ms ± 3% 876ms ± 1% +11.83% (p=0.000 n=9+10)
```
And then runtime v2.39 compared to after this commit:
```
name old time/op new time/op delta
RangeQuery/expr=changes(a_one[1d]),steps=1-16 391µs ± 2% 547µs ± 1% +39.84% (p=0.000 n=9+8)
RangeQuery/expr=changes(a_one[1d]),steps=10-16 452µs ± 2% 616µs ± 2% +36.15% (p=0.000 n=10+10)
RangeQuery/expr=changes(a_one[1d]),steps=100-16 1.12ms ± 1% 1.26ms ± 1% +12.20% (p=0.000 n=8+10)
RangeQuery/expr=changes(a_one[1d]),steps=1000-16 7.83ms ± 1% 7.95ms ± 1% +1.59% (p=0.000 n=10+8)
RangeQuery/expr=changes(a_ten[1d]),steps=1-16 2.98ms ± 0% 3.38ms ± 2% +13.49% (p=0.000 n=9+10)
RangeQuery/expr=changes(a_ten[1d]),steps=10-16 3.66ms ± 1% 4.02ms ± 1% +9.80% (p=0.000 n=10+9)
RangeQuery/expr=changes(a_ten[1d]),steps=100-16 10.5ms ± 0% 10.8ms ± 1% +3.08% (p=0.000 n=8+10)
RangeQuery/expr=changes(a_ten[1d]),steps=1000-16 77.6ms ± 1% 78.1ms ± 1% +0.58% (p=0.035 n=9+10)
RangeQuery/expr=changes(a_hundred[1d]),steps=1-16 30.4ms ± 2% 33.5ms ± 4% +10.18% (p=0.000 n=10+10)
RangeQuery/expr=changes(a_hundred[1d]),steps=10-16 37.1ms ± 2% 40.0ms ± 1% +7.98% (p=0.000 n=10+10)
RangeQuery/expr=changes(a_hundred[1d]),steps=100-16 105ms ± 1% 107ms ± 1% +1.92% (p=0.000 n=10+10)
RangeQuery/expr=changes(a_hundred[1d]),steps=1000-16 783ms ± 3% 775ms ± 1% -1.02% (p=0.019 n=9+9)
```
In summary, the runtime doesn't really improve with this change for
queries with just a few steps. For queries with many steps, this
commit essentially reinstates the old performance. This is good
because the many-step queries are the one that matter most (longest
absolute runtime).
In terms of allocations, though, this commit doesn't make a dent at
all (numbers not shown). The reason is that most of the allocations
happen in the sampleRingIterator (in the storage package), which has
to be addressed in a separate commit.
Signed-off-by: beorn7 <beorn@grafana.com>
2 years ago
Floats : [ ] FPoint {
{ 0 , 1 } , { 10000 , 2 } , { 20000 , 3 } , { 30000 , 4 } , { 40000 , 5 } ,
} ,
} ,
} ,
} ,
{
ts : time . Unix ( 10 , 0 ) ,
series : [ ] Series {
{
Metric : labels . FromStrings ( "__name__" , "metric1" ) ,
promql: Separate `Point` into `FPoint` and `HPoint`
In other words: Instead of having a “polymorphous” `Point` that can
either contain a float value or a histogram value, use an `FPoint` for
floats and an `HPoint` for histograms.
This seemingly small change has a _lot_ of repercussions throughout
the codebase.
The idea here is to avoid the increase in size of `Point` arrays that
happened after native histograms had been added.
The higher-level data structures (`Sample`, `Series`, etc.) are still
“polymorphous”. The same idea could be applied to them, but at each
step the trade-offs needed to be evaluated.
The idea with this change is to do the minimum necessary to get back
to pre-histogram performance for functions that do not touch
histograms. Here are comparisons for the `changes` function. The test
data doesn't include histograms yet. Ideally, there would be no change
in the benchmark result at all.
First runtime v2.39 compared to directly prior to this commit:
```
name old time/op new time/op delta
RangeQuery/expr=changes(a_one[1d]),steps=1-16 391µs ± 2% 542µs ± 1% +38.58% (p=0.000 n=9+8)
RangeQuery/expr=changes(a_one[1d]),steps=10-16 452µs ± 2% 617µs ± 2% +36.48% (p=0.000 n=10+10)
RangeQuery/expr=changes(a_one[1d]),steps=100-16 1.12ms ± 1% 1.36ms ± 2% +21.58% (p=0.000 n=8+10)
RangeQuery/expr=changes(a_one[1d]),steps=1000-16 7.83ms ± 1% 8.94ms ± 1% +14.21% (p=0.000 n=10+10)
RangeQuery/expr=changes(a_ten[1d]),steps=1-16 2.98ms ± 0% 3.30ms ± 1% +10.67% (p=0.000 n=9+10)
RangeQuery/expr=changes(a_ten[1d]),steps=10-16 3.66ms ± 1% 4.10ms ± 1% +11.82% (p=0.000 n=10+10)
RangeQuery/expr=changes(a_ten[1d]),steps=100-16 10.5ms ± 0% 11.8ms ± 1% +12.50% (p=0.000 n=8+10)
RangeQuery/expr=changes(a_ten[1d]),steps=1000-16 77.6ms ± 1% 87.4ms ± 1% +12.63% (p=0.000 n=9+9)
RangeQuery/expr=changes(a_hundred[1d]),steps=1-16 30.4ms ± 2% 32.8ms ± 1% +8.01% (p=0.000 n=10+10)
RangeQuery/expr=changes(a_hundred[1d]),steps=10-16 37.1ms ± 2% 40.6ms ± 2% +9.64% (p=0.000 n=10+10)
RangeQuery/expr=changes(a_hundred[1d]),steps=100-16 105ms ± 1% 117ms ± 1% +11.69% (p=0.000 n=10+10)
RangeQuery/expr=changes(a_hundred[1d]),steps=1000-16 783ms ± 3% 876ms ± 1% +11.83% (p=0.000 n=9+10)
```
And then runtime v2.39 compared to after this commit:
```
name old time/op new time/op delta
RangeQuery/expr=changes(a_one[1d]),steps=1-16 391µs ± 2% 547µs ± 1% +39.84% (p=0.000 n=9+8)
RangeQuery/expr=changes(a_one[1d]),steps=10-16 452µs ± 2% 616µs ± 2% +36.15% (p=0.000 n=10+10)
RangeQuery/expr=changes(a_one[1d]),steps=100-16 1.12ms ± 1% 1.26ms ± 1% +12.20% (p=0.000 n=8+10)
RangeQuery/expr=changes(a_one[1d]),steps=1000-16 7.83ms ± 1% 7.95ms ± 1% +1.59% (p=0.000 n=10+8)
RangeQuery/expr=changes(a_ten[1d]),steps=1-16 2.98ms ± 0% 3.38ms ± 2% +13.49% (p=0.000 n=9+10)
RangeQuery/expr=changes(a_ten[1d]),steps=10-16 3.66ms ± 1% 4.02ms ± 1% +9.80% (p=0.000 n=10+9)
RangeQuery/expr=changes(a_ten[1d]),steps=100-16 10.5ms ± 0% 10.8ms ± 1% +3.08% (p=0.000 n=8+10)
RangeQuery/expr=changes(a_ten[1d]),steps=1000-16 77.6ms ± 1% 78.1ms ± 1% +0.58% (p=0.035 n=9+10)
RangeQuery/expr=changes(a_hundred[1d]),steps=1-16 30.4ms ± 2% 33.5ms ± 4% +10.18% (p=0.000 n=10+10)
RangeQuery/expr=changes(a_hundred[1d]),steps=10-16 37.1ms ± 2% 40.0ms ± 1% +7.98% (p=0.000 n=10+10)
RangeQuery/expr=changes(a_hundred[1d]),steps=100-16 105ms ± 1% 107ms ± 1% +1.92% (p=0.000 n=10+10)
RangeQuery/expr=changes(a_hundred[1d]),steps=1000-16 783ms ± 3% 775ms ± 1% -1.02% (p=0.019 n=9+9)
```
In summary, the runtime doesn't really improve with this change for
queries with just a few steps. For queries with many steps, this
commit essentially reinstates the old performance. This is good
because the many-step queries are the one that matter most (longest
absolute runtime).
In terms of allocations, though, this commit doesn't make a dent at
all (numbers not shown). The reason is that most of the allocations
happen in the sampleRingIterator (in the storage package), which has
to be addressed in a separate commit.
Signed-off-by: beorn7 <beorn@grafana.com>
2 years ago
Floats : [ ] FPoint {
{ 0 , 1 } , { 10000 , 2 } , { 20000 , 3 } , { 30000 , 4 } , { 40000 , 5 } ,
} ,
} ,
} ,
} ,
{
ts : time . Unix ( 60 , 0 ) ,
series : [ ] Series {
{
Metric : labels . FromStrings ( "__name__" , "metric1" ) ,
promql: Separate `Point` into `FPoint` and `HPoint`
In other words: Instead of having a “polymorphous” `Point` that can
either contain a float value or a histogram value, use an `FPoint` for
floats and an `HPoint` for histograms.
This seemingly small change has a _lot_ of repercussions throughout
the codebase.
The idea here is to avoid the increase in size of `Point` arrays that
happened after native histograms had been added.
The higher-level data structures (`Sample`, `Series`, etc.) are still
“polymorphous”. The same idea could be applied to them, but at each
step the trade-offs needed to be evaluated.
The idea with this change is to do the minimum necessary to get back
to pre-histogram performance for functions that do not touch
histograms. Here are comparisons for the `changes` function. The test
data doesn't include histograms yet. Ideally, there would be no change
in the benchmark result at all.
First runtime v2.39 compared to directly prior to this commit:
```
name old time/op new time/op delta
RangeQuery/expr=changes(a_one[1d]),steps=1-16 391µs ± 2% 542µs ± 1% +38.58% (p=0.000 n=9+8)
RangeQuery/expr=changes(a_one[1d]),steps=10-16 452µs ± 2% 617µs ± 2% +36.48% (p=0.000 n=10+10)
RangeQuery/expr=changes(a_one[1d]),steps=100-16 1.12ms ± 1% 1.36ms ± 2% +21.58% (p=0.000 n=8+10)
RangeQuery/expr=changes(a_one[1d]),steps=1000-16 7.83ms ± 1% 8.94ms ± 1% +14.21% (p=0.000 n=10+10)
RangeQuery/expr=changes(a_ten[1d]),steps=1-16 2.98ms ± 0% 3.30ms ± 1% +10.67% (p=0.000 n=9+10)
RangeQuery/expr=changes(a_ten[1d]),steps=10-16 3.66ms ± 1% 4.10ms ± 1% +11.82% (p=0.000 n=10+10)
RangeQuery/expr=changes(a_ten[1d]),steps=100-16 10.5ms ± 0% 11.8ms ± 1% +12.50% (p=0.000 n=8+10)
RangeQuery/expr=changes(a_ten[1d]),steps=1000-16 77.6ms ± 1% 87.4ms ± 1% +12.63% (p=0.000 n=9+9)
RangeQuery/expr=changes(a_hundred[1d]),steps=1-16 30.4ms ± 2% 32.8ms ± 1% +8.01% (p=0.000 n=10+10)
RangeQuery/expr=changes(a_hundred[1d]),steps=10-16 37.1ms ± 2% 40.6ms ± 2% +9.64% (p=0.000 n=10+10)
RangeQuery/expr=changes(a_hundred[1d]),steps=100-16 105ms ± 1% 117ms ± 1% +11.69% (p=0.000 n=10+10)
RangeQuery/expr=changes(a_hundred[1d]),steps=1000-16 783ms ± 3% 876ms ± 1% +11.83% (p=0.000 n=9+10)
```
And then runtime v2.39 compared to after this commit:
```
name old time/op new time/op delta
RangeQuery/expr=changes(a_one[1d]),steps=1-16 391µs ± 2% 547µs ± 1% +39.84% (p=0.000 n=9+8)
RangeQuery/expr=changes(a_one[1d]),steps=10-16 452µs ± 2% 616µs ± 2% +36.15% (p=0.000 n=10+10)
RangeQuery/expr=changes(a_one[1d]),steps=100-16 1.12ms ± 1% 1.26ms ± 1% +12.20% (p=0.000 n=8+10)
RangeQuery/expr=changes(a_one[1d]),steps=1000-16 7.83ms ± 1% 7.95ms ± 1% +1.59% (p=0.000 n=10+8)
RangeQuery/expr=changes(a_ten[1d]),steps=1-16 2.98ms ± 0% 3.38ms ± 2% +13.49% (p=0.000 n=9+10)
RangeQuery/expr=changes(a_ten[1d]),steps=10-16 3.66ms ± 1% 4.02ms ± 1% +9.80% (p=0.000 n=10+9)
RangeQuery/expr=changes(a_ten[1d]),steps=100-16 10.5ms ± 0% 10.8ms ± 1% +3.08% (p=0.000 n=8+10)
RangeQuery/expr=changes(a_ten[1d]),steps=1000-16 77.6ms ± 1% 78.1ms ± 1% +0.58% (p=0.035 n=9+10)
RangeQuery/expr=changes(a_hundred[1d]),steps=1-16 30.4ms ± 2% 33.5ms ± 4% +10.18% (p=0.000 n=10+10)
RangeQuery/expr=changes(a_hundred[1d]),steps=10-16 37.1ms ± 2% 40.0ms ± 1% +7.98% (p=0.000 n=10+10)
RangeQuery/expr=changes(a_hundred[1d]),steps=100-16 105ms ± 1% 107ms ± 1% +1.92% (p=0.000 n=10+10)
RangeQuery/expr=changes(a_hundred[1d]),steps=1000-16 783ms ± 3% 775ms ± 1% -1.02% (p=0.019 n=9+9)
```
In summary, the runtime doesn't really improve with this change for
queries with just a few steps. For queries with many steps, this
commit essentially reinstates the old performance. This is good
because the many-step queries are the one that matter most (longest
absolute runtime).
In terms of allocations, though, this commit doesn't make a dent at
all (numbers not shown). The reason is that most of the allocations
happen in the sampleRingIterator (in the storage package), which has
to be addressed in a separate commit.
Signed-off-by: beorn7 <beorn@grafana.com>
2 years ago
Floats : [ ] FPoint {
{ 0 , 1 } , { 10000 , 2 } , { 20000 , 3 } , { 30000 , 4 } , { 40000 , 5 } , { 50000 , 6 } , { 60000 , 7 } ,
} ,
} ,
} ,
} ,
} ,
} ,
{
loadString : `
load 10 s
metric1 1 + 0x5
metric2 1 + 1 x100
` ,
testCases : [ ] testCase {
{ // Adds all samples of metric1.
ts : time . Unix ( 70 , 0 ) ,
series : [ ] Series {
{
Metric : labels . FromStrings ( "__name__" , "metric1" ) ,
promql: Separate `Point` into `FPoint` and `HPoint`
In other words: Instead of having a “polymorphous” `Point` that can
either contain a float value or a histogram value, use an `FPoint` for
floats and an `HPoint` for histograms.
This seemingly small change has a _lot_ of repercussions throughout
the codebase.
The idea here is to avoid the increase in size of `Point` arrays that
happened after native histograms had been added.
The higher-level data structures (`Sample`, `Series`, etc.) are still
“polymorphous”. The same idea could be applied to them, but at each
step the trade-offs needed to be evaluated.
The idea with this change is to do the minimum necessary to get back
to pre-histogram performance for functions that do not touch
histograms. Here are comparisons for the `changes` function. The test
data doesn't include histograms yet. Ideally, there would be no change
in the benchmark result at all.
First runtime v2.39 compared to directly prior to this commit:
```
name old time/op new time/op delta
RangeQuery/expr=changes(a_one[1d]),steps=1-16 391µs ± 2% 542µs ± 1% +38.58% (p=0.000 n=9+8)
RangeQuery/expr=changes(a_one[1d]),steps=10-16 452µs ± 2% 617µs ± 2% +36.48% (p=0.000 n=10+10)
RangeQuery/expr=changes(a_one[1d]),steps=100-16 1.12ms ± 1% 1.36ms ± 2% +21.58% (p=0.000 n=8+10)
RangeQuery/expr=changes(a_one[1d]),steps=1000-16 7.83ms ± 1% 8.94ms ± 1% +14.21% (p=0.000 n=10+10)
RangeQuery/expr=changes(a_ten[1d]),steps=1-16 2.98ms ± 0% 3.30ms ± 1% +10.67% (p=0.000 n=9+10)
RangeQuery/expr=changes(a_ten[1d]),steps=10-16 3.66ms ± 1% 4.10ms ± 1% +11.82% (p=0.000 n=10+10)
RangeQuery/expr=changes(a_ten[1d]),steps=100-16 10.5ms ± 0% 11.8ms ± 1% +12.50% (p=0.000 n=8+10)
RangeQuery/expr=changes(a_ten[1d]),steps=1000-16 77.6ms ± 1% 87.4ms ± 1% +12.63% (p=0.000 n=9+9)
RangeQuery/expr=changes(a_hundred[1d]),steps=1-16 30.4ms ± 2% 32.8ms ± 1% +8.01% (p=0.000 n=10+10)
RangeQuery/expr=changes(a_hundred[1d]),steps=10-16 37.1ms ± 2% 40.6ms ± 2% +9.64% (p=0.000 n=10+10)
RangeQuery/expr=changes(a_hundred[1d]),steps=100-16 105ms ± 1% 117ms ± 1% +11.69% (p=0.000 n=10+10)
RangeQuery/expr=changes(a_hundred[1d]),steps=1000-16 783ms ± 3% 876ms ± 1% +11.83% (p=0.000 n=9+10)
```
And then runtime v2.39 compared to after this commit:
```
name old time/op new time/op delta
RangeQuery/expr=changes(a_one[1d]),steps=1-16 391µs ± 2% 547µs ± 1% +39.84% (p=0.000 n=9+8)
RangeQuery/expr=changes(a_one[1d]),steps=10-16 452µs ± 2% 616µs ± 2% +36.15% (p=0.000 n=10+10)
RangeQuery/expr=changes(a_one[1d]),steps=100-16 1.12ms ± 1% 1.26ms ± 1% +12.20% (p=0.000 n=8+10)
RangeQuery/expr=changes(a_one[1d]),steps=1000-16 7.83ms ± 1% 7.95ms ± 1% +1.59% (p=0.000 n=10+8)
RangeQuery/expr=changes(a_ten[1d]),steps=1-16 2.98ms ± 0% 3.38ms ± 2% +13.49% (p=0.000 n=9+10)
RangeQuery/expr=changes(a_ten[1d]),steps=10-16 3.66ms ± 1% 4.02ms ± 1% +9.80% (p=0.000 n=10+9)
RangeQuery/expr=changes(a_ten[1d]),steps=100-16 10.5ms ± 0% 10.8ms ± 1% +3.08% (p=0.000 n=8+10)
RangeQuery/expr=changes(a_ten[1d]),steps=1000-16 77.6ms ± 1% 78.1ms ± 1% +0.58% (p=0.035 n=9+10)
RangeQuery/expr=changes(a_hundred[1d]),steps=1-16 30.4ms ± 2% 33.5ms ± 4% +10.18% (p=0.000 n=10+10)
RangeQuery/expr=changes(a_hundred[1d]),steps=10-16 37.1ms ± 2% 40.0ms ± 1% +7.98% (p=0.000 n=10+10)
RangeQuery/expr=changes(a_hundred[1d]),steps=100-16 105ms ± 1% 107ms ± 1% +1.92% (p=0.000 n=10+10)
RangeQuery/expr=changes(a_hundred[1d]),steps=1000-16 783ms ± 3% 775ms ± 1% -1.02% (p=0.019 n=9+9)
```
In summary, the runtime doesn't really improve with this change for
queries with just a few steps. For queries with many steps, this
commit essentially reinstates the old performance. This is good
because the many-step queries are the one that matter most (longest
absolute runtime).
In terms of allocations, though, this commit doesn't make a dent at
all (numbers not shown). The reason is that most of the allocations
happen in the sampleRingIterator (in the storage package), which has
to be addressed in a separate commit.
Signed-off-by: beorn7 <beorn@grafana.com>
2 years ago
Floats : [ ] FPoint {
{ 0 , 1 } , { 10000 , 1 } , { 20000 , 1 } , { 30000 , 1 } , { 40000 , 1 } , { 50000 , 1 } ,
} ,
} ,
{
Metric : labels . FromStrings ( "__name__" , "metric2" ) ,
promql: Separate `Point` into `FPoint` and `HPoint`
In other words: Instead of having a “polymorphous” `Point` that can
either contain a float value or a histogram value, use an `FPoint` for
floats and an `HPoint` for histograms.
This seemingly small change has a _lot_ of repercussions throughout
the codebase.
The idea here is to avoid the increase in size of `Point` arrays that
happened after native histograms had been added.
The higher-level data structures (`Sample`, `Series`, etc.) are still
“polymorphous”. The same idea could be applied to them, but at each
step the trade-offs needed to be evaluated.
The idea with this change is to do the minimum necessary to get back
to pre-histogram performance for functions that do not touch
histograms. Here are comparisons for the `changes` function. The test
data doesn't include histograms yet. Ideally, there would be no change
in the benchmark result at all.
First runtime v2.39 compared to directly prior to this commit:
```
name old time/op new time/op delta
RangeQuery/expr=changes(a_one[1d]),steps=1-16 391µs ± 2% 542µs ± 1% +38.58% (p=0.000 n=9+8)
RangeQuery/expr=changes(a_one[1d]),steps=10-16 452µs ± 2% 617µs ± 2% +36.48% (p=0.000 n=10+10)
RangeQuery/expr=changes(a_one[1d]),steps=100-16 1.12ms ± 1% 1.36ms ± 2% +21.58% (p=0.000 n=8+10)
RangeQuery/expr=changes(a_one[1d]),steps=1000-16 7.83ms ± 1% 8.94ms ± 1% +14.21% (p=0.000 n=10+10)
RangeQuery/expr=changes(a_ten[1d]),steps=1-16 2.98ms ± 0% 3.30ms ± 1% +10.67% (p=0.000 n=9+10)
RangeQuery/expr=changes(a_ten[1d]),steps=10-16 3.66ms ± 1% 4.10ms ± 1% +11.82% (p=0.000 n=10+10)
RangeQuery/expr=changes(a_ten[1d]),steps=100-16 10.5ms ± 0% 11.8ms ± 1% +12.50% (p=0.000 n=8+10)
RangeQuery/expr=changes(a_ten[1d]),steps=1000-16 77.6ms ± 1% 87.4ms ± 1% +12.63% (p=0.000 n=9+9)
RangeQuery/expr=changes(a_hundred[1d]),steps=1-16 30.4ms ± 2% 32.8ms ± 1% +8.01% (p=0.000 n=10+10)
RangeQuery/expr=changes(a_hundred[1d]),steps=10-16 37.1ms ± 2% 40.6ms ± 2% +9.64% (p=0.000 n=10+10)
RangeQuery/expr=changes(a_hundred[1d]),steps=100-16 105ms ± 1% 117ms ± 1% +11.69% (p=0.000 n=10+10)
RangeQuery/expr=changes(a_hundred[1d]),steps=1000-16 783ms ± 3% 876ms ± 1% +11.83% (p=0.000 n=9+10)
```
And then runtime v2.39 compared to after this commit:
```
name old time/op new time/op delta
RangeQuery/expr=changes(a_one[1d]),steps=1-16 391µs ± 2% 547µs ± 1% +39.84% (p=0.000 n=9+8)
RangeQuery/expr=changes(a_one[1d]),steps=10-16 452µs ± 2% 616µs ± 2% +36.15% (p=0.000 n=10+10)
RangeQuery/expr=changes(a_one[1d]),steps=100-16 1.12ms ± 1% 1.26ms ± 1% +12.20% (p=0.000 n=8+10)
RangeQuery/expr=changes(a_one[1d]),steps=1000-16 7.83ms ± 1% 7.95ms ± 1% +1.59% (p=0.000 n=10+8)
RangeQuery/expr=changes(a_ten[1d]),steps=1-16 2.98ms ± 0% 3.38ms ± 2% +13.49% (p=0.000 n=9+10)
RangeQuery/expr=changes(a_ten[1d]),steps=10-16 3.66ms ± 1% 4.02ms ± 1% +9.80% (p=0.000 n=10+9)
RangeQuery/expr=changes(a_ten[1d]),steps=100-16 10.5ms ± 0% 10.8ms ± 1% +3.08% (p=0.000 n=8+10)
RangeQuery/expr=changes(a_ten[1d]),steps=1000-16 77.6ms ± 1% 78.1ms ± 1% +0.58% (p=0.035 n=9+10)
RangeQuery/expr=changes(a_hundred[1d]),steps=1-16 30.4ms ± 2% 33.5ms ± 4% +10.18% (p=0.000 n=10+10)
RangeQuery/expr=changes(a_hundred[1d]),steps=10-16 37.1ms ± 2% 40.0ms ± 1% +7.98% (p=0.000 n=10+10)
RangeQuery/expr=changes(a_hundred[1d]),steps=100-16 105ms ± 1% 107ms ± 1% +1.92% (p=0.000 n=10+10)
RangeQuery/expr=changes(a_hundred[1d]),steps=1000-16 783ms ± 3% 775ms ± 1% -1.02% (p=0.019 n=9+9)
```
In summary, the runtime doesn't really improve with this change for
queries with just a few steps. For queries with many steps, this
commit essentially reinstates the old performance. This is good
because the many-step queries are the one that matter most (longest
absolute runtime).
In terms of allocations, though, this commit doesn't make a dent at
all (numbers not shown). The reason is that most of the allocations
happen in the sampleRingIterator (in the storage package), which has
to be addressed in a separate commit.
Signed-off-by: beorn7 <beorn@grafana.com>
2 years ago
Floats : [ ] FPoint {
{ 0 , 1 } , { 10000 , 2 } , { 20000 , 3 } , { 30000 , 4 } , { 40000 , 5 } , { 50000 , 6 } , { 60000 , 7 } , { 70000 , 8 } ,
} ,
} ,
} ,
} ,
{ // This tests fix for https://github.com/prometheus/prometheus/issues/5064.
ts : time . Unix ( 300 , 0 ) ,
checkOnlyError : true ,
} ,
} ,
} ,
}
for _ , c := range cases {
suite , err := NewLazyLoader ( c . loadString , LazyLoaderOpts { } )
require . NoError ( t , err )
defer suite . Close ( )
for _ , tc := range c . testCases {
suite . WithSamplesTill ( tc . ts , func ( err error ) {
require . NoError ( t , err )
if tc . checkOnlyError {
return
}
// Check the series.
queryable := suite . Queryable ( )
querier , err := queryable . Querier ( math . MinInt64 , math . MaxInt64 )
require . NoError ( t , err )
for _ , s := range tc . series {
var matchers [ ] * labels . Matcher
s . Metric . Range ( func ( label labels . Label ) {
m , err := labels . NewMatcher ( labels . MatchEqual , label . Name , label . Value )
require . NoError ( t , err )
matchers = append ( matchers , m )
} )
// Get the series for the matcher.
ss := querier . Select ( suite . Context ( ) , false , nil , matchers ... )
require . True ( t , ss . Next ( ) )
storageSeries := ss . At ( )
require . False ( t , ss . Next ( ) , "Expecting only 1 series" )
// Convert `storage.Series` to `promql.Series`.
got := Series {
Metric : storageSeries . Labels ( ) ,
}
it := storageSeries . Iterator ( nil )
for it . Next ( ) == chunkenc . ValFloat {
t , v := it . At ( )
promql: Separate `Point` into `FPoint` and `HPoint`
In other words: Instead of having a “polymorphous” `Point` that can
either contain a float value or a histogram value, use an `FPoint` for
floats and an `HPoint` for histograms.
This seemingly small change has a _lot_ of repercussions throughout
the codebase.
The idea here is to avoid the increase in size of `Point` arrays that
happened after native histograms had been added.
The higher-level data structures (`Sample`, `Series`, etc.) are still
“polymorphous”. The same idea could be applied to them, but at each
step the trade-offs needed to be evaluated.
The idea with this change is to do the minimum necessary to get back
to pre-histogram performance for functions that do not touch
histograms. Here are comparisons for the `changes` function. The test
data doesn't include histograms yet. Ideally, there would be no change
in the benchmark result at all.
First runtime v2.39 compared to directly prior to this commit:
```
name old time/op new time/op delta
RangeQuery/expr=changes(a_one[1d]),steps=1-16 391µs ± 2% 542µs ± 1% +38.58% (p=0.000 n=9+8)
RangeQuery/expr=changes(a_one[1d]),steps=10-16 452µs ± 2% 617µs ± 2% +36.48% (p=0.000 n=10+10)
RangeQuery/expr=changes(a_one[1d]),steps=100-16 1.12ms ± 1% 1.36ms ± 2% +21.58% (p=0.000 n=8+10)
RangeQuery/expr=changes(a_one[1d]),steps=1000-16 7.83ms ± 1% 8.94ms ± 1% +14.21% (p=0.000 n=10+10)
RangeQuery/expr=changes(a_ten[1d]),steps=1-16 2.98ms ± 0% 3.30ms ± 1% +10.67% (p=0.000 n=9+10)
RangeQuery/expr=changes(a_ten[1d]),steps=10-16 3.66ms ± 1% 4.10ms ± 1% +11.82% (p=0.000 n=10+10)
RangeQuery/expr=changes(a_ten[1d]),steps=100-16 10.5ms ± 0% 11.8ms ± 1% +12.50% (p=0.000 n=8+10)
RangeQuery/expr=changes(a_ten[1d]),steps=1000-16 77.6ms ± 1% 87.4ms ± 1% +12.63% (p=0.000 n=9+9)
RangeQuery/expr=changes(a_hundred[1d]),steps=1-16 30.4ms ± 2% 32.8ms ± 1% +8.01% (p=0.000 n=10+10)
RangeQuery/expr=changes(a_hundred[1d]),steps=10-16 37.1ms ± 2% 40.6ms ± 2% +9.64% (p=0.000 n=10+10)
RangeQuery/expr=changes(a_hundred[1d]),steps=100-16 105ms ± 1% 117ms ± 1% +11.69% (p=0.000 n=10+10)
RangeQuery/expr=changes(a_hundred[1d]),steps=1000-16 783ms ± 3% 876ms ± 1% +11.83% (p=0.000 n=9+10)
```
And then runtime v2.39 compared to after this commit:
```
name old time/op new time/op delta
RangeQuery/expr=changes(a_one[1d]),steps=1-16 391µs ± 2% 547µs ± 1% +39.84% (p=0.000 n=9+8)
RangeQuery/expr=changes(a_one[1d]),steps=10-16 452µs ± 2% 616µs ± 2% +36.15% (p=0.000 n=10+10)
RangeQuery/expr=changes(a_one[1d]),steps=100-16 1.12ms ± 1% 1.26ms ± 1% +12.20% (p=0.000 n=8+10)
RangeQuery/expr=changes(a_one[1d]),steps=1000-16 7.83ms ± 1% 7.95ms ± 1% +1.59% (p=0.000 n=10+8)
RangeQuery/expr=changes(a_ten[1d]),steps=1-16 2.98ms ± 0% 3.38ms ± 2% +13.49% (p=0.000 n=9+10)
RangeQuery/expr=changes(a_ten[1d]),steps=10-16 3.66ms ± 1% 4.02ms ± 1% +9.80% (p=0.000 n=10+9)
RangeQuery/expr=changes(a_ten[1d]),steps=100-16 10.5ms ± 0% 10.8ms ± 1% +3.08% (p=0.000 n=8+10)
RangeQuery/expr=changes(a_ten[1d]),steps=1000-16 77.6ms ± 1% 78.1ms ± 1% +0.58% (p=0.035 n=9+10)
RangeQuery/expr=changes(a_hundred[1d]),steps=1-16 30.4ms ± 2% 33.5ms ± 4% +10.18% (p=0.000 n=10+10)
RangeQuery/expr=changes(a_hundred[1d]),steps=10-16 37.1ms ± 2% 40.0ms ± 1% +7.98% (p=0.000 n=10+10)
RangeQuery/expr=changes(a_hundred[1d]),steps=100-16 105ms ± 1% 107ms ± 1% +1.92% (p=0.000 n=10+10)
RangeQuery/expr=changes(a_hundred[1d]),steps=1000-16 783ms ± 3% 775ms ± 1% -1.02% (p=0.019 n=9+9)
```
In summary, the runtime doesn't really improve with this change for
queries with just a few steps. For queries with many steps, this
commit essentially reinstates the old performance. This is good
because the many-step queries are the one that matter most (longest
absolute runtime).
In terms of allocations, though, this commit doesn't make a dent at
all (numbers not shown). The reason is that most of the allocations
happen in the sampleRingIterator (in the storage package), which has
to be addressed in a separate commit.
Signed-off-by: beorn7 <beorn@grafana.com>
2 years ago
got . Floats = append ( got . Floats , FPoint { T : t , F : v } )
}
require . NoError ( t , it . Err ( ) )
require . Equal ( t , s , got )
}
} )
}
}
}
func TestRunTest ( t * testing . T ) {
testData := `
load 5 m
http_requests { job = "api-server" , instance = "0" , group = "production" } 0 + 10 x10
http_requests { job = "api-server" , instance = "1" , group = "production" } 0 + 20 x10
http_requests { job = "api-server" , instance = "0" , group = "canary" } 0 + 30 x10
http_requests { job = "api-server" , instance = "1" , group = "canary" } 0 + 40 x10
`
testCases := map [ string ] struct {
input string
expectedError string
} {
"instant query with expected float result" : {
input : testData + `
eval instant at 5 m sum by ( group ) ( http_requests )
{ group = "production" } 30
{ group = "canary" } 70
` ,
} ,
"instant query with unexpected float result" : {
input : testData + `
eval instant at 5 m sum by ( group ) ( http_requests )
{ group = "production" } 30
{ group = "canary" } 80
` ,
expectedError : ` error in eval sum by (group) (http_requests) (line 8): expected 80 for { group="canary"} but got 70 ` ,
} ,
"instant query with expected histogram result" : {
input : `
load 5 m
testmetric { { schema : - 1 sum : 4 count : 1 buckets : [ 1 ] offset : 1 } }
eval instant at 0 testmetric
testmetric { { schema : - 1 sum : 4 count : 1 buckets : [ 1 ] offset : 1 } }
` ,
} ,
"instant query with unexpected histogram result" : {
input : `
load 5 m
testmetric { { schema : - 1 sum : 4 count : 1 buckets : [ 1 ] offset : 1 } }
eval instant at 0 testmetric
testmetric { { schema : - 1 sum : 6 count : 1 buckets : [ 1 ] offset : 1 } }
` ,
expectedError : ` error in eval testmetric (line 5): expected {{ schema : -1 count : 1 sum : 6 offset : 1 buckets : [ 1 ] }} for { __name__="testmetric"} but got {{ schema : -1 count : 1 sum : 4 offset : 1 buckets : [ 1 ] }} ` ,
} ,
"instant query with float value returned when histogram expected" : {
input : `
load 5 m
testmetric 2
eval instant at 0 testmetric
testmetric { { } }
` ,
expectedError : ` error in eval testmetric (line 5): expected histogram {{ }} for { __name__="testmetric"} but got float value 2 ` ,
} ,
"instant query with histogram returned when float expected" : {
input : `
load 5 m
testmetric { { } }
eval instant at 0 testmetric
testmetric 2
` ,
expectedError : ` error in eval testmetric (line 5): expected float value 2.000000 for { __name__="testmetric"} but got histogram {{ }} ` ,
} ,
"instant query, but result has an unexpected series with a float value" : {
input : testData + `
eval instant at 5 m sum by ( group ) ( http_requests )
{ group = "production" } 30
` ,
expectedError : ` error in eval sum by (group) (http_requests) (line 8): unexpected metric { group="canary"} in result, has value 70 ` ,
} ,
"instant query, but result has an unexpected series with a histogram value" : {
input : `
load 5 m
testmetric { { } }
eval instant at 5 m testmetric
` ,
expectedError : ` error in eval testmetric (line 5): unexpected metric { __name__="testmetric"} in result, has value { count:0, sum:0} ` ,
} ,
"instant query, but result is missing a series" : {
input : testData + `
eval instant at 5 m sum by ( group ) ( http_requests )
{ group = "production" } 30
{ group = "canary" } 70
{ group = "test" } 100
` ,
expectedError : ` error in eval sum by (group) (http_requests) (line 8): expected metric { group="test"} with 3: [100.000000] not found ` ,
} ,
"instant query expected to fail, and query fails" : {
input : `
load 5 m
testmetric1 { src = "a" , dst = "b" } 0
testmetric2 { src = "a" , dst = "b" } 1
eval_fail instant at 0 m ceil ( { __name__ = ~ ' testmetric1 | testmetric2 ' } )
` ,
} ,
"instant query expected to fail, but query succeeds" : {
input : ` eval_fail instant at 0s vector(0) ` ,
expectedError : ` expected error evaluating query "vector(0)" (line 1) but got none ` ,
} ,
"instant query with results expected to match provided order, and result is in expected order" : {
input : testData + `
eval_ordered instant at 50 m sort ( http_requests )
http_requests { group = "production" , instance = "0" , job = "api-server" } 100
http_requests { group = "production" , instance = "1" , job = "api-server" } 200
http_requests { group = "canary" , instance = "0" , job = "api-server" } 300
http_requests { group = "canary" , instance = "1" , job = "api-server" } 400
` ,
} ,
"instant query with results expected to match provided order, but result is out of order" : {
input : testData + `
eval_ordered instant at 50 m sort ( http_requests )
http_requests { group = "production" , instance = "0" , job = "api-server" } 100
http_requests { group = "production" , instance = "1" , job = "api-server" } 200
http_requests { group = "canary" , instance = "1" , job = "api-server" } 400
http_requests { group = "canary" , instance = "0" , job = "api-server" } 300
` ,
expectedError : ` error in eval sort(http_requests) (line 8): expected metric { __name__="http_requests", group="canary", instance="0", job="api-server"} with [300.000000] at position 4 but was at 3 ` ,
} ,
"instant query with results expected to match provided order, but result has an unexpected series" : {
input : testData + `
eval_ordered instant at 50 m sort ( http_requests )
http_requests { group = "production" , instance = "0" , job = "api-server" } 100
http_requests { group = "production" , instance = "1" , job = "api-server" } 200
http_requests { group = "canary" , instance = "0" , job = "api-server" } 300
` ,
expectedError : ` error in eval sort(http_requests) (line 8): unexpected metric { __name__="http_requests", group="canary", instance="1", job="api-server"} in result, has value 400 ` ,
} ,
"instant query with invalid timestamp" : {
input : ` eval instant at abc123 vector(0) ` ,
expectedError : ` error in eval vector(0) (line 1): invalid timestamp definition "abc123": not a valid duration string: "abc123" ` ,
} ,
"range query with expected result" : {
input : testData + `
eval range from 0 to 10 m step 5 m sum by ( group ) ( http_requests )
{ group = "production" } 0 30 60
{ group = "canary" } 0 70 140
` ,
} ,
"range query with unexpected float value" : {
input : testData + `
eval range from 0 to 10 m step 5 m sum by ( group ) ( http_requests )
{ group = "production" } 0 30 60
{ group = "canary" } 0 80 140
` ,
expectedError : ` error in eval sum by (group) (http_requests) (line 8): expected float value at index 1 (t=300000) for { group="canary"} to be 80, but got 70 (result has 3 float points [0 @[0] 70 @[300000] 140 @[600000]] and 0 histogram points []) ` ,
} ,
"range query with expected histogram values" : {
input : `
load 5 m
testmetric { { schema : - 1 sum : 4 count : 1 buckets : [ 1 ] offset : 1 } } { { schema : - 1 sum : 5 count : 1 buckets : [ 1 ] offset : 1 } } { { schema : - 1 sum : 6 count : 1 buckets : [ 1 ] offset : 1 } }
eval range from 0 to 10 m step 5 m testmetric
testmetric { { schema : - 1 sum : 4 count : 1 buckets : [ 1 ] offset : 1 } } { { schema : - 1 sum : 5 count : 1 buckets : [ 1 ] offset : 1 } } { { schema : - 1 sum : 6 count : 1 buckets : [ 1 ] offset : 1 } }
` ,
} ,
"range query with unexpected histogram value" : {
input : `
load 5 m
testmetric { { schema : - 1 sum : 4 count : 1 buckets : [ 1 ] offset : 1 } } { { schema : - 1 sum : 5 count : 1 buckets : [ 1 ] offset : 1 } } { { schema : - 1 sum : 6 count : 1 buckets : [ 1 ] offset : 1 } }
eval range from 0 to 10 m step 5 m testmetric
testmetric { { schema : - 1 sum : 4 count : 1 buckets : [ 1 ] offset : 1 } } { { schema : - 1 sum : 7 count : 1 buckets : [ 1 ] offset : 1 } } { { schema : - 1 sum : 8 count : 1 buckets : [ 1 ] offset : 1 } }
` ,
expectedError : ` error in eval testmetric (line 5): expected histogram value at index 1 (t=300000) for { __name__="testmetric"} to be { count:1, sum:7, (1,4]:1}, but got { count:1, sum:5, (1,4]:1} (result has 0 float points [] and 3 histogram points [ { count:1, sum:4, (1,4]:1} @[0] { count:1, sum:5, (1,4]:1} @[300000] { count:1, sum:6, (1,4]:1} @[600000]]) ` ,
} ,
"range query with too many points for query time range" : {
input : testData + `
eval range from 0 to 10 m step 5 m sum by ( group ) ( http_requests )
{ group = "production" } 0 30 60 90
{ group = "canary" } 0 70 140
` ,
expectedError : ` error in eval sum by (group) (http_requests) (line 8): expected 4 points for { group="production"}, but query time range cannot return this many points ` ,
} ,
"range query with missing point in result" : {
input : `
load 5 m
testmetric 5
eval range from 0 to 6 m step 6 m testmetric
testmetric 5 10
` ,
expectedError : ` error in eval testmetric (line 5): expected 2 float points and 0 histogram points for { __name__="testmetric"}, but got 1 float point [5 @[0]] and 0 histogram points [] ` ,
} ,
"range query with extra point in result" : {
input : testData + `
eval range from 0 to 10 m step 5 m sum by ( group ) ( http_requests )
{ group = "production" } 0 30
{ group = "canary" } 0 70 140
` ,
expectedError : ` error in eval sum by (group) (http_requests) (line 8): expected 2 float points and 0 histogram points for { group="production"}, but got 3 float points [0 @[0] 30 @[300000] 60 @[600000]] and 0 histogram points [] ` ,
} ,
"range query, but result has an unexpected series" : {
input : testData + `
eval range from 0 to 10 m step 5 m sum by ( group ) ( http_requests )
{ group = "production" } 0 30 60
` ,
expectedError : ` error in eval sum by (group) (http_requests) (line 8): unexpected metric { group="canary"} in result, has 3 float points [0 @[0] 70 @[300000] 140 @[600000]] and 0 histogram points [] ` ,
} ,
"range query, but result is missing a series" : {
input : testData + `
eval range from 0 to 10 m step 5 m sum by ( group ) ( http_requests )
{ group = "production" } 0 30 60
{ group = "canary" } 0 70 140
{ group = "test" } 0 100 200
` ,
expectedError : ` error in eval sum by (group) (http_requests) (line 8): expected metric { group="test"} not found ` ,
} ,
"range query expected to fail, and query fails" : {
input : `
load 5 m
testmetric1 { src = "a" , dst = "b" } 0
testmetric2 { src = "a" , dst = "b" } 1
eval_fail range from 0 to 10 m step 5 m ceil ( { __name__ = ~ ' testmetric1 | testmetric2 ' } )
` ,
} ,
"range query expected to fail, but query succeeds" : {
input : ` eval_fail range from 0 to 10m step 5m vector(0) ` ,
expectedError : ` expected error evaluating query "vector(0)" (line 1) but got none ` ,
} ,
"range query with from and to timestamps in wrong order" : {
input : ` eval range from 10m to 9m step 5m vector(0) ` ,
expectedError : ` error in eval vector(0) (line 1): invalid test definition, end timestamp (9m) is before start timestamp (10m) ` ,
} ,
"range query with sparse output" : {
input : `
load 6 m
testmetric 1 _ 3
eval range from 0 to 18 m step 6 m testmetric
testmetric 1 _ 3
` ,
} ,
"range query with float value returned when no value expected" : {
input : `
load 6 m
testmetric 1 2 3
eval range from 0 to 18 m step 6 m testmetric
testmetric 1 _ 3
` ,
expectedError : ` error in eval testmetric (line 5): expected 2 float points and 0 histogram points for { __name__="testmetric"}, but got 3 float points [1 @[0] 2 @[360000] 3 @[720000]] and 0 histogram points [] ` ,
} ,
"range query with float value returned when histogram expected" : {
input : `
load 5 m
testmetric 2 3
eval range from 0 to 5 m step 5 m testmetric
testmetric { { } } { { } }
` ,
expectedError : ` error in eval testmetric (line 5): expected 0 float points and 2 histogram points for { __name__="testmetric"}, but got 2 float points [2 @[0] 3 @[300000]] and 0 histogram points [] ` ,
} ,
"range query with histogram returned when float expected" : {
input : `
load 5 m
testmetric { { } } { { } }
eval range from 0 to 5 m step 5 m testmetric
testmetric 2 3
` ,
expectedError : ` error in eval testmetric (line 5): expected 2 float points and 0 histogram points for { __name__="testmetric"}, but got 0 float points [] and 2 histogram points [ { count:0, sum:0} @[0] { count:0, sum:0} @[300000]] ` ,
} ,
"range query with expected mixed results" : {
input : `
load 6 m
testmetric { group = "a" } { { } } _ _
testmetric { group = "b" } _ _ 3
eval range from 0 to 12 m step 6 m sum ( testmetric )
{ } { { } } _ 3
` ,
} ,
"range query with mixed results and incorrect values" : {
input : `
load 5 m
testmetric 3 { { } }
eval range from 0 to 5 m step 5 m testmetric
testmetric { { } } 3
` ,
expectedError : ` error in eval testmetric (line 5): expected float value at index 0 for { __name__="testmetric"} to have timestamp 300000, but it had timestamp 0 (result has 1 float point [3 @[0]] and 1 histogram point [ { count:0, sum:0} @[300000]]) ` ,
} ,
}
for name , testCase := range testCases {
t . Run ( name , func ( t * testing . T ) {
err := runTest ( t , testCase . input , newTestEngine ( ) )
if testCase . expectedError == "" {
require . NoError ( t , err )
} else {
require . EqualError ( t , err , testCase . expectedError )
}
} )
}
}
func TestAssertMatrixSorted ( t * testing . T ) {
testCases := map [ string ] struct {
matrix Matrix
expectedError string
} {
"empty matrix" : {
matrix : Matrix { } ,
} ,
"matrix with one series" : {
matrix : Matrix {
Series { Metric : labels . FromStrings ( "the_label" , "value_1" ) } ,
} ,
} ,
"matrix with two series, series in sorted order" : {
matrix : Matrix {
Series { Metric : labels . FromStrings ( "the_label" , "value_1" ) } ,
Series { Metric : labels . FromStrings ( "the_label" , "value_2" ) } ,
} ,
} ,
"matrix with two series, series in reverse order" : {
matrix : Matrix {
Series { Metric : labels . FromStrings ( "the_label" , "value_2" ) } ,
Series { Metric : labels . FromStrings ( "the_label" , "value_1" ) } ,
} ,
expectedError : ` matrix results should always be sorted by labels, but matrix is not sorted: series at index 1 with labels { the_label="value_1"} sorts before series at index 0 with labels { the_label="value_2"} ` ,
} ,
"matrix with three series, series in sorted order" : {
matrix : Matrix {
Series { Metric : labels . FromStrings ( "the_label" , "value_1" ) } ,
Series { Metric : labels . FromStrings ( "the_label" , "value_2" ) } ,
Series { Metric : labels . FromStrings ( "the_label" , "value_3" ) } ,
} ,
} ,
"matrix with three series, series not in sorted order" : {
matrix : Matrix {
Series { Metric : labels . FromStrings ( "the_label" , "value_1" ) } ,
Series { Metric : labels . FromStrings ( "the_label" , "value_3" ) } ,
Series { Metric : labels . FromStrings ( "the_label" , "value_2" ) } ,
} ,
expectedError : ` matrix results should always be sorted by labels, but matrix is not sorted: series at index 2 with labels { the_label="value_2"} sorts before series at index 1 with labels { the_label="value_3"} ` ,
} ,
}
for name , testCase := range testCases {
t . Run ( name , func ( t * testing . T ) {
err := assertMatrixSorted ( testCase . matrix )
if testCase . expectedError == "" {
require . NoError ( t , err )
} else {
require . EqualError ( t , err , testCase . expectedError )
}
} )
}
}