2017-04-10 20:59:45 +02: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-04 13:16:11 +01:00
package tsdb
2017-04-18 17:22:13 +01:00
import (
"io/ioutil"
2017-08-29 00:39:17 +02:00
"math"
2017-05-23 18:13:30 +05:30
"math/rand"
2017-04-18 17:22:13 +01:00
"os"
2017-08-29 00:39:17 +02:00
"sort"
2017-04-18 17:22:13 +01:00
"testing"
2017-05-17 16:43:01 +02:00
"github.com/pkg/errors"
2017-04-18 17:22:13 +01:00
"github.com/prometheus/tsdb/labels"
2017-04-20 14:24:35 +01:00
"github.com/stretchr/testify/require"
2017-04-18 17:22:13 +01:00
)
2017-08-29 00:39:17 +02:00
func openTestDB ( t testing . TB , opts * Options ) ( db * DB , close func ( ) ) {
tmpdir , _ := ioutil . TempDir ( "" , "test" )
db , err := Open ( tmpdir , nil , nil , opts )
require . NoError ( t , err )
// Do not close the test database by default as it will deadlock on test failures.
2017-08-30 18:34:54 +02:00
return db , func ( ) { os . RemoveAll ( tmpdir ) }
2017-08-29 00:39:17 +02:00
}
2017-04-20 14:24:35 +01:00
// Convert a SeriesSet into a form useable with reflect.DeepEqual.
2017-08-29 00:39:17 +02:00
func readSeriesSet ( t testing . TB , ss SeriesSet ) map [ string ] [ ] sample {
2017-04-20 14:24:35 +01:00
result := map [ string ] [ ] sample { }
for ss . Next ( ) {
series := ss . At ( )
samples := [ ] sample { }
it := series . Iterator ( )
for it . Next ( ) {
t , v := it . At ( )
samples = append ( samples , sample { t : t , v : v } )
}
2017-08-29 00:39:17 +02:00
require . NoError ( t , it . Err ( ) )
2017-04-20 14:24:35 +01:00
name := series . Labels ( ) . String ( )
result [ name ] = samples
}
2017-08-29 00:39:17 +02:00
require . NoError ( t , ss . Err ( ) )
return result
2017-04-20 14:24:35 +01:00
}
2017-04-18 17:22:13 +01:00
func TestDataAvailableOnlyAfterCommit ( t * testing . T ) {
2017-08-29 00:39:17 +02:00
db , close := openTestDB ( t , nil )
defer close ( )
2017-04-18 17:22:13 +01:00
app := db . Appender ( )
2017-08-29 00:39:17 +02:00
_ , err := app . Add ( labels . FromStrings ( "foo" , "bar" ) , 0 , 0 )
2017-04-20 14:24:35 +01:00
require . NoError ( t , err )
2017-04-18 17:22:13 +01:00
2017-10-09 15:21:46 +02:00
querier , err := db . Querier ( 0 , 1 )
require . NoError ( t , err )
2017-08-29 00:39:17 +02:00
seriesSet := readSeriesSet ( t , querier . Select ( labels . NewEqualMatcher ( "foo" , "bar" ) ) )
2017-04-20 14:24:35 +01:00
require . Equal ( t , seriesSet , map [ string ] [ ] sample { } )
2017-07-14 09:00:22 +02:00
require . NoError ( t , querier . Close ( ) )
2017-04-18 17:22:13 +01:00
err = app . Commit ( )
2017-04-20 14:24:35 +01:00
require . NoError ( t , err )
2017-04-18 17:22:13 +01:00
2017-10-09 15:21:46 +02:00
querier , err = db . Querier ( 0 , 1 )
require . NoError ( t , err )
2017-04-18 17:22:13 +01:00
defer querier . Close ( )
2017-08-29 00:39:17 +02:00
seriesSet = readSeriesSet ( t , querier . Select ( labels . NewEqualMatcher ( "foo" , "bar" ) ) )
2017-04-20 14:24:35 +01:00
require . Equal ( t , seriesSet , map [ string ] [ ] sample { ` { foo="bar"} ` : [ ] sample { { t : 0 , v : 0 } } } )
2017-04-18 17:22:13 +01:00
}
func TestDataNotAvailableAfterRollback ( t * testing . T ) {
2017-08-29 00:39:17 +02:00
db , close := openTestDB ( t , nil )
defer close ( )
2017-04-18 17:22:13 +01:00
app := db . Appender ( )
2017-08-29 00:39:17 +02:00
_ , err := app . Add ( labels . FromStrings ( "foo" , "bar" ) , 0 , 0 )
2017-04-20 14:24:35 +01:00
require . NoError ( t , err )
2017-04-18 17:22:13 +01:00
err = app . Rollback ( )
2017-04-20 14:24:35 +01:00
require . NoError ( t , err )
2017-04-18 17:22:13 +01:00
2017-10-09 15:21:46 +02:00
querier , err := db . Querier ( 0 , 1 )
require . NoError ( t , err )
2017-04-18 17:22:13 +01:00
defer querier . Close ( )
2017-08-29 00:39:17 +02:00
seriesSet := readSeriesSet ( t , querier . Select ( labels . NewEqualMatcher ( "foo" , "bar" ) ) )
2017-04-20 14:24:35 +01:00
require . Equal ( t , seriesSet , map [ string ] [ ] sample { } )
2017-04-18 17:22:13 +01:00
}
2017-04-28 15:24:28 +02:00
func TestDBAppenderAddRef ( t * testing . T ) {
2017-08-29 00:39:17 +02:00
db , close := openTestDB ( t , nil )
defer close ( )
2017-04-28 15:24:28 +02:00
2017-05-18 16:09:30 +02:00
app1 := db . Appender ( )
2017-04-28 15:24:28 +02:00
2017-09-05 11:45:18 +02:00
ref1 , err := app1 . Add ( labels . FromStrings ( "a" , "b" ) , 123 , 0 )
2017-04-28 15:24:28 +02:00
require . NoError ( t , err )
2017-09-05 11:45:18 +02:00
// Reference should already work before commit.
err = app1 . AddFast ( ref1 , 124 , 1 )
require . NoError ( t , err )
2017-05-17 16:43:01 +02:00
2017-05-18 16:09:30 +02:00
err = app1 . Commit ( )
2017-05-17 16:43:01 +02:00
require . NoError ( t , err )
2017-05-18 16:09:30 +02:00
app2 := db . Appender ( )
2017-09-05 11:45:18 +02:00
// first ref should already work in next transaction.
err = app2 . AddFast ( ref1 , 125 , 0 )
2017-05-17 16:43:01 +02:00
require . NoError ( t , err )
2017-09-05 11:45:18 +02:00
ref2 , err := app2 . Add ( labels . FromStrings ( "a" , "b" ) , 133 , 1 )
require . NoError ( t , err )
require . True ( t , ref1 == ref2 )
2017-04-28 15:24:28 +02:00
// Reference must be valid to add another sample.
2017-09-05 11:45:18 +02:00
err = app2 . AddFast ( ref2 , 143 , 2 )
2017-04-28 15:24:28 +02:00
require . NoError ( t , err )
2017-09-05 11:45:18 +02:00
err = app2 . AddFast ( 9999999 , 1 , 1 )
2017-05-17 16:43:01 +02:00
require . EqualError ( t , errors . Cause ( err ) , ErrNotFound . Error ( ) )
2017-08-29 00:39:17 +02:00
require . NoError ( t , app2 . Commit ( ) )
2017-10-09 15:21:46 +02:00
q , err := db . Querier ( 0 , 200 )
require . NoError ( t , err )
2017-08-29 00:39:17 +02:00
res := readSeriesSet ( t , q . Select ( labels . NewEqualMatcher ( "a" , "b" ) ) )
require . Equal ( t , map [ string ] [ ] sample {
labels . FromStrings ( "a" , "b" ) . String ( ) : [ ] sample {
{ t : 123 , v : 0 } ,
2017-09-05 11:45:18 +02:00
{ t : 124 , v : 1 } ,
{ t : 125 , v : 0 } ,
2017-08-29 00:39:17 +02:00
{ t : 133 , v : 1 } ,
{ t : 143 , v : 2 } ,
} ,
} , res )
require . NoError ( t , q . Close ( ) )
2017-04-28 15:24:28 +02:00
}
2017-05-23 18:13:30 +05:30
func TestDeleteSimple ( t * testing . T ) {
numSamples := int64 ( 10 )
2017-08-29 00:39:17 +02:00
db , close := openTestDB ( t , nil )
defer close ( )
2017-05-23 18:13:30 +05:30
app := db . Appender ( )
smpls := make ( [ ] float64 , numSamples )
for i := int64 ( 0 ) ; i < numSamples ; i ++ {
smpls [ i ] = rand . Float64 ( )
app . Add ( labels . Labels { { "a" , "b" } } , i , smpls [ i ] )
}
require . NoError ( t , app . Commit ( ) )
cases := [ ] struct {
2017-08-25 13:41:46 +05:30
intervals Intervals
2017-05-23 18:13:30 +05:30
remaint [ ] int64
} {
{
2017-08-25 13:41:46 +05:30
intervals : Intervals { { 1 , 3 } , { 4 , 7 } } ,
2017-05-23 18:13:30 +05:30
remaint : [ ] int64 { 0 , 8 , 9 } ,
} ,
}
Outer :
for _ , c := range cases {
// TODO(gouthamve): Reset the tombstones somehow.
// Delete the ranges.
for _ , r := range c . intervals {
2017-08-25 13:41:46 +05:30
require . NoError ( t , db . Delete ( r . Mint , r . Maxt , labels . NewEqualMatcher ( "a" , "b" ) ) )
2017-05-23 18:13:30 +05:30
}
// Compare the result.
2017-10-09 15:21:46 +02:00
q , err := db . Querier ( 0 , numSamples )
require . NoError ( t , err )
2017-05-23 18:13:30 +05:30
res := q . Select ( labels . NewEqualMatcher ( "a" , "b" ) )
expSamples := make ( [ ] sample , 0 , len ( c . remaint ) )
for _ , ts := range c . remaint {
expSamples = append ( expSamples , sample { ts , smpls [ ts ] } )
}
expss := newListSeriesSet ( [ ] Series {
newSeries ( map [ string ] string { "a" : "b" } , expSamples ) ,
} )
if len ( expSamples ) == 0 {
require . False ( t , res . Next ( ) )
continue
}
for {
eok , rok := expss . Next ( ) , res . Next ( )
require . Equal ( t , eok , rok , "next" )
if ! eok {
continue Outer
}
sexp := expss . At ( )
sres := res . At ( )
require . Equal ( t , sexp . Labels ( ) , sres . Labels ( ) , "labels" )
smplExp , errExp := expandSeriesIterator ( sexp . Iterator ( ) )
smplRes , errRes := expandSeriesIterator ( sres . Iterator ( ) )
require . Equal ( t , errExp , errRes , "samples error" )
require . Equal ( t , smplExp , smplRes , "samples" )
}
}
}
2017-08-29 00:39:17 +02:00
func TestAmendDatapointCausesError ( t * testing . T ) {
db , close := openTestDB ( t , nil )
defer close ( )
app := db . Appender ( )
_ , err := app . Add ( labels . Labels { } , 0 , 0 )
require . NoError ( t , err , "Failed to add sample" )
require . NoError ( t , app . Commit ( ) , "Unexpected error committing appender" )
app = db . Appender ( )
_ , err = app . Add ( labels . Labels { } , 0 , 1 )
require . Equal ( t , ErrAmendSample , err )
require . NoError ( t , app . Rollback ( ) , "Unexpected error rolling back appender" )
}
func TestDuplicateNaNDatapointNoAmendError ( t * testing . T ) {
db , close := openTestDB ( t , nil )
defer close ( )
app := db . Appender ( )
_ , err := app . Add ( labels . Labels { } , 0 , math . NaN ( ) )
require . NoError ( t , err , "Failed to add sample" )
require . NoError ( t , app . Commit ( ) , "Unexpected error committing appender" )
app = db . Appender ( )
_ , err = app . Add ( labels . Labels { } , 0 , math . NaN ( ) )
require . NoError ( t , err )
}
func TestNonDuplicateNaNDatapointsCausesAmendError ( t * testing . T ) {
db , close := openTestDB ( t , nil )
defer close ( )
app := db . Appender ( )
_ , err := app . Add ( labels . Labels { } , 0 , math . Float64frombits ( 0x7ff0000000000001 ) )
require . NoError ( t , err , "Failed to add sample" )
require . NoError ( t , app . Commit ( ) , "Unexpected error committing appender" )
app = db . Appender ( )
_ , err = app . Add ( labels . Labels { } , 0 , math . Float64frombits ( 0x7ff0000000000002 ) )
require . Equal ( t , ErrAmendSample , err )
}
func TestSkippingInvalidValuesInSameTxn ( t * testing . T ) {
db , close := openTestDB ( t , nil )
defer close ( )
// Append AmendedValue.
app := db . Appender ( )
_ , err := app . Add ( labels . Labels { { "a" , "b" } } , 0 , 1 )
require . NoError ( t , err )
_ , err = app . Add ( labels . Labels { { "a" , "b" } } , 0 , 2 )
require . NoError ( t , err )
require . NoError ( t , app . Commit ( ) )
// Make sure the right value is stored.
2017-10-09 15:21:46 +02:00
q , err := db . Querier ( 0 , 10 )
require . NoError ( t , err )
2017-08-29 00:39:17 +02:00
ss := q . Select ( labels . NewEqualMatcher ( "a" , "b" ) )
ssMap := readSeriesSet ( t , ss )
require . Equal ( t , map [ string ] [ ] sample {
labels . New ( labels . Label { "a" , "b" } ) . String ( ) : [ ] sample { { 0 , 1 } } ,
} , ssMap )
require . NoError ( t , q . Close ( ) )
// Append Out of Order Value.
app = db . Appender ( )
_ , err = app . Add ( labels . Labels { { "a" , "b" } } , 10 , 3 )
require . NoError ( t , err )
_ , err = app . Add ( labels . Labels { { "a" , "b" } } , 7 , 5 )
require . NoError ( t , err )
require . NoError ( t , app . Commit ( ) )
2017-10-09 15:21:46 +02:00
q , err = db . Querier ( 0 , 10 )
require . NoError ( t , err )
2017-08-29 00:39:17 +02:00
ss = q . Select ( labels . NewEqualMatcher ( "a" , "b" ) )
ssMap = readSeriesSet ( t , ss )
require . Equal ( t , map [ string ] [ ] sample {
labels . New ( labels . Label { "a" , "b" } ) . String ( ) : [ ] sample { { 0 , 1 } , { 10 , 3 } } ,
} , ssMap )
require . NoError ( t , q . Close ( ) )
}
2017-10-03 13:06:26 +01:00
func TestDB_Snapshot ( t * testing . T ) {
db , close := openTestDB ( t , nil )
defer close ( )
// append data
app := db . Appender ( )
mint := int64 ( 1414141414000 )
for i := 0 ; i < 1000 ; i ++ {
_ , err := app . Add ( labels . FromStrings ( "foo" , "bar" ) , mint + int64 ( i ) , 1.0 )
require . NoError ( t , err )
}
require . NoError ( t , app . Commit ( ) )
require . NoError ( t , app . Rollback ( ) )
// create snapshot
snap , err := ioutil . TempDir ( "" , "snap" )
require . NoError ( t , err )
require . NoError ( t , db . Snapshot ( snap ) )
require . NoError ( t , db . Close ( ) )
// reopen DB from snapshot
db , err = Open ( snap , nil , nil , nil )
require . NoError ( t , err )
2017-10-09 15:21:46 +02:00
querier , err := db . Querier ( mint , mint + 1000 )
require . NoError ( t , err )
2017-10-03 13:06:26 +01:00
defer querier . Close ( )
// sum values
seriesSet := querier . Select ( labels . NewEqualMatcher ( "foo" , "bar" ) )
sum := 0.0
for seriesSet . Next ( ) {
series := seriesSet . At ( ) . Iterator ( )
for series . Next ( ) {
_ , v := series . At ( )
sum += v
}
require . NoError ( t , series . Err ( ) )
}
require . NoError ( t , seriesSet . Err ( ) )
require . Equal ( t , sum , 1000.0 )
}
2017-08-29 00:39:17 +02:00
func TestDB_e2e ( t * testing . T ) {
const (
numDatapoints = 1000
numRanges = 1000
timeInterval = int64 ( 3 )
maxTime = int64 ( 2 * 1000 )
minTime = int64 ( 200 )
)
// Create 8 series with 1000 data-points of different ranges and run queries.
lbls := [ ] [ ] labels . Label {
{
{ "a" , "b" } ,
{ "instance" , "localhost:9090" } ,
{ "job" , "prometheus" } ,
} ,
{
{ "a" , "b" } ,
{ "instance" , "127.0.0.1:9090" } ,
{ "job" , "prometheus" } ,
} ,
{
{ "a" , "b" } ,
{ "instance" , "127.0.0.1:9090" } ,
{ "job" , "prom-k8s" } ,
} ,
{
{ "a" , "b" } ,
{ "instance" , "localhost:9090" } ,
{ "job" , "prom-k8s" } ,
} ,
{
{ "a" , "c" } ,
{ "instance" , "localhost:9090" } ,
{ "job" , "prometheus" } ,
} ,
{
{ "a" , "c" } ,
{ "instance" , "127.0.0.1:9090" } ,
{ "job" , "prometheus" } ,
} ,
{
{ "a" , "c" } ,
{ "instance" , "127.0.0.1:9090" } ,
{ "job" , "prom-k8s" } ,
} ,
{
{ "a" , "c" } ,
{ "instance" , "localhost:9090" } ,
{ "job" , "prom-k8s" } ,
} ,
}
seriesMap := map [ string ] [ ] sample { }
for _ , l := range lbls {
seriesMap [ labels . New ( l ... ) . String ( ) ] = [ ] sample { }
}
db , close := openTestDB ( t , nil )
defer close ( )
app := db . Appender ( )
for _ , l := range lbls {
lset := labels . New ( l ... )
series := [ ] sample { }
ts := rand . Int63n ( 300 )
for i := 0 ; i < numDatapoints ; i ++ {
v := rand . Float64 ( )
series = append ( series , sample { ts , v } )
_ , err := app . Add ( lset , ts , v )
require . NoError ( t , err )
ts += rand . Int63n ( timeInterval ) + 1
}
seriesMap [ lset . String ( ) ] = series
}
require . NoError ( t , app . Commit ( ) )
// Query each selector on 1000 random time-ranges.
queries := [ ] struct {
ms [ ] labels . Matcher
} {
{
ms : [ ] labels . Matcher { labels . NewEqualMatcher ( "a" , "b" ) } ,
} ,
{
ms : [ ] labels . Matcher {
labels . NewEqualMatcher ( "a" , "b" ) ,
labels . NewEqualMatcher ( "job" , "prom-k8s" ) ,
} ,
} ,
{
ms : [ ] labels . Matcher {
labels . NewEqualMatcher ( "a" , "c" ) ,
labels . NewEqualMatcher ( "instance" , "localhost:9090" ) ,
labels . NewEqualMatcher ( "job" , "prometheus" ) ,
} ,
} ,
// TODO: Add Regexp Matchers.
}
for _ , qry := range queries {
matched := labels . Slice { }
for _ , ls := range lbls {
s := labels . Selector ( qry . ms )
if s . Matches ( ls ) {
matched = append ( matched , ls )
}
}
sort . Sort ( matched )
for i := 0 ; i < numRanges ; i ++ {
mint := rand . Int63n ( 300 )
maxt := mint + rand . Int63n ( timeInterval * int64 ( numDatapoints ) )
expected := map [ string ] [ ] sample { }
// Build the mockSeriesSet.
for _ , m := range matched {
smpls := boundedSamples ( seriesMap [ m . String ( ) ] , mint , maxt )
if len ( smpls ) > 0 {
expected [ m . String ( ) ] = smpls
}
}
2017-10-09 15:21:46 +02:00
q , err := db . Querier ( mint , maxt )
require . NoError ( t , err )
2017-08-29 00:39:17 +02:00
ss := q . Select ( qry . ms ... )
result := map [ string ] [ ] sample { }
for ss . Next ( ) {
x := ss . At ( )
smpls , err := expandSeriesIterator ( x . Iterator ( ) )
require . NoError ( t , err )
if len ( smpls ) > 0 {
result [ x . Labels ( ) . String ( ) ] = smpls
}
}
require . NoError ( t , ss . Err ( ) )
require . Equal ( t , expected , result )
q . Close ( )
}
}
return
}
2017-11-10 21:19:39 +01:00
func TestWALFlushedOnDBClose ( t * testing . T ) {
tmpdir , _ := ioutil . TempDir ( "" , "test" )
defer os . RemoveAll ( tmpdir )
db , err := Open ( tmpdir , nil , nil , nil )
require . NoError ( t , err )
lbls := labels . Labels { labels . Label { Name : "labelname" , Value : "labelvalue" } }
app := db . Appender ( )
_ , err = app . Add ( lbls , 0 , 1 )
require . NoError ( t , err )
require . NoError ( t , app . Commit ( ) )
db . Close ( )
db , err = Open ( tmpdir , nil , nil , nil )
require . NoError ( t , err )
q , err := db . Querier ( 0 , 1 )
require . NoError ( t , err )
values , err := q . LabelValues ( "labelname" )
require . NoError ( t , err )
require . Equal ( t , values , [ ] string { "labelvalue" } )
}
2017-11-22 18:04:50 +05:30
func TestTombstoneClean ( t * testing . T ) {
numSamples := int64 ( 10 )
db , close := openTestDB ( t , nil )
defer close ( )
app := db . Appender ( )
smpls := make ( [ ] float64 , numSamples )
for i := int64 ( 0 ) ; i < numSamples ; i ++ {
smpls [ i ] = rand . Float64 ( )
app . Add ( labels . Labels { { "a" , "b" } } , i , smpls [ i ] )
}
require . NoError ( t , app . Commit ( ) )
cases := [ ] struct {
intervals Intervals
remaint [ ] int64
} {
{
intervals : Intervals { { 1 , 3 } , { 4 , 7 } } ,
remaint : [ ] int64 { 0 , 8 , 9 } ,
} ,
}
for _ , c := range cases {
// Delete the ranges.
// create snapshot
snap , err := ioutil . TempDir ( "" , "snap" )
require . NoError ( t , err )
require . NoError ( t , db . Snapshot ( snap ) )
require . NoError ( t , db . Close ( ) )
// reopen DB from snapshot
db , err = Open ( snap , nil , nil , nil )
require . NoError ( t , err )
for _ , r := range c . intervals {
require . NoError ( t , db . Delete ( r . Mint , r . Maxt , labels . NewEqualMatcher ( "a" , "b" ) ) )
}
// All of the setup for THIS line.
require . NoError ( t , db . CleanTombstones ( ) )
// Compare the result.
q , err := db . Querier ( 0 , numSamples )
require . NoError ( t , err )
res := q . Select ( labels . NewEqualMatcher ( "a" , "b" ) )
expSamples := make ( [ ] sample , 0 , len ( c . remaint ) )
for _ , ts := range c . remaint {
expSamples = append ( expSamples , sample { ts , smpls [ ts ] } )
}
expss := newListSeriesSet ( [ ] Series {
newSeries ( map [ string ] string { "a" : "b" } , expSamples ) ,
} )
if len ( expSamples ) == 0 {
require . False ( t , res . Next ( ) )
continue
}
for {
eok , rok := expss . Next ( ) , res . Next ( )
require . Equal ( t , eok , rok , "next" )
if ! eok {
break
}
sexp := expss . At ( )
sres := res . At ( )
require . Equal ( t , sexp . Labels ( ) , sres . Labels ( ) , "labels" )
smplExp , errExp := expandSeriesIterator ( sexp . Iterator ( ) )
smplRes , errRes := expandSeriesIterator ( sres . Iterator ( ) )
require . Equal ( t , errExp , errRes , "samples error" )
require . Equal ( t , smplExp , smplRes , "samples" )
}
for _ , b := range db . blocks {
Equals ( t , 0 , len ( b . tombstones ) )
}
}
}