2017-04-19 13:43:09 +01:00
// Copyright 2017 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.
2016-12-25 01:40:28 +01:00
package storage
import (
"math/rand"
2016-12-30 10:45:56 +01:00
"sort"
2016-12-25 01:40:28 +01:00
"testing"
2017-04-27 10:23:34 +02:00
"github.com/prometheus/prometheus/pkg/labels"
2020-02-06 15:58:38 +00:00
"github.com/prometheus/prometheus/tsdb/chunkenc"
2019-08-08 19:36:42 -06:00
"github.com/prometheus/prometheus/util/testutil"
2016-12-25 01:40:28 +01:00
)
func TestSampleRing ( t * testing . T ) {
cases := [ ] struct {
input [ ] int64
delta int64
size int
} {
{
input : [ ] int64 { 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 } ,
delta : 2 ,
size : 1 ,
} ,
{
input : [ ] int64 { 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 } ,
delta : 2 ,
size : 2 ,
} ,
{
input : [ ] int64 { 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 } ,
delta : 7 ,
size : 3 ,
} ,
{
input : [ ] int64 { 1 , 2 , 3 , 4 , 5 , 16 , 17 , 18 , 19 , 20 } ,
delta : 7 ,
size : 1 ,
} ,
2018-03-12 13:16:59 +00:00
{
input : [ ] int64 { 1 , 2 , 3 , 4 , 6 } ,
delta : 4 ,
size : 4 ,
} ,
2016-12-25 01:40:28 +01:00
}
for _ , c := range cases {
r := newSampleRing ( c . delta , c . size )
input := [ ] sample { }
for _ , t := range c . input {
input = append ( input , sample {
t : t ,
v : float64 ( rand . Intn ( 100 ) ) ,
} )
}
for i , s := range input {
r . add ( s . t , s . v )
buffered := r . samples ( )
for _ , sold := range input [ : i ] {
found := false
for _ , bs := range buffered {
if bs . t == sold . t && bs . v == sold . v {
found = true
break
}
}
2019-10-02 01:28:08 -04:00
if found {
testutil . Assert ( t , sold . t >= s . t - c . delta , "%d: unexpected sample %d in buffer; buffer %v" , i , sold . t , buffered )
} else {
testutil . Assert ( t , sold . t < s . t - c . delta , "%d: expected sample %d to be in buffer but was not; buffer %v" , i , sold . t , buffered )
2016-12-25 01:40:28 +01:00
}
}
}
}
}
func TestBufferedSeriesIterator ( t * testing . T ) {
var it * BufferedSeriesIterator
bufferEq := func ( exp [ ] sample ) {
var b [ ] sample
bit := it . Buffer ( )
for bit . Next ( ) {
2017-01-02 13:33:37 +01:00
t , v := bit . At ( )
2016-12-25 01:40:28 +01:00
b = append ( b , sample { t : t , v : v } )
}
2019-08-08 19:36:42 -06:00
testutil . Equals ( t , exp , b , "buffer mismatch" )
2016-12-25 01:40:28 +01:00
}
sampleEq := func ( ets int64 , ev float64 ) {
ts , v := it . Values ( )
2019-08-08 19:36:42 -06:00
testutil . Equals ( t , ets , ts , "timestamp mismatch" )
testutil . Equals ( t , ev , v , "value mismatch" )
2016-12-25 01:40:28 +01:00
}
2018-07-18 06:10:28 +02:00
it = NewBufferIterator ( newListSeriesIterator ( [ ] sample {
2016-12-25 01:40:28 +01:00
{ t : 1 , v : 2 } ,
{ t : 2 , v : 3 } ,
{ t : 3 , v : 4 } ,
{ t : 4 , v : 5 } ,
{ t : 5 , v : 6 } ,
{ t : 99 , v : 8 } ,
{ t : 100 , v : 9 } ,
{ t : 101 , v : 10 } ,
} ) , 2 )
2019-08-08 19:36:42 -06:00
testutil . Assert ( t , it . Seek ( - 123 ) , "seek failed" )
2016-12-25 01:40:28 +01:00
sampleEq ( 1 , 2 )
bufferEq ( nil )
2019-08-08 19:36:42 -06:00
testutil . Assert ( t , it . Next ( ) , "next failed" )
2016-12-25 01:40:28 +01:00
sampleEq ( 2 , 3 )
bufferEq ( [ ] sample { { t : 1 , v : 2 } } )
2019-08-08 19:36:42 -06:00
testutil . Assert ( t , it . Next ( ) , "next failed" )
testutil . Assert ( t , it . Next ( ) , "next failed" )
testutil . Assert ( t , it . Next ( ) , "next failed" )
2016-12-25 01:40:28 +01:00
sampleEq ( 5 , 6 )
bufferEq ( [ ] sample { { t : 2 , v : 3 } , { t : 3 , v : 4 } , { t : 4 , v : 5 } } )
2019-08-08 19:36:42 -06:00
testutil . Assert ( t , it . Seek ( 5 ) , "seek failed" )
2016-12-25 01:40:28 +01:00
sampleEq ( 5 , 6 )
bufferEq ( [ ] sample { { t : 2 , v : 3 } , { t : 3 , v : 4 } , { t : 4 , v : 5 } } )
2019-08-08 19:36:42 -06:00
testutil . Assert ( t , it . Seek ( 101 ) , "seek failed" )
2016-12-25 01:40:28 +01:00
sampleEq ( 101 , 10 )
bufferEq ( [ ] sample { { t : 99 , v : 8 } , { t : 100 , v : 9 } } )
2019-08-08 19:36:42 -06:00
testutil . Assert ( t , ! it . Next ( ) , "next succeeded unexpectedly" )
2016-12-25 01:40:28 +01:00
}
2016-12-30 10:45:56 +01:00
2017-06-13 10:52:27 +05:30
// At() should not be called once Next() returns false.
func TestBufferedSeriesIteratorNoBadAt ( t * testing . T ) {
done := false
m := & mockSeriesIterator {
seek : func ( int64 ) bool { return false } ,
at : func ( ) ( int64 , float64 ) {
2019-08-08 19:36:42 -06:00
testutil . Assert ( t , ! done , "unexpectedly done" )
2017-06-13 10:52:27 +05:30
done = true
return 0 , 0
} ,
next : func ( ) bool { return ! done } ,
err : func ( ) error { return nil } ,
}
2018-07-18 06:10:28 +02:00
it := NewBufferIterator ( m , 60 )
2017-06-13 10:52:27 +05:30
it . Next ( )
it . Next ( )
}
2017-03-14 10:57:34 +01:00
func BenchmarkBufferedSeriesIterator ( b * testing . B ) {
// Simulate a 5 minute rate.
2018-12-18 12:22:33 +01:00
it := NewBufferIterator ( newFakeSeriesIterator ( int64 ( b . N ) , 30 ) , 5 * 60 )
2017-03-14 10:57:34 +01:00
b . SetBytes ( int64 ( b . N * 16 ) )
b . ReportAllocs ( )
b . ResetTimer ( )
for it . Next ( ) {
// scan everything
}
2019-08-08 19:36:42 -06:00
testutil . Ok ( b , it . Err ( ) )
2017-03-14 10:57:34 +01:00
}
2016-12-30 10:45:56 +01:00
type mockSeriesIterator struct {
2017-06-13 10:52:27 +05:30
seek func ( int64 ) bool
at func ( ) ( int64 , float64 )
next func ( ) bool
err func ( ) error
2016-12-30 10:45:56 +01:00
}
2017-06-13 10:52:27 +05:30
func ( m * mockSeriesIterator ) Seek ( t int64 ) bool { return m . seek ( t ) }
func ( m * mockSeriesIterator ) At ( ) ( int64 , float64 ) { return m . at ( ) }
func ( m * mockSeriesIterator ) Next ( ) bool { return m . next ( ) }
func ( m * mockSeriesIterator ) Err ( ) error { return m . err ( ) }
2016-12-30 10:45:56 +01:00
type mockSeries struct {
labels func ( ) labels . Labels
2020-02-06 15:58:38 +00:00
iterator func ( ) chunkenc . Iterator
2016-12-30 10:45:56 +01:00
}
2018-01-26 11:01:59 +00:00
func newMockSeries ( lset labels . Labels , samples [ ] sample ) Series {
return & mockSeries {
labels : func ( ) labels . Labels {
return lset
} ,
2020-02-06 15:58:38 +00:00
iterator : func ( ) chunkenc . Iterator {
2018-01-26 11:01:59 +00:00
return newListSeriesIterator ( samples )
} ,
}
}
2020-02-06 15:58:38 +00:00
func ( m * mockSeries ) Labels ( ) labels . Labels { return m . labels ( ) }
func ( m * mockSeries ) Iterator ( ) chunkenc . Iterator { return m . iterator ( ) }
2016-12-30 10:45:56 +01:00
type listSeriesIterator struct {
list [ ] sample
idx int
}
func newListSeriesIterator ( list [ ] sample ) * listSeriesIterator {
return & listSeriesIterator { list : list , idx : - 1 }
}
2017-01-02 13:33:37 +01:00
func ( it * listSeriesIterator ) At ( ) ( int64 , float64 ) {
2016-12-30 10:45:56 +01:00
s := it . list [ it . idx ]
return s . t , s . v
}
func ( it * listSeriesIterator ) Next ( ) bool {
it . idx ++
return it . idx < len ( it . list )
}
func ( it * listSeriesIterator ) Seek ( t int64 ) bool {
if it . idx == - 1 {
it . idx = 0
}
// Do binary search between current position and end.
it . idx = sort . Search ( len ( it . list ) - it . idx , func ( i int ) bool {
s := it . list [ i + it . idx ]
return s . t >= t
} )
return it . idx < len ( it . list )
}
func ( it * listSeriesIterator ) Err ( ) error {
return nil
}
2018-12-18 12:22:33 +01:00
type fakeSeriesIterator struct {
nsamples int64
step int64
idx int64
}
func newFakeSeriesIterator ( nsamples , step int64 ) * fakeSeriesIterator {
return & fakeSeriesIterator { nsamples : nsamples , step : step , idx : - 1 }
}
func ( it * fakeSeriesIterator ) At ( ) ( int64 , float64 ) {
return it . idx * it . step , 123 // value doesn't matter
}
func ( it * fakeSeriesIterator ) Next ( ) bool {
it . idx ++
return it . idx < it . nsamples
}
func ( it * fakeSeriesIterator ) Seek ( t int64 ) bool {
it . idx = t / it . step
return it . idx < it . nsamples
}
func ( it * fakeSeriesIterator ) Err ( ) error {
return nil
}