storage: Update LabelQuerier interface to return sorted label values (#14849)

* Change LabelQuerier.LabelValues() to return sorted values

---------

Signed-off-by: 🌲 Harry 🌊 John 🏔 <johrry@amazon.com>
This commit is contained in:
Harry John 2024-09-16 23:55:02 -07:00 committed by GitHub
parent 2c87817e40
commit 919dc0cbc6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 60 additions and 1 deletions

View file

@ -157,7 +157,7 @@ type ChunkQuerier interface {
// LabelQuerier provides querying access over labels. // LabelQuerier provides querying access over labels.
type LabelQuerier interface { type LabelQuerier interface {
// LabelValues returns all potential values for a label name. // LabelValues returns all potential values for a label name in sorted order.
// It is not safe to use the strings beyond the lifetime of the querier. // It is not safe to use the strings beyond the lifetime of the querier.
// If matchers are specified the returned result set is reduced // If matchers are specified the returned result set is reduced
// to label values of metrics matching the matchers. // to label values of metrics matching the matchers.

View file

@ -22,6 +22,7 @@ import (
"math/rand" "math/rand"
"os" "os"
"path/filepath" "path/filepath"
"slices"
"sort" "sort"
"strconv" "strconv"
"testing" "testing"
@ -310,6 +311,33 @@ func TestLabelValuesWithMatchers(t *testing.T) {
} }
} }
func TestBlockQuerierReturnsSortedLabelValues(t *testing.T) {
tmpdir := t.TempDir()
ctx := context.Background()
var seriesEntries []storage.Series
for i := 100; i > 0; i-- {
seriesEntries = append(seriesEntries, storage.NewListSeries(labels.FromStrings(
"__name__", fmt.Sprintf("value%d", i),
), []chunks.Sample{sample{100, 0, nil, nil}}))
}
blockDir := createBlock(t, tmpdir, seriesEntries)
// Check open err.
block, err := OpenBlock(nil, blockDir, nil)
require.NoError(t, err)
t.Cleanup(func() { require.NoError(t, block.Close()) })
q, err := newBlockBaseQuerier(block, 0, 100)
require.NoError(t, err)
t.Cleanup(func() { require.NoError(t, q.Close()) })
res, _, err := q.LabelValues(ctx, "__name__", nil)
require.NoError(t, err)
require.True(t, slices.IsSorted(res))
}
// TestBlockSize ensures that the block size is calculated correctly. // TestBlockSize ensures that the block size is calculated correctly.
func TestBlockSize(t *testing.T) { func TestBlockSize(t *testing.T) {
tmpdir := t.TempDir() tmpdir := t.TempDir()

View file

@ -23,6 +23,7 @@ import (
"path" "path"
"path/filepath" "path/filepath"
"reflect" "reflect"
"slices"
"sort" "sort"
"strconv" "strconv"
"strings" "strings"
@ -2101,6 +2102,36 @@ func TestHead_LogRollback(t *testing.T) {
} }
} }
func TestHead_ReturnsSortedLabelValues(t *testing.T) {
h, _ := newTestHead(t, 1000, wlog.CompressionNone, false)
defer func() {
require.NoError(t, h.Close())
}()
h.initTime(0)
app := h.appender()
for i := 100; i > 0; i-- {
for j := 0; j < 10; j++ {
lset := labels.FromStrings(
"__name__", fmt.Sprintf("metric_%d", i),
"label", fmt.Sprintf("value_%d", j),
)
_, err := app.Append(0, lset, 2100, 1)
require.NoError(t, err)
}
}
q, err := NewBlockQuerier(h, 1500, 2500)
require.NoError(t, err)
res, _, err := q.LabelValues(context.Background(), "__name__", nil)
require.NoError(t, err)
require.True(t, slices.IsSorted(res))
require.NoError(t, q.Close())
}
// TestWalRepair_DecodingError ensures that a repair is run for an error // TestWalRepair_DecodingError ensures that a repair is run for an error
// when decoding a record. // when decoding a record.
func TestWalRepair_DecodingError(t *testing.T) { func TestWalRepair_DecodingError(t *testing.T) {