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:
2026-07-04 15:19:09 -04:00
parent 42b03db2fa
commit 323a6f2951
11 changed files with 1585 additions and 0 deletions
+91
View File
@@ -0,0 +1,91 @@
// Package postings provides set operations on sorted uint64 slices,
// used for combining postings lists from index lookups.
package postings
// Intersect returns the sorted intersection of all input lists.
// An empty input returns nil.
func Intersect(lists ...[]uint64) []uint64 {
if len(lists) == 0 {
return nil
}
if len(lists) == 1 {
return lists[0]
}
result := lists[0]
for _, b := range lists[1:] {
result = intersectTwo(result, b)
}
return result
}
func intersectTwo(a, b []uint64) []uint64 {
var out []uint64
i, j := 0, 0
for i < len(a) && j < len(b) {
switch {
case a[i] < b[j]:
i++
case a[i] > b[j]:
j++
default:
out = append(out, a[i])
i++
j++
}
}
return out
}
// Union returns the sorted union of all input lists.
func Union(lists ...[]uint64) []uint64 {
if len(lists) == 0 {
return nil
}
if len(lists) == 1 {
return lists[0]
}
result := lists[0]
for _, b := range lists[1:] {
result = unionTwo(result, b)
}
return result
}
func unionTwo(a, b []uint64) []uint64 {
out := make([]uint64, 0, len(a)+len(b))
i, j := 0, 0
for i < len(a) && j < len(b) {
switch {
case a[i] < b[j]:
out = append(out, a[i])
i++
case a[i] > b[j]:
out = append(out, b[j])
j++
default:
out = append(out, a[i])
i++
j++
}
}
out = append(out, a[i:]...)
out = append(out, b[j:]...)
return out
}
// Without returns elements in full that are not in remove.
// Both inputs must be sorted.
func Without(full, remove []uint64) []uint64 {
var out []uint64
j := 0
for _, v := range full {
for j < len(remove) && remove[j] < v {
j++
}
if j < len(remove) && remove[j] == v {
continue
}
out = append(out, v)
}
return out
}
+79
View File
@@ -0,0 +1,79 @@
package postings
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestIntersect(t *testing.T) {
tests := []struct {
name string
lists [][]uint64
want []uint64
}{
{name: "empty_input", lists: nil, want: nil},
{name: "single_list", lists: [][]uint64{{1, 2, 3}}, want: []uint64{1, 2, 3}},
{name: "full_overlap", lists: [][]uint64{{1, 2, 3}, {1, 2, 3}}, want: []uint64{1, 2, 3}},
{name: "partial_overlap", lists: [][]uint64{{1, 2, 3, 4}, {2, 3, 5}}, want: []uint64{2, 3}},
{name: "no_overlap", lists: [][]uint64{{1, 2}, {3, 4}}, want: nil},
{name: "one_empty", lists: [][]uint64{{1, 2, 3}, {}}, want: nil},
{name: "three_lists", lists: [][]uint64{{1, 2, 3, 4, 5}, {2, 3, 4}, {3, 4, 5}}, want: []uint64{3, 4}},
{name: "single_element", lists: [][]uint64{{5}, {5}}, want: []uint64{5}},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
got := Intersect(tc.lists...)
assert.Equal(t, tc.want, got)
})
}
}
func TestUnion(t *testing.T) {
tests := []struct {
name string
lists [][]uint64
want []uint64
}{
{name: "empty_input", lists: nil, want: nil},
{name: "single_list", lists: [][]uint64{{1, 2, 3}}, want: []uint64{1, 2, 3}},
{name: "full_overlap", lists: [][]uint64{{1, 2, 3}, {1, 2, 3}}, want: []uint64{1, 2, 3}},
{name: "no_overlap", lists: [][]uint64{{1, 2}, {3, 4}}, want: []uint64{1, 2, 3, 4}},
{name: "partial_overlap", lists: [][]uint64{{1, 3, 5}, {2, 3, 4}}, want: []uint64{1, 2, 3, 4, 5}},
{name: "one_empty", lists: [][]uint64{{1, 2}, {}}, want: []uint64{1, 2}},
{name: "both_empty", lists: [][]uint64{{}, {}}, want: []uint64{}},
{name: "three_lists", lists: [][]uint64{{1}, {2}, {3}}, want: []uint64{1, 2, 3}},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
got := Union(tc.lists...)
assert.Equal(t, tc.want, got)
})
}
}
func TestWithout(t *testing.T) {
tests := []struct {
name string
full []uint64
remove []uint64
want []uint64
}{
{name: "empty_full", full: nil, remove: []uint64{1, 2}, want: nil},
{name: "empty_remove", full: []uint64{1, 2, 3}, remove: nil, want: []uint64{1, 2, 3}},
{name: "both_empty", full: nil, remove: nil, want: nil},
{name: "remove_subset", full: []uint64{1, 2, 3, 4, 5}, remove: []uint64{2, 4}, want: []uint64{1, 3, 5}},
{name: "remove_all", full: []uint64{1, 2, 3}, remove: []uint64{1, 2, 3}, want: nil},
{name: "remove_none", full: []uint64{1, 2, 3}, remove: []uint64{4, 5}, want: []uint64{1, 2, 3}},
{name: "remove_superset", full: []uint64{2, 3}, remove: []uint64{1, 2, 3, 4}, want: nil},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
got := Without(tc.full, tc.remove)
assert.Equal(t, tc.want, got)
})
}
}