2021-03-16 02:47:45 -07:00
// Copyright 2020 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 tsdb
import (
"reflect"
"strconv"
2021-05-06 13:53:52 -07:00
"strings"
2021-03-16 02:47:45 -07:00
"testing"
"github.com/stretchr/testify/require"
"github.com/prometheus/prometheus/pkg/exemplar"
"github.com/prometheus/prometheus/pkg/labels"
"github.com/prometheus/prometheus/storage"
)
2021-05-19 18:51:45 -07:00
// Tests the same exemplar cases as AddExemplar, but specifically the ValidateExemplar function so it can be relied on externally.
2021-05-06 13:53:52 -07:00
func TestValidateExemplar ( t * testing . T ) {
2021-03-16 02:47:45 -07:00
exs , err := NewCircularExemplarStorage ( 2 , nil )
require . NoError ( t , err )
es := exs . ( * CircularExemplarStorage )
l := labels . Labels {
{ Name : "service" , Value : "asdf" } ,
}
e := exemplar . Exemplar {
Labels : labels . Labels {
labels . Label {
Name : "traceID" ,
Value : "qwerty" ,
} ,
} ,
Value : 0.1 ,
Ts : 1 ,
}
2021-05-06 13:53:52 -07:00
require . NoError ( t , es . ValidateExemplar ( l , e ) )
require . NoError ( t , es . AddExemplar ( l , e ) )
e2 := exemplar . Exemplar {
Labels : labels . Labels {
labels . Label {
Name : "traceID" ,
Value : "zxcvb" ,
} ,
} ,
Value : 0.1 ,
Ts : 2 ,
}
require . NoError ( t , es . ValidateExemplar ( l , e2 ) )
require . NoError ( t , es . AddExemplar ( l , e2 ) )
require . Equal ( t , es . ValidateExemplar ( l , e2 ) , storage . ErrDuplicateExemplar , "error is expected attempting to validate duplicate exemplar" )
e3 := e2
e3 . Ts = 3
require . Equal ( t , es . ValidateExemplar ( l , e3 ) , storage . ErrDuplicateExemplar , "error is expected when attempting to add duplicate exemplar, even with different timestamp" )
e3 . Ts = 1
e3 . Value = 0.3
require . Equal ( t , es . ValidateExemplar ( l , e3 ) , storage . ErrOutOfOrderExemplar )
e4 := exemplar . Exemplar {
Labels : labels . Labels {
labels . Label {
Name : "a" ,
Value : strings . Repeat ( "b" , exemplar . ExemplarMaxLabelSetLength ) ,
} ,
} ,
Value : 0.1 ,
Ts : 2 ,
}
require . Equal ( t , storage . ErrExemplarLabelLength , es . ValidateExemplar ( l , e4 ) )
}
func TestAddExemplar ( t * testing . T ) {
exs , err := NewCircularExemplarStorage ( 2 , nil )
2021-03-16 02:47:45 -07:00
require . NoError ( t , err )
2021-05-06 13:53:52 -07:00
es := exs . ( * CircularExemplarStorage )
l := labels . Labels {
{ Name : "service" , Value : "asdf" } ,
}
e := exemplar . Exemplar {
Labels : labels . Labels {
labels . Label {
Name : "traceID" ,
Value : "qwerty" ,
} ,
} ,
Value : 0.1 ,
Ts : 1 ,
}
require . NoError ( t , es . AddExemplar ( l , e ) )
2021-04-16 05:44:53 -07:00
require . Equal ( t , es . index [ l . String ( ) ] . newest , 0 , "exemplar was not stored correctly" )
2021-03-16 02:47:45 -07:00
e2 := exemplar . Exemplar {
Labels : labels . Labels {
labels . Label {
Name : "traceID" ,
Value : "zxcvb" ,
} ,
} ,
Value : 0.1 ,
Ts : 2 ,
}
2021-05-06 13:53:52 -07:00
require . NoError ( t , es . AddExemplar ( l , e2 ) )
2021-04-16 05:44:53 -07:00
require . Equal ( t , es . index [ l . String ( ) ] . newest , 1 , "exemplar was not stored correctly, location of newest exemplar for series in index did not update" )
require . True ( t , es . exemplars [ es . index [ l . String ( ) ] . newest ] . exemplar . Equals ( e2 ) , "exemplar was not stored correctly, expected %+v got: %+v" , e2 , es . exemplars [ es . index [ l . String ( ) ] . newest ] . exemplar )
2021-03-16 02:47:45 -07:00
2021-05-06 13:53:52 -07:00
require . NoError ( t , es . AddExemplar ( l , e2 ) , "no error is expected attempting to add duplicate exemplar" )
2021-03-16 02:47:45 -07:00
e3 := e2
e3 . Ts = 3
2021-05-06 13:53:52 -07:00
require . NoError ( t , es . AddExemplar ( l , e3 ) , "no error is expected when attempting to add duplicate exemplar, even with different timestamp" )
2021-03-16 02:47:45 -07:00
e3 . Ts = 1
e3 . Value = 0.3
2021-05-06 13:53:52 -07:00
require . Equal ( t , storage . ErrOutOfOrderExemplar , es . AddExemplar ( l , e3 ) )
e4 := exemplar . Exemplar {
Labels : labels . Labels {
labels . Label {
Name : "a" ,
Value : strings . Repeat ( "b" , exemplar . ExemplarMaxLabelSetLength ) ,
} ,
} ,
Value : 0.1 ,
Ts : 2 ,
}
require . Equal ( t , storage . ErrExemplarLabelLength , es . AddExemplar ( l , e4 ) )
2021-03-16 02:47:45 -07:00
}
func TestStorageOverflow ( t * testing . T ) {
// Test that circular buffer index and assignment
// works properly, adding more exemplars than can
// be stored and then querying for them.
exs , err := NewCircularExemplarStorage ( 5 , nil )
require . NoError ( t , err )
es := exs . ( * CircularExemplarStorage )
l := labels . Labels {
{ Name : "service" , Value : "asdf" } ,
}
var eList [ ] exemplar . Exemplar
for i := 0 ; i < len ( es . exemplars ) + 1 ; i ++ {
e := exemplar . Exemplar {
Labels : labels . Labels {
labels . Label {
Name : "traceID" ,
Value : "a" ,
} ,
} ,
Value : float64 ( i + 1 ) / 10 ,
Ts : int64 ( 101 + i ) ,
}
es . AddExemplar ( l , e )
eList = append ( eList , e )
}
require . True ( t , ( es . exemplars [ 0 ] . exemplar . Ts == 106 ) , "exemplar was not stored correctly" )
m , err := labels . NewMatcher ( labels . MatchEqual , l [ 0 ] . Name , l [ 0 ] . Value )
require . NoError ( t , err , "error creating label matcher for exemplar query" )
ret , err := es . Select ( 100 , 110 , [ ] * labels . Matcher { m } )
require . NoError ( t , err )
require . True ( t , len ( ret ) == 1 , "select should have returned samples for a single series only" )
require . True ( t , reflect . DeepEqual ( eList [ 1 : ] , ret [ 0 ] . Exemplars ) , "select did not return expected exemplars\n\texpected: %+v\n\tactual: %+v\n" , eList [ 1 : ] , ret [ 0 ] . Exemplars )
}
func TestSelectExemplar ( t * testing . T ) {
exs , err := NewCircularExemplarStorage ( 5 , nil )
require . NoError ( t , err )
es := exs . ( * CircularExemplarStorage )
2021-04-16 05:44:53 -07:00
l := labels . Labels { { Name : "service" , Value : "asdf" } }
2021-03-16 02:47:45 -07:00
e := exemplar . Exemplar {
Labels : labels . Labels {
labels . Label {
Name : "traceID" ,
Value : "qwerty" ,
} ,
} ,
Value : 0.1 ,
Ts : 12 ,
}
err = es . AddExemplar ( l , e )
require . NoError ( t , err , "adding exemplar failed" )
require . True ( t , reflect . DeepEqual ( es . exemplars [ 0 ] . exemplar , e ) , "exemplar was not stored correctly" )
m , err := labels . NewMatcher ( labels . MatchEqual , l [ 0 ] . Name , l [ 0 ] . Value )
require . NoError ( t , err , "error creating label matcher for exemplar query" )
ret , err := es . Select ( 0 , 100 , [ ] * labels . Matcher { m } )
require . NoError ( t , err )
require . True ( t , len ( ret ) == 1 , "select should have returned samples for a single series only" )
expectedResult := [ ] exemplar . Exemplar { e }
require . True ( t , reflect . DeepEqual ( expectedResult , ret [ 0 ] . Exemplars ) , "select did not return expected exemplars\n\texpected: %+v\n\tactual: %+v\n" , expectedResult , ret [ 0 ] . Exemplars )
}
func TestSelectExemplar_MultiSeries ( t * testing . T ) {
exs , err := NewCircularExemplarStorage ( 5 , nil )
require . NoError ( t , err )
es := exs . ( * CircularExemplarStorage )
l1 := labels . Labels {
{ Name : "__name__" , Value : "test_metric" } ,
{ Name : "service" , Value : "asdf" } ,
}
l2 := labels . Labels {
{ Name : "__name__" , Value : "test_metric2" } ,
{ Name : "service" , Value : "qwer" } ,
}
for i := 0 ; i < len ( es . exemplars ) ; i ++ {
e1 := exemplar . Exemplar {
Labels : labels . Labels {
labels . Label {
Name : "traceID" ,
Value : "a" ,
} ,
} ,
Value : float64 ( i + 1 ) / 10 ,
Ts : int64 ( 101 + i ) ,
}
err = es . AddExemplar ( l1 , e1 )
require . NoError ( t , err )
e2 := exemplar . Exemplar {
Labels : labels . Labels {
labels . Label {
Name : "traceID" ,
Value : "b" ,
} ,
} ,
Value : float64 ( i + 1 ) / 10 ,
Ts : int64 ( 101 + i ) ,
}
err = es . AddExemplar ( l2 , e2 )
require . NoError ( t , err )
}
m , err := labels . NewMatcher ( labels . MatchEqual , l2 [ 0 ] . Name , l2 [ 0 ] . Value )
require . NoError ( t , err , "error creating label matcher for exemplar query" )
ret , err := es . Select ( 100 , 200 , [ ] * labels . Matcher { m } )
require . NoError ( t , err )
require . True ( t , len ( ret ) == 1 , "select should have returned samples for a single series only" )
require . True ( t , len ( ret [ 0 ] . Exemplars ) == 3 , "didn't get expected 8 exemplars, got %d" , len ( ret [ 0 ] . Exemplars ) )
m , err = labels . NewMatcher ( labels . MatchEqual , l1 [ 0 ] . Name , l1 [ 0 ] . Value )
require . NoError ( t , err , "error creating label matcher for exemplar query" )
ret , err = es . Select ( 100 , 200 , [ ] * labels . Matcher { m } )
require . NoError ( t , err )
require . True ( t , len ( ret ) == 1 , "select should have returned samples for a single series only" )
require . True ( t , len ( ret [ 0 ] . Exemplars ) == 2 , "didn't get expected 8 exemplars, got %d" , len ( ret [ 0 ] . Exemplars ) )
}
func TestSelectExemplar_TimeRange ( t * testing . T ) {
lenEs := 5
exs , err := NewCircularExemplarStorage ( lenEs , nil )
require . NoError ( t , err )
es := exs . ( * CircularExemplarStorage )
l := labels . Labels {
{ Name : "service" , Value : "asdf" } ,
}
for i := 0 ; i < lenEs ; i ++ {
err := es . AddExemplar ( l , exemplar . Exemplar {
Labels : labels . Labels {
labels . Label {
Name : "traceID" ,
Value : strconv . Itoa ( i ) ,
} ,
} ,
Value : 0.1 ,
Ts : int64 ( 101 + i ) ,
} )
require . NoError ( t , err )
2021-04-16 05:44:53 -07:00
require . Equal ( t , es . index [ l . String ( ) ] . newest , i , "exemplar was not stored correctly" )
2021-03-16 02:47:45 -07:00
}
m , err := labels . NewMatcher ( labels . MatchEqual , l [ 0 ] . Name , l [ 0 ] . Value )
require . NoError ( t , err , "error creating label matcher for exemplar query" )
ret , err := es . Select ( 102 , 104 , [ ] * labels . Matcher { m } )
require . NoError ( t , err )
require . True ( t , len ( ret ) == 1 , "select should have returned samples for a single series only" )
require . True ( t , len ( ret [ 0 ] . Exemplars ) == 3 , "didn't get expected two exemplars %d, %+v" , len ( ret [ 0 ] . Exemplars ) , ret )
}
// Test to ensure that even though a series matches more than one matcher from the
// query that it's exemplars are only included in the result a single time.
func TestSelectExemplar_DuplicateSeries ( t * testing . T ) {
exs , err := NewCircularExemplarStorage ( 4 , nil )
require . NoError ( t , err )
es := exs . ( * CircularExemplarStorage )
e := exemplar . Exemplar {
Labels : labels . Labels {
labels . Label {
Name : "traceID" ,
Value : "qwerty" ,
} ,
} ,
Value : 0.1 ,
Ts : 12 ,
}
l := labels . Labels {
{ Name : "service" , Value : "asdf" } ,
{ Name : "cluster" , Value : "us-central1" } ,
}
// Lets just assume somehow the PromQL expression generated two separate lists of matchers,
// both of which can select this particular series.
m := [ ] [ ] * labels . Matcher {
{
labels . MustNewMatcher ( labels . MatchEqual , l [ 0 ] . Name , l [ 0 ] . Value ) ,
} ,
{
labels . MustNewMatcher ( labels . MatchEqual , l [ 1 ] . Name , l [ 1 ] . Value ) ,
} ,
}
err = es . AddExemplar ( l , e )
require . NoError ( t , err , "adding exemplar failed" )
require . True ( t , reflect . DeepEqual ( es . exemplars [ 0 ] . exemplar , e ) , "exemplar was not stored correctly" )
ret , err := es . Select ( 0 , 100 , m ... )
require . NoError ( t , err )
require . True ( t , len ( ret ) == 1 , "select should have returned samples for a single series only" )
}
func TestIndexOverwrite ( t * testing . T ) {
exs , err := NewCircularExemplarStorage ( 2 , nil )
require . NoError ( t , err )
es := exs . ( * CircularExemplarStorage )
l1 := labels . Labels {
{ Name : "service" , Value : "asdf" } ,
}
l2 := labels . Labels {
{ Name : "service" , Value : "qwer" } ,
}
err = es . AddExemplar ( l1 , exemplar . Exemplar { Value : 1 , Ts : 1 } )
require . NoError ( t , err )
err = es . AddExemplar ( l2 , exemplar . Exemplar { Value : 2 , Ts : 2 } )
require . NoError ( t , err )
err = es . AddExemplar ( l2 , exemplar . Exemplar { Value : 3 , Ts : 3 } )
require . NoError ( t , err )
// Ensure index GC'ing is taking place, there should no longer be any
// index entry for series l1 since we just wrote two exemplars for series l2.
_ , ok := es . index [ l1 . String ( ) ]
require . False ( t , ok )
2021-05-05 10:51:16 -07:00
require . Equal ( t , & indexEntry { 1 , 0 , l2 } , es . index [ l2 . String ( ) ] )
2021-03-16 02:47:45 -07:00
err = es . AddExemplar ( l1 , exemplar . Exemplar { Value : 4 , Ts : 4 } )
require . NoError ( t , err )
i := es . index [ l2 . String ( ) ]
2021-05-05 10:51:16 -07:00
require . Equal ( t , & indexEntry { 0 , 0 , l2 } , i )
2021-03-16 02:47:45 -07:00
}