diff --git a/chunkenc/chunk.go b/chunkenc/chunk.go index bc6c2010..71325843 100644 --- a/chunkenc/chunk.go +++ b/chunkenc/chunk.go @@ -58,9 +58,10 @@ type Appender interface { // Iterator iterates over the data of a time series. type Iterator interface { - // Seek advances the iterator forward to the given timestamp. - // If there's no value exactly at t, it advances to the first value - // after t. + // Seek advances the iterator forward to the sample with the timestamp t or first value after t. + // If the current iterator points to the sample with timestamp after t already, + // Seek should not advance the iterator. + // Seek returns false if there is no such sample with the timestamp equal or larger than t. Seek(t int64) bool // At returns the current timestamp/value pair. At() (int64, float64) diff --git a/chunkenc/chunk_test.go b/chunkenc/chunk_test.go index f6577b42..dd7b6615 100644 --- a/chunkenc/chunk_test.go +++ b/chunkenc/chunk_test.go @@ -17,7 +17,6 @@ import ( "fmt" "io" "math/rand" - "reflect" "testing" "github.com/prometheus/tsdb/testutil" @@ -35,28 +34,23 @@ func TestChunk(t *testing.T) { t.Run(fmt.Sprintf("%v", enc), func(t *testing.T) { for range make([]struct{}, 1) { c := nc() - if err := testChunk(c); err != nil { - t.Fatal(err) - } + testChunk(t, c) } }) } } -func testChunk(c Chunk) error { +func testChunk(t *testing.T, c Chunk) { app, err := c.Appender() - if err != nil { - return err - } + testutil.Ok(t, err) - var exp []pair + var all []pair var ( ts = int64(1234123324) v = 1243535.123 ) for i := 0; i < 300; i++ { ts += int64(rand.Intn(10000) + 1) - // v = rand.Float64() if i%2 == 0 { v += float64(rand.Intn(1000000)) } else { @@ -67,29 +61,51 @@ func testChunk(c Chunk) error { // appending to a partially filled chunk. if i%10 == 0 { app, err = c.Appender() - if err != nil { - return err - } + testutil.Ok(t, err) } app.Append(ts, v) - exp = append(exp, pair{t: ts, v: v}) - // fmt.Println("appended", len(c.Bytes()), c.Bytes()) + all = append(all, pair{t: ts, v: v}) } - it := c.Iterator(nil) - var res []pair - for it.Next() { - ts, v := it.At() - res = append(res, pair{t: ts, v: v}) + // 1. Expand iterator in simple case. + it1 := c.Iterator(nil) + var res1 []pair + for it1.Next() { + ts, v := it1.At() + res1 = append(res1, pair{t: ts, v: v}) } - if it.Err() != nil { - return it.Err() + testutil.Ok(t, it1.Err()) + testutil.Equals(t, all, res1) + + // 2. Expand second iterator while reusing first one. + it2 := c.Iterator(it1) + var res2 []pair + for it2.Next() { + ts, v := it2.At() + res2 = append(res2, pair{t: ts, v: v}) } - if !reflect.DeepEqual(exp, res) { - return fmt.Errorf("unexpected result\n\ngot: %v\n\nexp: %v", res, exp) + testutil.Ok(t, it2.Err()) + testutil.Equals(t, all, res2) + + // 3. Test Iterator Seek. + mid := len(all) / 2 + + it3 := c.Iterator(nil) + var res3 []pair + testutil.Equals(t, true, it3.Seek(all[mid].t)) + // Below ones should not matter. + testutil.Equals(t, true, it3.Seek(all[mid].t)) + testutil.Equals(t, true, it3.Seek(all[mid].t)) + ts, v = it3.At() + res3 = append(res3, pair{t: ts, v: v}) + + for it3.Next() { + ts, v := it3.At() + res3 = append(res3, pair{t: ts, v: v}) } - return nil + testutil.Ok(t, it3.Err()) + testutil.Equals(t, all[mid:], res3) } func benchmarkIterator(b *testing.B, newChunk func() Chunk) { diff --git a/chunkenc/xor.go b/chunkenc/xor.go index 7d31c4c5..86c05f0f 100644 --- a/chunkenc/xor.go +++ b/chunkenc/xor.go @@ -242,16 +242,16 @@ type xorIterator struct { } func (it *xorIterator) Seek(t int64) bool { - if t >= it.t { + if it.err != nil { return false } - for it.Next() { - if t >= it.t { - return true + for t > it.t || it.numRead == 0 { + if !it.Next() { + return false } } - return false + return true } func (it *xorIterator) At() (int64, float64) { diff --git a/querier_test.go b/querier_test.go index cc48c95d..5ef27b75 100644 --- a/querier_test.go +++ b/querier_test.go @@ -924,7 +924,7 @@ func TestSeriesIterator(t *testing.T) { if tc.success { // Init the list and then proceed to check. remaining := exp.Next() - testutil.Assert(t, remaining == true, "") + testutil.Assert(t, remaining, "") for remaining { sExp, eExp := exp.At() @@ -1034,7 +1034,7 @@ func TestSeriesIterator(t *testing.T) { if tc.success { // Init the list and then proceed to check. remaining := exp.Next() - testutil.Assert(t, remaining == true, "") + testutil.Assert(t, remaining, "") for remaining { sExp, eExp := exp.At() @@ -1101,8 +1101,8 @@ func TestChunkSeriesIterator_DoubleSeek(t *testing.T) { } res := newChunkSeriesIterator(chkMetas, nil, 2, 8) - testutil.Assert(t, res.Seek(1) == true, "") - testutil.Assert(t, res.Seek(2) == true, "") + testutil.Assert(t, res.Seek(1), "") + testutil.Assert(t, res.Seek(2), "") ts, v := res.At() testutil.Equals(t, int64(2), ts) testutil.Equals(t, float64(2), v) @@ -1119,12 +1119,12 @@ func TestChunkSeriesIterator_SeekInCurrentChunk(t *testing.T) { it := newChunkSeriesIterator(metas, nil, 1, 7) - testutil.Assert(t, it.Next() == true, "") + testutil.Assert(t, it.Next(), "") ts, v := it.At() testutil.Equals(t, int64(1), ts) testutil.Equals(t, float64(2), v) - testutil.Assert(t, it.Seek(4) == true, "") + testutil.Assert(t, it.Seek(4), "") ts, v = it.At() testutil.Equals(t, int64(5), ts) testutil.Equals(t, float64(6), v)