Query oracle and API freeze
Label matchers (equal, not-equal, regexp, not-regexp) in labels/, postings intersection/union/difference in internal/postings/, LabelValues and AllPostings on index, block, and head readers. ingot.go wired to real DB backed by head + block readers. Querier snapshots overlapping blocks, Select resolves matchers to postings, merged iterator chains blocks then head with block-wins dedup. Oracle tests compare every query against a naive []sample reference across head-only, block-only, head+block merge seam, multiple blocks, all four matcher types, and time range filtering.
This commit is contained in:
@@ -159,6 +159,50 @@ func TestIndexChunkRefEncoding(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestIndexLabelValues(t *testing.T) {
|
||||
entries := []SeriesEntry{
|
||||
{Ref: 1, Labels: []labels.Label{{Name: "__name__", Value: "temp"}, {Name: "room", Value: "office"}}, Chunks: []ChunkMeta{{MinT: 0, MaxT: 1, Ref: 0}}},
|
||||
{Ref: 2, Labels: []labels.Label{{Name: "__name__", Value: "humidity"}, {Name: "room", Value: "office"}}, Chunks: []ChunkMeta{{MinT: 0, MaxT: 1, Ref: 0}}},
|
||||
{Ref: 3, Labels: []labels.Label{{Name: "__name__", Value: "temp"}, {Name: "room", Value: "kitchen"}}, Chunks: []ChunkMeta{{MinT: 0, MaxT: 1, Ref: 0}}},
|
||||
}
|
||||
|
||||
data := writeIndex(t, entries)
|
||||
r, err := NewReader(data)
|
||||
require.NoError(t, err)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
label string
|
||||
wantVals []string
|
||||
}{
|
||||
{name: "name_values", label: "__name__", wantVals: []string{"humidity", "temp"}},
|
||||
{name: "room_values", label: "room", wantVals: []string{"kitchen", "office"}},
|
||||
{name: "missing_label", label: "nonexistent", wantVals: []string{}},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
got := r.LabelValues(tc.label)
|
||||
assert.Equal(t, tc.wantVals, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIndexAllPostings(t *testing.T) {
|
||||
entries := []SeriesEntry{
|
||||
{Ref: 5, Labels: []labels.Label{{Name: "__name__", Value: "a"}}, Chunks: []ChunkMeta{{MinT: 0, MaxT: 1, Ref: 0}}},
|
||||
{Ref: 2, Labels: []labels.Label{{Name: "__name__", Value: "b"}}, Chunks: []ChunkMeta{{MinT: 0, MaxT: 1, Ref: 0}}},
|
||||
{Ref: 8, Labels: []labels.Label{{Name: "__name__", Value: "c"}}, Chunks: []ChunkMeta{{MinT: 0, MaxT: 1, Ref: 0}}},
|
||||
}
|
||||
|
||||
data := writeIndex(t, entries)
|
||||
r, err := NewReader(data)
|
||||
require.NoError(t, err)
|
||||
|
||||
refs := r.AllPostings()
|
||||
assert.Equal(t, []uint64{2, 5, 8}, refs)
|
||||
}
|
||||
|
||||
func TestIndexCorruptData(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
|
||||
@@ -3,6 +3,7 @@ package index
|
||||
import (
|
||||
"encoding/binary"
|
||||
"hash/crc32"
|
||||
"sort"
|
||||
|
||||
"git.dvdt.dev/david/ingot/labels"
|
||||
)
|
||||
@@ -213,3 +214,29 @@ func (r *Reader) SeriesByRef(ref uint64) (SeriesEntry, bool) {
|
||||
func (r *Reader) Postings(name, value string) []uint64 {
|
||||
return r.postings[labelPair{name, value}]
|
||||
}
|
||||
|
||||
// LabelValues returns sorted unique values for the given label name.
|
||||
func (r *Reader) LabelValues(name string) []string {
|
||||
seen := make(map[string]struct{})
|
||||
for key := range r.postings {
|
||||
if key.name == name {
|
||||
seen[key.value] = struct{}{}
|
||||
}
|
||||
}
|
||||
vals := make([]string, 0, len(seen))
|
||||
for v := range seen {
|
||||
vals = append(vals, v)
|
||||
}
|
||||
sort.Strings(vals)
|
||||
return vals
|
||||
}
|
||||
|
||||
// AllPostings returns sorted refs for all series in the index.
|
||||
func (r *Reader) AllPostings() []uint64 {
|
||||
refs := make([]uint64, len(r.series))
|
||||
for i, s := range r.series {
|
||||
refs[i] = s.Ref
|
||||
}
|
||||
sort.Slice(refs, func(i, j int) bool { return refs[i] < refs[j] })
|
||||
return refs
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user