diff --git a/tsdb/querier.go b/tsdb/querier.go index b4513218e..74c73f460 100644 --- a/tsdb/querier.go +++ b/tsdb/querier.go @@ -958,6 +958,7 @@ type mergedStringIter struct { b index.StringIter aok, bok bool cur string + err error } func (m *mergedStringIter) Next() bool { @@ -968,29 +969,34 @@ func (m *mergedStringIter) Next() bool { if !m.aok { m.cur = m.b.At() m.bok = m.b.Next() + m.err = m.b.Err() } else if !m.bok { m.cur = m.a.At() m.aok = m.a.Next() + m.err = m.a.Err() } else if m.b.At() > m.a.At() { m.cur = m.a.At() m.aok = m.a.Next() + m.err = m.a.Err() } else if m.a.At() > m.b.At() { m.cur = m.b.At() m.bok = m.b.Next() + m.err = m.b.Err() } else { // Equal. m.cur = m.b.At() m.aok = m.a.Next() + m.err = m.a.Err() m.bok = m.b.Next() + if m.err == nil { + m.err = m.b.Err() + } } return true } func (m mergedStringIter) At() string { return m.cur } func (m mergedStringIter) Err() error { - if m.a.Err() != nil { - return m.a.Err() - } - return m.b.Err() + return m.err } // DeletedIterator wraps chunk Iterator and makes sure any deleted metrics are not returned. diff --git a/tsdb/querier_bench_test.go b/tsdb/querier_bench_test.go index 1feb48f6f..78993d105 100644 --- a/tsdb/querier_bench_test.go +++ b/tsdb/querier_bench_test.go @@ -19,9 +19,10 @@ import ( "strconv" "testing" - "github.com/stretchr/testify/require" - "github.com/prometheus/prometheus/model/labels" + "github.com/prometheus/prometheus/tsdb/index" + + "github.com/stretchr/testify/require" ) // Make entries ~50B in size, to emulate real-world high cardinality. @@ -202,6 +203,28 @@ func benchmarkLabelValuesWithMatchers(b *testing.B, ir IndexReader) { } } +func BenchmarkMergedStringIter(b *testing.B) { + numSymbols := 100000 + s := make([]string, numSymbols) + for i := 0; i < numSymbols; i++ { + s[i] = fmt.Sprintf("symbol%v", i) + } + + for i := 0; i < b.N; i++ { + it := NewMergedStringIter(index.NewStringListIter(s), index.NewStringListIter(s)) + for j := 0; j < 100; j++ { + it = NewMergedStringIter(it, index.NewStringListIter(s)) + } + + for it.Next() { + require.NotNil(b, it.At()) + require.NoError(b, it.Err()) + } + } + + b.ReportAllocs() +} + func BenchmarkQuerierSelect(b *testing.B) { chunkDir := b.TempDir() opts := DefaultHeadOptions()