prometheus
1645 строк · 65.0 Кб
1// Copyright 2020 The Prometheus Authors
2// Licensed under the Apache License, Version 2.0 (the "License");
3// you may not use this file except in compliance with the License.
4// You may obtain a copy of the License at
5//
6// http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
14package storage
15
16import (
17"context"
18"errors"
19"fmt"
20"math"
21"sort"
22"sync"
23"testing"
24
25"github.com/stretchr/testify/require"
26
27"github.com/prometheus/prometheus/model/histogram"
28"github.com/prometheus/prometheus/model/labels"
29"github.com/prometheus/prometheus/tsdb/chunkenc"
30"github.com/prometheus/prometheus/tsdb/chunks"
31"github.com/prometheus/prometheus/tsdb/tsdbutil"
32"github.com/prometheus/prometheus/util/annotations"
33)
34
35func TestMergeQuerierWithChainMerger(t *testing.T) {
36for _, tc := range []struct {
37name string
38primaryQuerierSeries []Series
39querierSeries [][]Series
40extraQueriers []Querier
41
42expected SeriesSet
43}{
44{
45name: "one primary querier with no series",
46primaryQuerierSeries: []Series{},
47expected: NewMockSeriesSet(),
48},
49{
50name: "one secondary querier with no series",
51querierSeries: [][]Series{{}},
52expected: NewMockSeriesSet(),
53},
54{
55name: "many secondary queriers with no series",
56querierSeries: [][]Series{{}, {}, {}, {}, {}, {}, {}},
57expected: NewMockSeriesSet(),
58},
59{
60name: "mix of queriers with no series",
61primaryQuerierSeries: []Series{},
62querierSeries: [][]Series{{}, {}, {}, {}, {}, {}, {}},
63expected: NewMockSeriesSet(),
64},
65// Test rest of cases on secondary queriers as the different between primary vs secondary is just error handling.
66{
67name: "one querier, two series",
68querierSeries: [][]Series{{
69NewListSeries(labels.FromStrings("bar", "baz"), []chunks.Sample{fSample{1, 1}, fSample{2, 2}, fSample{3, 3}}),
70NewListSeries(labels.FromStrings("foo", "bar"), []chunks.Sample{fSample{0, 0}, fSample{1, 1}, fSample{2, 2}}),
71}},
72expected: NewMockSeriesSet(
73NewListSeries(labels.FromStrings("bar", "baz"), []chunks.Sample{fSample{1, 1}, fSample{2, 2}, fSample{3, 3}}),
74NewListSeries(labels.FromStrings("foo", "bar"), []chunks.Sample{fSample{0, 0}, fSample{1, 1}, fSample{2, 2}}),
75),
76},
77{
78name: "two queriers, one different series each",
79querierSeries: [][]Series{{
80NewListSeries(labels.FromStrings("bar", "baz"), []chunks.Sample{fSample{1, 1}, fSample{2, 2}, fSample{3, 3}}),
81}, {
82NewListSeries(labels.FromStrings("foo", "bar"), []chunks.Sample{fSample{0, 0}, fSample{1, 1}, fSample{2, 2}}),
83}},
84expected: NewMockSeriesSet(
85NewListSeries(labels.FromStrings("bar", "baz"), []chunks.Sample{fSample{1, 1}, fSample{2, 2}, fSample{3, 3}}),
86NewListSeries(labels.FromStrings("foo", "bar"), []chunks.Sample{fSample{0, 0}, fSample{1, 1}, fSample{2, 2}}),
87),
88},
89{
90name: "two time unsorted queriers, two series each",
91querierSeries: [][]Series{{
92NewListSeries(labels.FromStrings("bar", "baz"), []chunks.Sample{fSample{5, 5}, fSample{6, 6}}),
93NewListSeries(labels.FromStrings("foo", "bar"), []chunks.Sample{fSample{0, 0}, fSample{1, 1}, fSample{2, 2}}),
94}, {
95NewListSeries(labels.FromStrings("bar", "baz"), []chunks.Sample{fSample{1, 1}, fSample{2, 2}, fSample{3, 3}}),
96NewListSeries(labels.FromStrings("foo", "bar"), []chunks.Sample{fSample{3, 3}, fSample{4, 4}}),
97}},
98expected: NewMockSeriesSet(
99NewListSeries(
100labels.FromStrings("bar", "baz"),
101[]chunks.Sample{fSample{1, 1}, fSample{2, 2}, fSample{3, 3}, fSample{5, 5}, fSample{6, 6}},
102),
103NewListSeries(
104labels.FromStrings("foo", "bar"),
105[]chunks.Sample{fSample{0, 0}, fSample{1, 1}, fSample{2, 2}, fSample{3, 3}, fSample{4, 4}},
106),
107),
108},
109{
110name: "five queriers, only two queriers have two time unsorted series each",
111querierSeries: [][]Series{{}, {}, {
112NewListSeries(labels.FromStrings("bar", "baz"), []chunks.Sample{fSample{5, 5}, fSample{6, 6}}),
113NewListSeries(labels.FromStrings("foo", "bar"), []chunks.Sample{fSample{0, 0}, fSample{1, 1}, fSample{2, 2}}),
114}, {
115NewListSeries(labels.FromStrings("bar", "baz"), []chunks.Sample{fSample{1, 1}, fSample{2, 2}, fSample{3, 3}}),
116NewListSeries(labels.FromStrings("foo", "bar"), []chunks.Sample{fSample{3, 3}, fSample{4, 4}}),
117}, {}},
118expected: NewMockSeriesSet(
119NewListSeries(
120labels.FromStrings("bar", "baz"),
121[]chunks.Sample{fSample{1, 1}, fSample{2, 2}, fSample{3, 3}, fSample{5, 5}, fSample{6, 6}},
122),
123NewListSeries(
124labels.FromStrings("foo", "bar"),
125[]chunks.Sample{fSample{0, 0}, fSample{1, 1}, fSample{2, 2}, fSample{3, 3}, fSample{4, 4}},
126),
127),
128},
129{
130name: "two queriers, only two queriers have two time unsorted series each, with 3 noop and one nil querier together",
131querierSeries: [][]Series{{}, {}, {
132NewListSeries(labels.FromStrings("bar", "baz"), []chunks.Sample{fSample{5, 5}, fSample{6, 6}}),
133NewListSeries(labels.FromStrings("foo", "bar"), []chunks.Sample{fSample{0, 0}, fSample{1, 1}, fSample{2, 2}}),
134}, {
135NewListSeries(labels.FromStrings("bar", "baz"), []chunks.Sample{fSample{1, 1}, fSample{2, 2}, fSample{3, 3}}),
136NewListSeries(labels.FromStrings("foo", "bar"), []chunks.Sample{fSample{3, 3}, fSample{4, 4}}),
137}, {}},
138extraQueriers: []Querier{NoopQuerier(), NoopQuerier(), nil, NoopQuerier()},
139expected: NewMockSeriesSet(
140NewListSeries(
141labels.FromStrings("bar", "baz"),
142[]chunks.Sample{fSample{1, 1}, fSample{2, 2}, fSample{3, 3}, fSample{5, 5}, fSample{6, 6}},
143),
144NewListSeries(
145labels.FromStrings("foo", "bar"),
146[]chunks.Sample{fSample{0, 0}, fSample{1, 1}, fSample{2, 2}, fSample{3, 3}, fSample{4, 4}},
147),
148),
149},
150{
151name: "two queriers, with two series, one is overlapping",
152querierSeries: [][]Series{{}, {}, {
153NewListSeries(labels.FromStrings("bar", "baz"), []chunks.Sample{fSample{2, 21}, fSample{3, 31}, fSample{5, 5}, fSample{6, 6}}),
154NewListSeries(labels.FromStrings("foo", "bar"), []chunks.Sample{fSample{0, 0}, fSample{1, 1}, fSample{2, 2}}),
155}, {
156NewListSeries(labels.FromStrings("bar", "baz"), []chunks.Sample{fSample{1, 1}, fSample{2, 22}, fSample{3, 32}}),
157NewListSeries(labels.FromStrings("foo", "bar"), []chunks.Sample{fSample{3, 3}, fSample{4, 4}}),
158}, {}},
159expected: NewMockSeriesSet(
160NewListSeries(
161labels.FromStrings("bar", "baz"),
162[]chunks.Sample{fSample{1, 1}, fSample{2, 21}, fSample{3, 31}, fSample{5, 5}, fSample{6, 6}},
163),
164NewListSeries(
165labels.FromStrings("foo", "bar"),
166[]chunks.Sample{fSample{0, 0}, fSample{1, 1}, fSample{2, 2}, fSample{3, 3}, fSample{4, 4}},
167),
168),
169},
170{
171name: "two queries, one with NaN samples series",
172querierSeries: [][]Series{{
173NewListSeries(labels.FromStrings("foo", "bar"), []chunks.Sample{fSample{0, math.NaN()}}),
174}, {
175NewListSeries(labels.FromStrings("foo", "bar"), []chunks.Sample{fSample{1, 1}}),
176}},
177expected: NewMockSeriesSet(
178NewListSeries(labels.FromStrings("foo", "bar"), []chunks.Sample{fSample{0, math.NaN()}, fSample{1, 1}}),
179),
180},
181} {
182t.Run(tc.name, func(t *testing.T) {
183var p Querier
184if tc.primaryQuerierSeries != nil {
185p = &mockQuerier{toReturn: tc.primaryQuerierSeries}
186}
187var qs []Querier
188for _, in := range tc.querierSeries {
189qs = append(qs, &mockQuerier{toReturn: in})
190}
191qs = append(qs, tc.extraQueriers...)
192
193mergedQuerier := NewMergeQuerier([]Querier{p}, qs, ChainedSeriesMerge).Select(context.Background(), false, nil)
194
195// Get all merged series upfront to make sure there are no incorrectly retained shared
196// buffers causing bugs.
197var mergedSeries []Series
198for mergedQuerier.Next() {
199mergedSeries = append(mergedSeries, mergedQuerier.At())
200}
201require.NoError(t, mergedQuerier.Err())
202
203for _, actualSeries := range mergedSeries {
204require.True(t, tc.expected.Next(), "Expected Next() to be true")
205expectedSeries := tc.expected.At()
206require.Equal(t, expectedSeries.Labels(), actualSeries.Labels())
207
208expSmpl, expErr := ExpandSamples(expectedSeries.Iterator(nil), nil)
209actSmpl, actErr := ExpandSamples(actualSeries.Iterator(nil), nil)
210require.Equal(t, expErr, actErr)
211require.Equal(t, expSmpl, actSmpl)
212}
213require.False(t, tc.expected.Next(), "Expected Next() to be false")
214})
215}
216}
217
218func TestMergeChunkQuerierWithNoVerticalChunkSeriesMerger(t *testing.T) {
219for _, tc := range []struct {
220name string
221primaryChkQuerierSeries []ChunkSeries
222chkQuerierSeries [][]ChunkSeries
223extraQueriers []ChunkQuerier
224
225expected ChunkSeriesSet
226}{
227{
228name: "one primary querier with no series",
229primaryChkQuerierSeries: []ChunkSeries{},
230expected: NewMockChunkSeriesSet(),
231},
232{
233name: "one secondary querier with no series",
234chkQuerierSeries: [][]ChunkSeries{{}},
235expected: NewMockChunkSeriesSet(),
236},
237{
238name: "many secondary queriers with no series",
239chkQuerierSeries: [][]ChunkSeries{{}, {}, {}, {}, {}, {}, {}},
240expected: NewMockChunkSeriesSet(),
241},
242{
243name: "mix of queriers with no series",
244primaryChkQuerierSeries: []ChunkSeries{},
245chkQuerierSeries: [][]ChunkSeries{{}, {}, {}, {}, {}, {}, {}},
246expected: NewMockChunkSeriesSet(),
247},
248// Test rest of cases on secondary queriers as the different between primary vs secondary is just error handling.
249{
250name: "one querier, two series",
251chkQuerierSeries: [][]ChunkSeries{{
252NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"), []chunks.Sample{fSample{1, 1}, fSample{2, 2}}, []chunks.Sample{fSample{3, 3}}),
253NewListChunkSeriesFromSamples(labels.FromStrings("foo", "bar"), []chunks.Sample{fSample{0, 0}, fSample{1, 1}}, []chunks.Sample{fSample{2, 2}}),
254}},
255expected: NewMockChunkSeriesSet(
256NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"), []chunks.Sample{fSample{1, 1}, fSample{2, 2}}, []chunks.Sample{fSample{3, 3}}),
257NewListChunkSeriesFromSamples(labels.FromStrings("foo", "bar"), []chunks.Sample{fSample{0, 0}, fSample{1, 1}}, []chunks.Sample{fSample{2, 2}}),
258),
259},
260{
261name: "two secondaries, one different series each",
262chkQuerierSeries: [][]ChunkSeries{{
263NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"), []chunks.Sample{fSample{1, 1}, fSample{2, 2}}, []chunks.Sample{fSample{3, 3}}),
264}, {
265NewListChunkSeriesFromSamples(labels.FromStrings("foo", "bar"), []chunks.Sample{fSample{0, 0}, fSample{1, 1}}, []chunks.Sample{fSample{2, 2}}),
266}},
267expected: NewMockChunkSeriesSet(
268NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"), []chunks.Sample{fSample{1, 1}, fSample{2, 2}}, []chunks.Sample{fSample{3, 3}}),
269NewListChunkSeriesFromSamples(labels.FromStrings("foo", "bar"), []chunks.Sample{fSample{0, 0}, fSample{1, 1}}, []chunks.Sample{fSample{2, 2}}),
270),
271},
272{
273name: "two secondaries, two not in time order series each",
274chkQuerierSeries: [][]ChunkSeries{{
275NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"), []chunks.Sample{fSample{5, 5}}, []chunks.Sample{fSample{6, 6}}),
276NewListChunkSeriesFromSamples(labels.FromStrings("foo", "bar"), []chunks.Sample{fSample{0, 0}, fSample{1, 1}}, []chunks.Sample{fSample{2, 2}}),
277}, {
278NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"), []chunks.Sample{fSample{1, 1}, fSample{2, 2}}, []chunks.Sample{fSample{3, 3}}),
279NewListChunkSeriesFromSamples(labels.FromStrings("foo", "bar"), []chunks.Sample{fSample{3, 3}}, []chunks.Sample{fSample{4, 4}}),
280}},
281expected: NewMockChunkSeriesSet(
282NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"),
283[]chunks.Sample{fSample{1, 1}, fSample{2, 2}},
284[]chunks.Sample{fSample{3, 3}},
285[]chunks.Sample{fSample{5, 5}},
286[]chunks.Sample{fSample{6, 6}},
287),
288NewListChunkSeriesFromSamples(labels.FromStrings("foo", "bar"),
289[]chunks.Sample{fSample{0, 0}, fSample{1, 1}},
290[]chunks.Sample{fSample{2, 2}},
291[]chunks.Sample{fSample{3, 3}},
292[]chunks.Sample{fSample{4, 4}},
293),
294),
295},
296{
297name: "five secondaries, only two have two not in time order series each",
298chkQuerierSeries: [][]ChunkSeries{{}, {}, {
299NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"), []chunks.Sample{fSample{5, 5}}, []chunks.Sample{fSample{6, 6}}),
300NewListChunkSeriesFromSamples(labels.FromStrings("foo", "bar"), []chunks.Sample{fSample{0, 0}, fSample{1, 1}}, []chunks.Sample{fSample{2, 2}}),
301}, {
302NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"), []chunks.Sample{fSample{1, 1}, fSample{2, 2}}, []chunks.Sample{fSample{3, 3}}),
303NewListChunkSeriesFromSamples(labels.FromStrings("foo", "bar"), []chunks.Sample{fSample{3, 3}}, []chunks.Sample{fSample{4, 4}}),
304}, {}},
305expected: NewMockChunkSeriesSet(
306NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"),
307[]chunks.Sample{fSample{1, 1}, fSample{2, 2}},
308[]chunks.Sample{fSample{3, 3}},
309[]chunks.Sample{fSample{5, 5}},
310[]chunks.Sample{fSample{6, 6}},
311),
312NewListChunkSeriesFromSamples(labels.FromStrings("foo", "bar"),
313[]chunks.Sample{fSample{0, 0}, fSample{1, 1}},
314[]chunks.Sample{fSample{2, 2}},
315[]chunks.Sample{fSample{3, 3}},
316[]chunks.Sample{fSample{4, 4}},
317),
318),
319},
320{
321name: "two secondaries, with two not in time order series each, with 3 noop queries and one nil together",
322chkQuerierSeries: [][]ChunkSeries{{
323NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"), []chunks.Sample{fSample{5, 5}}, []chunks.Sample{fSample{6, 6}}),
324NewListChunkSeriesFromSamples(labels.FromStrings("foo", "bar"), []chunks.Sample{fSample{0, 0}, fSample{1, 1}}, []chunks.Sample{fSample{2, 2}}),
325}, {
326NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"), []chunks.Sample{fSample{1, 1}, fSample{2, 2}}, []chunks.Sample{fSample{3, 3}}),
327NewListChunkSeriesFromSamples(labels.FromStrings("foo", "bar"), []chunks.Sample{fSample{3, 3}}, []chunks.Sample{fSample{4, 4}}),
328}},
329extraQueriers: []ChunkQuerier{NoopChunkedQuerier(), NoopChunkedQuerier(), nil, NoopChunkedQuerier()},
330expected: NewMockChunkSeriesSet(
331NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"),
332[]chunks.Sample{fSample{1, 1}, fSample{2, 2}},
333[]chunks.Sample{fSample{3, 3}},
334[]chunks.Sample{fSample{5, 5}},
335[]chunks.Sample{fSample{6, 6}},
336),
337NewListChunkSeriesFromSamples(labels.FromStrings("foo", "bar"),
338[]chunks.Sample{fSample{0, 0}, fSample{1, 1}},
339[]chunks.Sample{fSample{2, 2}},
340[]chunks.Sample{fSample{3, 3}},
341[]chunks.Sample{fSample{4, 4}},
342),
343),
344},
345{
346name: "two queries, one with NaN samples series",
347chkQuerierSeries: [][]ChunkSeries{{
348NewListChunkSeriesFromSamples(labels.FromStrings("foo", "bar"), []chunks.Sample{fSample{0, math.NaN()}}),
349}, {
350NewListChunkSeriesFromSamples(labels.FromStrings("foo", "bar"), []chunks.Sample{fSample{1, 1}}),
351}},
352expected: NewMockChunkSeriesSet(
353NewListChunkSeriesFromSamples(labels.FromStrings("foo", "bar"), []chunks.Sample{fSample{0, math.NaN()}}, []chunks.Sample{fSample{1, 1}}),
354),
355},
356} {
357t.Run(tc.name, func(t *testing.T) {
358var p ChunkQuerier
359if tc.primaryChkQuerierSeries != nil {
360p = &mockChunkQuerier{toReturn: tc.primaryChkQuerierSeries}
361}
362
363var qs []ChunkQuerier
364for _, in := range tc.chkQuerierSeries {
365qs = append(qs, &mockChunkQuerier{toReturn: in})
366}
367qs = append(qs, tc.extraQueriers...)
368
369merged := NewMergeChunkQuerier([]ChunkQuerier{p}, qs, NewCompactingChunkSeriesMerger(nil)).Select(context.Background(), false, nil)
370for merged.Next() {
371require.True(t, tc.expected.Next(), "Expected Next() to be true")
372actualSeries := merged.At()
373expectedSeries := tc.expected.At()
374require.Equal(t, expectedSeries.Labels(), actualSeries.Labels())
375
376expChks, expErr := ExpandChunks(expectedSeries.Iterator(nil))
377actChks, actErr := ExpandChunks(actualSeries.Iterator(nil))
378require.Equal(t, expErr, actErr)
379require.Equal(t, expChks, actChks)
380}
381require.NoError(t, merged.Err())
382require.False(t, tc.expected.Next(), "Expected Next() to be false")
383})
384}
385}
386
387func histogramSample(ts int64, hint histogram.CounterResetHint) hSample {
388h := tsdbutil.GenerateTestHistogram(int(ts + 1))
389h.CounterResetHint = hint
390return hSample{t: ts, h: h}
391}
392
393func floatHistogramSample(ts int64, hint histogram.CounterResetHint) fhSample {
394fh := tsdbutil.GenerateTestFloatHistogram(int(ts + 1))
395fh.CounterResetHint = hint
396return fhSample{t: ts, fh: fh}
397}
398
399// Shorthands for counter reset hints.
400const (
401uk = histogram.UnknownCounterReset
402cr = histogram.CounterReset
403nr = histogram.NotCounterReset
404ga = histogram.GaugeType
405)
406
407func TestCompactingChunkSeriesMerger(t *testing.T) {
408m := NewCompactingChunkSeriesMerger(ChainedSeriesMerge)
409
410// histogramSample returns a histogram that is unique to the ts.
411histogramSample := func(ts int64) hSample {
412return histogramSample(ts, uk)
413}
414
415floatHistogramSample := func(ts int64) fhSample {
416return floatHistogramSample(ts, uk)
417}
418
419for _, tc := range []struct {
420name string
421input []ChunkSeries
422expected ChunkSeries
423}{
424{
425name: "single empty series",
426input: []ChunkSeries{
427NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"), nil),
428},
429expected: NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"), nil),
430},
431{
432name: "single series",
433input: []ChunkSeries{
434NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"), []chunks.Sample{fSample{1, 1}, fSample{2, 2}}, []chunks.Sample{fSample{3, 3}}),
435},
436expected: NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"), []chunks.Sample{fSample{1, 1}, fSample{2, 2}}, []chunks.Sample{fSample{3, 3}}),
437},
438{
439name: "two empty series",
440input: []ChunkSeries{
441NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"), nil),
442NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"), nil),
443},
444expected: NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"), nil),
445},
446{
447name: "two non overlapping",
448input: []ChunkSeries{
449NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"), []chunks.Sample{fSample{1, 1}, fSample{2, 2}}, []chunks.Sample{fSample{3, 3}, fSample{5, 5}}),
450NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"), []chunks.Sample{fSample{7, 7}, fSample{9, 9}}, []chunks.Sample{fSample{10, 10}}),
451},
452expected: NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"), []chunks.Sample{fSample{1, 1}, fSample{2, 2}}, []chunks.Sample{fSample{3, 3}, fSample{5, 5}}, []chunks.Sample{fSample{7, 7}, fSample{9, 9}}, []chunks.Sample{fSample{10, 10}}),
453},
454{
455name: "two overlapping",
456input: []ChunkSeries{
457NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"), []chunks.Sample{fSample{1, 1}, fSample{2, 2}}, []chunks.Sample{fSample{3, 3}, fSample{8, 8}}),
458NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"), []chunks.Sample{fSample{7, 7}, fSample{9, 9}}, []chunks.Sample{fSample{10, 10}}),
459},
460expected: NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"), []chunks.Sample{fSample{1, 1}, fSample{2, 2}}, []chunks.Sample{fSample{3, 3}, fSample{7, 7}, fSample{8, 8}, fSample{9, 9}}, []chunks.Sample{fSample{10, 10}}),
461},
462{
463name: "two duplicated",
464input: []ChunkSeries{
465NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"), []chunks.Sample{fSample{1, 1}, fSample{2, 2}, fSample{3, 3}, fSample{5, 5}}),
466NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"), []chunks.Sample{fSample{2, 2}, fSample{3, 3}, fSample{5, 5}}),
467},
468expected: NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"), []chunks.Sample{fSample{1, 1}, fSample{2, 2}, fSample{3, 3}, fSample{5, 5}}),
469},
470{
471name: "three overlapping",
472input: []ChunkSeries{
473NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"), []chunks.Sample{fSample{1, 1}, fSample{2, 2}, fSample{3, 3}, fSample{5, 5}}),
474NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"), []chunks.Sample{fSample{2, 2}, fSample{3, 3}, fSample{6, 6}}),
475NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"), []chunks.Sample{fSample{0, 0}, fSample{4, 4}}),
476},
477expected: NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"), []chunks.Sample{fSample{0, 0}, fSample{1, 1}, fSample{2, 2}, fSample{3, 3}, fSample{4, 4}, fSample{5, 5}, fSample{6, 6}}),
478},
479{
480name: "three in chained overlap",
481input: []ChunkSeries{
482NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"), []chunks.Sample{fSample{1, 1}, fSample{2, 2}, fSample{3, 3}, fSample{5, 5}}),
483NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"), []chunks.Sample{fSample{4, 4}, fSample{6, 66}}),
484NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"), []chunks.Sample{fSample{6, 6}, fSample{10, 10}}),
485},
486expected: NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"), []chunks.Sample{fSample{1, 1}, fSample{2, 2}, fSample{3, 3}, fSample{4, 4}, fSample{5, 5}, fSample{6, 66}, fSample{10, 10}}),
487},
488{
489name: "three in chained overlap complex",
490input: []ChunkSeries{
491NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"), []chunks.Sample{fSample{0, 0}, fSample{5, 5}}, []chunks.Sample{fSample{10, 10}, fSample{15, 15}}),
492NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"), []chunks.Sample{fSample{2, 2}, fSample{20, 20}}, []chunks.Sample{fSample{25, 25}, fSample{30, 30}}),
493NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"), []chunks.Sample{fSample{18, 18}, fSample{26, 26}}, []chunks.Sample{fSample{31, 31}, fSample{35, 35}}),
494},
495expected: NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"),
496[]chunks.Sample{fSample{0, 0}, fSample{2, 2}, fSample{5, 5}, fSample{10, 10}, fSample{15, 15}, fSample{18, 18}, fSample{20, 20}, fSample{25, 25}, fSample{26, 26}, fSample{30, 30}},
497[]chunks.Sample{fSample{31, 31}, fSample{35, 35}},
498),
499},
500{
501name: "110 overlapping",
502input: []ChunkSeries{
503NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"), chunks.GenerateSamples(0, 110)), // [0 - 110)
504NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"), chunks.GenerateSamples(60, 50)), // [60 - 110)
505},
506expected: NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"),
507chunks.GenerateSamples(0, 110),
508),
509},
510{
511name: "150 overlapping samples, split chunk",
512input: []ChunkSeries{
513NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"), chunks.GenerateSamples(0, 90)), // [0 - 90)
514NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"), chunks.GenerateSamples(60, 90)), // [90 - 150)
515},
516expected: NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"),
517chunks.GenerateSamples(0, 120),
518chunks.GenerateSamples(120, 30),
519),
520},
521{
522name: "histogram chunks overlapping",
523input: []ChunkSeries{
524NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"), []chunks.Sample{histogramSample(0), histogramSample(5)}, []chunks.Sample{histogramSample(10), histogramSample(15)}),
525NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"), []chunks.Sample{histogramSample(2), histogramSample(20)}, []chunks.Sample{histogramSample(25), histogramSample(30)}),
526NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"), []chunks.Sample{histogramSample(18), histogramSample(26)}, []chunks.Sample{histogramSample(31), histogramSample(35)}),
527},
528expected: NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"),
529[]chunks.Sample{histogramSample(0), histogramSample(2), histogramSample(5), histogramSample(10), histogramSample(15), histogramSample(18), histogramSample(20), histogramSample(25), histogramSample(26), histogramSample(30)},
530[]chunks.Sample{histogramSample(31), histogramSample(35)},
531),
532},
533{
534name: "histogram chunks overlapping with float chunks",
535input: []ChunkSeries{
536NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"), []chunks.Sample{histogramSample(0), histogramSample(5)}, []chunks.Sample{histogramSample(10), histogramSample(15)}),
537NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"), []chunks.Sample{fSample{1, 1}, fSample{12, 12}}, []chunks.Sample{fSample{14, 14}}),
538},
539expected: NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"),
540[]chunks.Sample{histogramSample(0)},
541[]chunks.Sample{fSample{1, 1}},
542[]chunks.Sample{histogramSample(5), histogramSample(10)},
543[]chunks.Sample{fSample{12, 12}, fSample{14, 14}},
544[]chunks.Sample{histogramSample(15)},
545),
546},
547{
548name: "float histogram chunks overlapping",
549input: []ChunkSeries{
550NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"), []chunks.Sample{floatHistogramSample(0), floatHistogramSample(5)}, []chunks.Sample{floatHistogramSample(10), floatHistogramSample(15)}),
551NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"), []chunks.Sample{floatHistogramSample(2), floatHistogramSample(20)}, []chunks.Sample{floatHistogramSample(25), floatHistogramSample(30)}),
552NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"), []chunks.Sample{floatHistogramSample(18), floatHistogramSample(26)}, []chunks.Sample{floatHistogramSample(31), floatHistogramSample(35)}),
553},
554expected: NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"),
555[]chunks.Sample{floatHistogramSample(0), floatHistogramSample(2), floatHistogramSample(5), floatHistogramSample(10), floatHistogramSample(15), floatHistogramSample(18), floatHistogramSample(20), floatHistogramSample(25), floatHistogramSample(26), floatHistogramSample(30)},
556[]chunks.Sample{floatHistogramSample(31), floatHistogramSample(35)},
557),
558},
559{
560name: "float histogram chunks overlapping with float chunks",
561input: []ChunkSeries{
562NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"), []chunks.Sample{floatHistogramSample(0), floatHistogramSample(5)}, []chunks.Sample{floatHistogramSample(10), floatHistogramSample(15)}),
563NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"), []chunks.Sample{fSample{1, 1}, fSample{12, 12}}, []chunks.Sample{fSample{14, 14}}),
564},
565expected: NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"),
566[]chunks.Sample{floatHistogramSample(0)},
567[]chunks.Sample{fSample{1, 1}},
568[]chunks.Sample{floatHistogramSample(5), floatHistogramSample(10)},
569[]chunks.Sample{fSample{12, 12}, fSample{14, 14}},
570[]chunks.Sample{floatHistogramSample(15)},
571),
572},
573{
574name: "float histogram chunks overlapping with histogram chunks",
575input: []ChunkSeries{
576NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"), []chunks.Sample{floatHistogramSample(0), floatHistogramSample(5)}, []chunks.Sample{floatHistogramSample(10), floatHistogramSample(15)}),
577NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"), []chunks.Sample{histogramSample(1), histogramSample(12)}, []chunks.Sample{histogramSample(14)}),
578},
579expected: NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"),
580[]chunks.Sample{floatHistogramSample(0)},
581[]chunks.Sample{histogramSample(1)},
582[]chunks.Sample{floatHistogramSample(5), floatHistogramSample(10)},
583[]chunks.Sample{histogramSample(12), histogramSample(14)},
584[]chunks.Sample{floatHistogramSample(15)},
585),
586},
587} {
588t.Run(tc.name, func(t *testing.T) {
589merged := m(tc.input...)
590require.Equal(t, tc.expected.Labels(), merged.Labels())
591actChks, actErr := ExpandChunks(merged.Iterator(nil))
592expChks, expErr := ExpandChunks(tc.expected.Iterator(nil))
593
594require.Equal(t, expErr, actErr)
595require.Equal(t, expChks, actChks)
596
597actSamples := chunks.ChunkMetasToSamples(actChks)
598expSamples := chunks.ChunkMetasToSamples(expChks)
599require.Equal(t, expSamples, actSamples)
600})
601}
602}
603
604func TestCompactingChunkSeriesMergerHistogramCounterResetHint(t *testing.T) {
605m := NewCompactingChunkSeriesMerger(ChainedSeriesMerge)
606
607for sampleType, sampleFunc := range map[string]func(int64, histogram.CounterResetHint) chunks.Sample{
608"histogram": func(ts int64, hint histogram.CounterResetHint) chunks.Sample { return histogramSample(ts, hint) },
609"float histogram": func(ts int64, hint histogram.CounterResetHint) chunks.Sample { return floatHistogramSample(ts, hint) },
610} {
611for name, tc := range map[string]struct {
612input []ChunkSeries
613expected ChunkSeries
614}{
615"histogram counter reset hint kept in single series": {
616input: []ChunkSeries{
617NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"),
618[]chunks.Sample{sampleFunc(0, cr), sampleFunc(5, uk)},
619[]chunks.Sample{sampleFunc(10, cr), sampleFunc(15, uk)},
620),
621},
622expected: NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"),
623[]chunks.Sample{sampleFunc(0, cr), sampleFunc(5, uk)},
624[]chunks.Sample{sampleFunc(10, cr), sampleFunc(15, uk)},
625),
626},
627"histogram not counter reset hint kept in single series": {
628input: []ChunkSeries{
629NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"),
630[]chunks.Sample{sampleFunc(0, nr), sampleFunc(5, uk)},
631[]chunks.Sample{sampleFunc(10, nr), sampleFunc(15, uk)},
632),
633},
634expected: NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"),
635[]chunks.Sample{sampleFunc(0, nr), sampleFunc(5, uk)},
636[]chunks.Sample{sampleFunc(10, nr), sampleFunc(15, uk)},
637),
638},
639"histogram counter reset hint kept in multiple equal series": {
640input: []ChunkSeries{
641NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"),
642[]chunks.Sample{sampleFunc(0, cr), sampleFunc(5, uk)},
643[]chunks.Sample{sampleFunc(10, cr), sampleFunc(15, uk)},
644),
645NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"),
646[]chunks.Sample{sampleFunc(0, cr), sampleFunc(5, uk)},
647[]chunks.Sample{sampleFunc(10, cr), sampleFunc(15, uk)},
648),
649},
650expected: NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"),
651[]chunks.Sample{sampleFunc(0, cr), sampleFunc(5, uk)},
652[]chunks.Sample{sampleFunc(10, cr), sampleFunc(15, uk)},
653),
654},
655"histogram not counter reset hint kept in multiple equal series": {
656input: []ChunkSeries{
657NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"),
658[]chunks.Sample{sampleFunc(0, nr), sampleFunc(5, uk)},
659[]chunks.Sample{sampleFunc(10, nr), sampleFunc(15, uk)},
660),
661NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"),
662[]chunks.Sample{sampleFunc(0, nr), sampleFunc(5, uk)},
663[]chunks.Sample{sampleFunc(10, nr), sampleFunc(15, uk)},
664),
665},
666expected: NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"),
667[]chunks.Sample{sampleFunc(0, nr), sampleFunc(5, uk)},
668[]chunks.Sample{sampleFunc(10, nr), sampleFunc(15, uk)},
669),
670},
671"histogram counter reset hint dropped from differing series": {
672input: []ChunkSeries{
673NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"),
674[]chunks.Sample{sampleFunc(0, cr), sampleFunc(5, uk)},
675[]chunks.Sample{sampleFunc(10, cr), sampleFunc(15, uk)},
676),
677NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"),
678[]chunks.Sample{sampleFunc(0, cr), sampleFunc(5, uk)},
679[]chunks.Sample{sampleFunc(10, cr), sampleFunc(12, uk), sampleFunc(15, uk)},
680),
681},
682expected: NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"),
683[]chunks.Sample{sampleFunc(0, cr), sampleFunc(5, uk)},
684[]chunks.Sample{sampleFunc(10, uk), sampleFunc(12, uk), sampleFunc(15, uk)},
685),
686},
687"histogram counter not reset hint dropped from differing series": {
688input: []ChunkSeries{
689NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"),
690[]chunks.Sample{sampleFunc(0, nr), sampleFunc(5, uk)},
691[]chunks.Sample{sampleFunc(10, nr), sampleFunc(15, uk)},
692),
693NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"),
694[]chunks.Sample{sampleFunc(0, nr), sampleFunc(5, uk)},
695[]chunks.Sample{sampleFunc(10, nr), sampleFunc(12, uk), sampleFunc(15, uk)},
696),
697},
698expected: NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"),
699[]chunks.Sample{sampleFunc(0, nr), sampleFunc(5, uk)},
700[]chunks.Sample{sampleFunc(10, uk), sampleFunc(12, uk), sampleFunc(15, uk)},
701),
702},
703} {
704t.Run(sampleType+"/"+name, func(t *testing.T) {
705merged := m(tc.input...)
706require.Equal(t, tc.expected.Labels(), merged.Labels())
707actChks, actErr := ExpandChunks(merged.Iterator(nil))
708expChks, expErr := ExpandChunks(tc.expected.Iterator(nil))
709
710require.Equal(t, expErr, actErr)
711require.Equal(t, expChks, actChks)
712
713actSamples := chunks.ChunkMetasToSamples(actChks)
714expSamples := chunks.ChunkMetasToSamples(expChks)
715require.Equal(t, expSamples, actSamples)
716})
717}
718}
719}
720
721func TestConcatenatingChunkSeriesMerger(t *testing.T) {
722m := NewConcatenatingChunkSeriesMerger()
723
724for _, tc := range []struct {
725name string
726input []ChunkSeries
727expected ChunkSeries
728}{
729{
730name: "single empty series",
731input: []ChunkSeries{
732NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"), nil),
733},
734expected: NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"), nil),
735},
736{
737name: "single series",
738input: []ChunkSeries{
739NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"), []chunks.Sample{fSample{1, 1}, fSample{2, 2}}, []chunks.Sample{fSample{3, 3}}),
740},
741expected: NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"), []chunks.Sample{fSample{1, 1}, fSample{2, 2}}, []chunks.Sample{fSample{3, 3}}),
742},
743{
744name: "two empty series",
745input: []ChunkSeries{
746NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"), nil),
747NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"), nil),
748},
749expected: NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"), nil, nil),
750},
751{
752name: "two non overlapping",
753input: []ChunkSeries{
754NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"), []chunks.Sample{fSample{1, 1}, fSample{2, 2}}, []chunks.Sample{fSample{3, 3}, fSample{5, 5}}),
755NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"), []chunks.Sample{fSample{7, 7}, fSample{9, 9}}, []chunks.Sample{fSample{10, 10}}),
756},
757expected: NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"), []chunks.Sample{fSample{1, 1}, fSample{2, 2}}, []chunks.Sample{fSample{3, 3}, fSample{5, 5}}, []chunks.Sample{fSample{7, 7}, fSample{9, 9}}, []chunks.Sample{fSample{10, 10}}),
758},
759{
760name: "two overlapping",
761input: []ChunkSeries{
762NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"), []chunks.Sample{fSample{1, 1}, fSample{2, 2}}, []chunks.Sample{fSample{3, 3}, fSample{8, 8}}),
763NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"), []chunks.Sample{fSample{7, 7}, fSample{9, 9}}, []chunks.Sample{fSample{10, 10}}),
764},
765expected: NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"),
766[]chunks.Sample{fSample{1, 1}, fSample{2, 2}}, []chunks.Sample{fSample{3, 3}, fSample{8, 8}},
767[]chunks.Sample{fSample{7, 7}, fSample{9, 9}}, []chunks.Sample{fSample{10, 10}},
768),
769},
770{
771name: "two duplicated",
772input: []ChunkSeries{
773NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"), []chunks.Sample{fSample{1, 1}, fSample{2, 2}, fSample{3, 3}, fSample{5, 5}}),
774NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"), []chunks.Sample{fSample{2, 2}, fSample{3, 3}, fSample{5, 5}}),
775},
776expected: NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"),
777[]chunks.Sample{fSample{1, 1}, fSample{2, 2}, fSample{3, 3}, fSample{5, 5}},
778[]chunks.Sample{fSample{2, 2}, fSample{3, 3}, fSample{5, 5}},
779),
780},
781{
782name: "three overlapping",
783input: []ChunkSeries{
784NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"), []chunks.Sample{fSample{1, 1}, fSample{2, 2}, fSample{3, 3}, fSample{5, 5}}),
785NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"), []chunks.Sample{fSample{2, 2}, fSample{3, 3}, fSample{6, 6}}),
786NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"), []chunks.Sample{fSample{0, 0}, fSample{4, 4}}),
787},
788expected: NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"),
789[]chunks.Sample{fSample{1, 1}, fSample{2, 2}, fSample{3, 3}, fSample{5, 5}},
790[]chunks.Sample{fSample{2, 2}, fSample{3, 3}, fSample{6, 6}},
791[]chunks.Sample{fSample{0, 0}, fSample{4, 4}},
792),
793},
794{
795name: "three in chained overlap",
796input: []ChunkSeries{
797NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"), []chunks.Sample{fSample{1, 1}, fSample{2, 2}, fSample{3, 3}, fSample{5, 5}}),
798NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"), []chunks.Sample{fSample{4, 4}, fSample{6, 66}}),
799NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"), []chunks.Sample{fSample{6, 6}, fSample{10, 10}}),
800},
801expected: NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"),
802[]chunks.Sample{fSample{1, 1}, fSample{2, 2}, fSample{3, 3}, fSample{5, 5}},
803[]chunks.Sample{fSample{4, 4}, fSample{6, 66}},
804[]chunks.Sample{fSample{6, 6}, fSample{10, 10}},
805),
806},
807{
808name: "three in chained overlap complex",
809input: []ChunkSeries{
810NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"), []chunks.Sample{fSample{0, 0}, fSample{5, 5}}, []chunks.Sample{fSample{10, 10}, fSample{15, 15}}),
811NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"), []chunks.Sample{fSample{2, 2}, fSample{20, 20}}, []chunks.Sample{fSample{25, 25}, fSample{30, 30}}),
812NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"), []chunks.Sample{fSample{18, 18}, fSample{26, 26}}, []chunks.Sample{fSample{31, 31}, fSample{35, 35}}),
813},
814expected: NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"),
815[]chunks.Sample{fSample{0, 0}, fSample{5, 5}}, []chunks.Sample{fSample{10, 10}, fSample{15, 15}},
816[]chunks.Sample{fSample{2, 2}, fSample{20, 20}}, []chunks.Sample{fSample{25, 25}, fSample{30, 30}},
817[]chunks.Sample{fSample{18, 18}, fSample{26, 26}}, []chunks.Sample{fSample{31, 31}, fSample{35, 35}},
818),
819},
820{
821name: "110 overlapping",
822input: []ChunkSeries{
823NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"), chunks.GenerateSamples(0, 110)), // [0 - 110)
824NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"), chunks.GenerateSamples(60, 50)), // [60 - 110)
825},
826expected: NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"),
827chunks.GenerateSamples(0, 110),
828chunks.GenerateSamples(60, 50),
829),
830},
831{
832name: "150 overlapping samples, simply concatenated and no splits",
833input: []ChunkSeries{
834NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"), chunks.GenerateSamples(0, 90)), // [0 - 90)
835NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"), chunks.GenerateSamples(60, 90)), // [90 - 150)
836},
837expected: NewListChunkSeriesFromSamples(labels.FromStrings("bar", "baz"),
838chunks.GenerateSamples(0, 90),
839chunks.GenerateSamples(60, 90),
840),
841},
842} {
843t.Run(tc.name, func(t *testing.T) {
844merged := m(tc.input...)
845require.Equal(t, tc.expected.Labels(), merged.Labels())
846actChks, actErr := ExpandChunks(merged.Iterator(nil))
847expChks, expErr := ExpandChunks(tc.expected.Iterator(nil))
848
849require.Equal(t, expErr, actErr)
850require.Equal(t, expChks, actChks)
851})
852}
853}
854
855func TestConcatenatingChunkIterator(t *testing.T) {
856chunk1, err := chunks.ChunkFromSamples([]chunks.Sample{fSample{t: 1, f: 10}})
857require.NoError(t, err)
858chunk2, err := chunks.ChunkFromSamples([]chunks.Sample{fSample{t: 2, f: 20}})
859require.NoError(t, err)
860chunk3, err := chunks.ChunkFromSamples([]chunks.Sample{fSample{t: 3, f: 30}})
861require.NoError(t, err)
862
863testError := errors.New("something went wrong")
864
865testCases := map[string]struct {
866iterators []chunks.Iterator
867expectedChunks []chunks.Meta
868expectedError error
869}{
870"many successful iterators": {
871iterators: []chunks.Iterator{
872NewListChunkSeriesIterator(chunk1, chunk2),
873NewListChunkSeriesIterator(chunk3),
874},
875expectedChunks: []chunks.Meta{chunk1, chunk2, chunk3},
876},
877"single failing iterator": {
878iterators: []chunks.Iterator{
879errChunksIterator{err: testError},
880},
881expectedError: testError,
882},
883"some failing and some successful iterators": {
884iterators: []chunks.Iterator{
885NewListChunkSeriesIterator(chunk1, chunk2),
886errChunksIterator{err: testError},
887NewListChunkSeriesIterator(chunk3),
888},
889expectedChunks: []chunks.Meta{chunk1, chunk2}, // Should stop before advancing to last iterator.
890expectedError: testError,
891},
892}
893
894for name, testCase := range testCases {
895t.Run(name, func(t *testing.T) {
896it := concatenatingChunkIterator{iterators: testCase.iterators}
897var chks []chunks.Meta
898
899for it.Next() {
900chks = append(chks, it.At())
901}
902
903require.Equal(t, testCase.expectedChunks, chks)
904
905if testCase.expectedError == nil {
906require.NoError(t, it.Err())
907} else {
908require.EqualError(t, it.Err(), testCase.expectedError.Error())
909}
910})
911}
912}
913
914type mockQuerier struct {
915LabelQuerier
916
917toReturn []Series
918}
919
920type seriesByLabel []Series
921
922func (a seriesByLabel) Len() int { return len(a) }
923func (a seriesByLabel) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
924func (a seriesByLabel) Less(i, j int) bool { return labels.Compare(a[i].Labels(), a[j].Labels()) < 0 }
925
926func (m *mockQuerier) Select(_ context.Context, sortSeries bool, _ *SelectHints, _ ...*labels.Matcher) SeriesSet {
927cpy := make([]Series, len(m.toReturn))
928copy(cpy, m.toReturn)
929if sortSeries {
930sort.Sort(seriesByLabel(cpy))
931}
932
933return NewMockSeriesSet(cpy...)
934}
935
936type mockChunkQuerier struct {
937LabelQuerier
938
939toReturn []ChunkSeries
940}
941
942type chunkSeriesByLabel []ChunkSeries
943
944func (a chunkSeriesByLabel) Len() int { return len(a) }
945func (a chunkSeriesByLabel) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
946func (a chunkSeriesByLabel) Less(i, j int) bool {
947return labels.Compare(a[i].Labels(), a[j].Labels()) < 0
948}
949
950func (m *mockChunkQuerier) Select(_ context.Context, sortSeries bool, _ *SelectHints, _ ...*labels.Matcher) ChunkSeriesSet {
951cpy := make([]ChunkSeries, len(m.toReturn))
952copy(cpy, m.toReturn)
953if sortSeries {
954sort.Sort(chunkSeriesByLabel(cpy))
955}
956
957return NewMockChunkSeriesSet(cpy...)
958}
959
960type mockSeriesSet struct {
961idx int
962series []Series
963}
964
965func NewMockSeriesSet(series ...Series) SeriesSet {
966return &mockSeriesSet{
967idx: -1,
968series: series,
969}
970}
971
972func (m *mockSeriesSet) Next() bool {
973m.idx++
974return m.idx < len(m.series)
975}
976
977func (m *mockSeriesSet) At() Series { return m.series[m.idx] }
978
979func (m *mockSeriesSet) Err() error { return nil }
980
981func (m *mockSeriesSet) Warnings() annotations.Annotations { return nil }
982
983type mockChunkSeriesSet struct {
984idx int
985series []ChunkSeries
986}
987
988func NewMockChunkSeriesSet(series ...ChunkSeries) ChunkSeriesSet {
989return &mockChunkSeriesSet{
990idx: -1,
991series: series,
992}
993}
994
995func (m *mockChunkSeriesSet) Next() bool {
996m.idx++
997return m.idx < len(m.series)
998}
999
1000func (m *mockChunkSeriesSet) At() ChunkSeries { return m.series[m.idx] }
1001
1002func (m *mockChunkSeriesSet) Err() error { return nil }
1003
1004func (m *mockChunkSeriesSet) Warnings() annotations.Annotations { return nil }
1005
1006func TestChainSampleIterator(t *testing.T) {
1007for sampleType, sampleFunc := range map[string]func(int64) chunks.Sample{
1008"float": func(ts int64) chunks.Sample { return fSample{ts, float64(ts)} },
1009"histogram": func(ts int64) chunks.Sample { return histogramSample(ts, uk) },
1010"float histogram": func(ts int64) chunks.Sample { return floatHistogramSample(ts, uk) },
1011} {
1012for name, tc := range map[string]struct {
1013input []chunkenc.Iterator
1014expected []chunks.Sample
1015}{
1016"single iterator": {
1017input: []chunkenc.Iterator{
1018NewListSeriesIterator(samples{sampleFunc(0), sampleFunc(1)}),
1019},
1020expected: []chunks.Sample{sampleFunc(0), sampleFunc(1)},
1021},
1022"non overlapping iterators": {
1023input: []chunkenc.Iterator{
1024NewListSeriesIterator(samples{sampleFunc(0), sampleFunc(1)}),
1025NewListSeriesIterator(samples{sampleFunc(2), sampleFunc(3)}),
1026},
1027expected: []chunks.Sample{sampleFunc(0), sampleFunc(1), sampleFunc(2), sampleFunc(3)},
1028},
1029"overlapping but distinct iterators": {
1030input: []chunkenc.Iterator{
1031NewListSeriesIterator(samples{sampleFunc(0), sampleFunc(3)}),
1032NewListSeriesIterator(samples{sampleFunc(1), sampleFunc(4)}),
1033NewListSeriesIterator(samples{sampleFunc(2), sampleFunc(5)}),
1034},
1035expected: []chunks.Sample{
1036sampleFunc(0), sampleFunc(1), sampleFunc(2), sampleFunc(3), sampleFunc(4), sampleFunc(5),
1037},
1038},
1039"overlapping iterators": {
1040input: []chunkenc.Iterator{
1041NewListSeriesIterator(samples{sampleFunc(0), sampleFunc(1)}),
1042NewListSeriesIterator(samples{sampleFunc(0), sampleFunc(2)}),
1043NewListSeriesIterator(samples{sampleFunc(2), sampleFunc(3)}),
1044NewListSeriesIterator(samples{}),
1045NewListSeriesIterator(samples{}),
1046NewListSeriesIterator(samples{}),
1047},
1048expected: []chunks.Sample{sampleFunc(0), sampleFunc(1), sampleFunc(2), sampleFunc(3)},
1049},
1050} {
1051t.Run(sampleType+"/"+name, func(t *testing.T) {
1052merged := ChainSampleIteratorFromIterators(nil, tc.input)
1053actual, err := ExpandSamples(merged, nil)
1054require.NoError(t, err)
1055require.Equal(t, tc.expected, actual)
1056})
1057}
1058}
1059}
1060
1061func TestChainSampleIteratorHistogramCounterResetHint(t *testing.T) {
1062for sampleType, sampleFunc := range map[string]func(int64, histogram.CounterResetHint) chunks.Sample{
1063"histogram": func(ts int64, hint histogram.CounterResetHint) chunks.Sample { return histogramSample(ts, hint) },
1064"float histogram": func(ts int64, hint histogram.CounterResetHint) chunks.Sample { return floatHistogramSample(ts, hint) },
1065} {
1066for name, tc := range map[string]struct {
1067input []chunkenc.Iterator
1068expected []chunks.Sample
1069}{
1070"single iterator": {
1071input: []chunkenc.Iterator{
1072NewListSeriesIterator(samples{sampleFunc(0, cr), sampleFunc(1, cr), sampleFunc(2, uk)}),
1073},
1074expected: []chunks.Sample{sampleFunc(0, uk), sampleFunc(1, cr), sampleFunc(2, uk)},
1075},
1076"single iterator gauge": {
1077input: []chunkenc.Iterator{
1078NewListSeriesIterator(samples{sampleFunc(0, ga), sampleFunc(1, ga), sampleFunc(2, ga)}),
1079},
1080expected: []chunks.Sample{sampleFunc(0, ga), sampleFunc(1, ga), sampleFunc(2, ga)},
1081},
1082"overlapping iterators gauge": {
1083input: []chunkenc.Iterator{
1084NewListSeriesIterator(samples{sampleFunc(0, ga), sampleFunc(1, ga), sampleFunc(2, ga), sampleFunc(4, ga)}),
1085NewListSeriesIterator(samples{sampleFunc(0, ga), sampleFunc(1, ga), sampleFunc(3, ga), sampleFunc(5, ga)}),
1086},
1087expected: []chunks.Sample{sampleFunc(0, ga), sampleFunc(1, ga), sampleFunc(2, ga), sampleFunc(3, ga), sampleFunc(4, ga), sampleFunc(5, ga)},
1088},
1089"non overlapping iterators": {
1090input: []chunkenc.Iterator{
1091NewListSeriesIterator(samples{sampleFunc(0, cr), sampleFunc(1, uk)}),
1092NewListSeriesIterator(samples{sampleFunc(2, cr), sampleFunc(3, cr)}),
1093},
1094expected: []chunks.Sample{sampleFunc(0, uk), sampleFunc(1, uk), sampleFunc(2, uk), sampleFunc(3, cr)},
1095},
1096"overlapping but distinct iterators": {
1097input: []chunkenc.Iterator{
1098NewListSeriesIterator(samples{sampleFunc(0, cr), sampleFunc(3, uk), sampleFunc(5, cr)}),
1099NewListSeriesIterator(samples{sampleFunc(1, uk), sampleFunc(2, cr), sampleFunc(4, cr)}),
1100},
1101expected: []chunks.Sample{
1102sampleFunc(0, uk), sampleFunc(1, uk), sampleFunc(2, cr), sampleFunc(3, uk), sampleFunc(4, uk), sampleFunc(5, uk),
1103},
1104},
1105"overlapping iterators": {
1106input: []chunkenc.Iterator{
1107NewListSeriesIterator(samples{sampleFunc(0, cr), sampleFunc(1, cr), sampleFunc(2, cr)}),
1108NewListSeriesIterator(samples{sampleFunc(0, cr), sampleFunc(1, cr), sampleFunc(2, cr)}),
1109},
1110expected: []chunks.Sample{sampleFunc(0, uk), sampleFunc(1, uk), sampleFunc(2, uk)},
1111},
1112} {
1113t.Run(sampleType+"/"+name, func(t *testing.T) {
1114merged := ChainSampleIteratorFromIterators(nil, tc.input)
1115actual, err := ExpandSamples(merged, nil)
1116require.NoError(t, err)
1117require.Equal(t, tc.expected, actual)
1118})
1119}
1120}
1121}
1122
1123func TestChainSampleIteratorSeek(t *testing.T) {
1124for sampleType, sampleFunc := range map[string]func(int64) chunks.Sample{
1125"float": func(ts int64) chunks.Sample { return fSample{ts, float64(ts)} },
1126"histogram": func(ts int64) chunks.Sample { return histogramSample(ts, uk) },
1127"float histogram": func(ts int64) chunks.Sample { return floatHistogramSample(ts, uk) },
1128} {
1129for name, tc := range map[string]struct {
1130input []chunkenc.Iterator
1131seek int64
1132expected []chunks.Sample
1133}{
1134"single iterator": {
1135input: []chunkenc.Iterator{
1136NewListSeriesIterator(samples{sampleFunc(0), sampleFunc(1), sampleFunc(2)}),
1137},
1138seek: 1,
1139expected: []chunks.Sample{sampleFunc(1), sampleFunc(2)},
1140},
1141"non overlapping iterators": {
1142input: []chunkenc.Iterator{
1143NewListSeriesIterator(samples{sampleFunc(0), sampleFunc(1)}),
1144NewListSeriesIterator(samples{sampleFunc(2), sampleFunc(3)}),
1145},
1146seek: 2,
1147expected: []chunks.Sample{sampleFunc(2), sampleFunc(3)},
1148},
1149"overlapping but distinct iterators": {
1150input: []chunkenc.Iterator{
1151NewListSeriesIterator(samples{sampleFunc(0), sampleFunc(3)}),
1152NewListSeriesIterator(samples{sampleFunc(1), sampleFunc(4)}),
1153NewListSeriesIterator(samples{sampleFunc(2), sampleFunc(5)}),
1154},
1155seek: 2,
1156expected: []chunks.Sample{sampleFunc(2), sampleFunc(3), sampleFunc(4), sampleFunc(5)},
1157},
1158"overlapping iterators": {
1159input: []chunkenc.Iterator{
1160NewListSeriesIterator(samples{sampleFunc(0), sampleFunc(2), sampleFunc(3)}),
1161NewListSeriesIterator(samples{sampleFunc(0), sampleFunc(1), sampleFunc(2)}),
1162},
1163seek: 0,
1164expected: []chunks.Sample{sampleFunc(0), sampleFunc(1), sampleFunc(2), sampleFunc(3)},
1165},
1166} {
1167t.Run(sampleType+"/"+name, func(t *testing.T) {
1168merged := ChainSampleIteratorFromIterators(nil, tc.input)
1169actual := []chunks.Sample{}
1170switch merged.Seek(tc.seek) {
1171case chunkenc.ValFloat:
1172t, f := merged.At()
1173actual = append(actual, fSample{t, f})
1174case chunkenc.ValHistogram:
1175t, h := merged.AtHistogram(nil)
1176actual = append(actual, hSample{t, h})
1177case chunkenc.ValFloatHistogram:
1178t, fh := merged.AtFloatHistogram(nil)
1179actual = append(actual, fhSample{t, fh})
1180}
1181s, err := ExpandSamples(merged, nil)
1182require.NoError(t, err)
1183actual = append(actual, s...)
1184require.Equal(t, tc.expected, actual)
1185})
1186}
1187}
1188}
1189
1190func TestChainSampleIteratorSeekFailingIterator(t *testing.T) {
1191merged := ChainSampleIteratorFromIterators(nil, []chunkenc.Iterator{
1192NewListSeriesIterator(samples{fSample{0, 0.1}, fSample{1, 1.1}, fSample{2, 2.1}}),
1193errIterator{errors.New("something went wrong")},
1194})
1195
1196require.Equal(t, chunkenc.ValNone, merged.Seek(0))
1197require.EqualError(t, merged.Err(), "something went wrong")
1198}
1199
1200func TestChainSampleIteratorNextImmediatelyFailingIterator(t *testing.T) {
1201merged := ChainSampleIteratorFromIterators(nil, []chunkenc.Iterator{
1202NewListSeriesIterator(samples{fSample{0, 0.1}, fSample{1, 1.1}, fSample{2, 2.1}}),
1203errIterator{errors.New("something went wrong")},
1204})
1205
1206require.Equal(t, chunkenc.ValNone, merged.Next())
1207require.EqualError(t, merged.Err(), "something went wrong")
1208
1209// Next() does some special handling for the first iterator, so make sure it handles the first iterator returning an error too.
1210merged = ChainSampleIteratorFromIterators(nil, []chunkenc.Iterator{
1211errIterator{errors.New("something went wrong")},
1212NewListSeriesIterator(samples{fSample{0, 0.1}, fSample{1, 1.1}, fSample{2, 2.1}}),
1213})
1214
1215require.Equal(t, chunkenc.ValNone, merged.Next())
1216require.EqualError(t, merged.Err(), "something went wrong")
1217}
1218
1219func TestChainSampleIteratorSeekHistogramCounterResetHint(t *testing.T) {
1220for sampleType, sampleFunc := range map[string]func(int64, histogram.CounterResetHint) chunks.Sample{
1221"histogram": func(ts int64, hint histogram.CounterResetHint) chunks.Sample { return histogramSample(ts, hint) },
1222"float histogram": func(ts int64, hint histogram.CounterResetHint) chunks.Sample { return floatHistogramSample(ts, hint) },
1223} {
1224for name, tc := range map[string]struct {
1225input []chunkenc.Iterator
1226seek int64
1227expected []chunks.Sample
1228}{
1229"single iterator": {
1230input: []chunkenc.Iterator{
1231NewListSeriesIterator(samples{sampleFunc(0, cr), sampleFunc(1, cr), sampleFunc(2, uk)}),
1232},
1233seek: 1,
1234expected: []chunks.Sample{sampleFunc(1, uk), sampleFunc(2, uk)},
1235},
1236"non overlapping iterators": {
1237input: []chunkenc.Iterator{
1238NewListSeriesIterator(samples{sampleFunc(0, cr), sampleFunc(1, uk)}),
1239NewListSeriesIterator(samples{sampleFunc(2, cr), sampleFunc(3, cr)}),
1240},
1241seek: 2,
1242expected: []chunks.Sample{sampleFunc(2, uk), sampleFunc(3, cr)},
1243},
1244"non overlapping iterators seek to internal reset": {
1245input: []chunkenc.Iterator{
1246NewListSeriesIterator(samples{sampleFunc(0, cr), sampleFunc(1, uk)}),
1247NewListSeriesIterator(samples{sampleFunc(2, cr), sampleFunc(3, cr)}),
1248},
1249seek: 3,
1250expected: []chunks.Sample{sampleFunc(3, uk)},
1251},
1252} {
1253t.Run(sampleType+"/"+name, func(t *testing.T) {
1254merged := ChainSampleIteratorFromIterators(nil, tc.input)
1255actual := []chunks.Sample{}
1256switch merged.Seek(tc.seek) {
1257case chunkenc.ValFloat:
1258t, f := merged.At()
1259actual = append(actual, fSample{t, f})
1260case chunkenc.ValHistogram:
1261t, h := merged.AtHistogram(nil)
1262actual = append(actual, hSample{t, h})
1263case chunkenc.ValFloatHistogram:
1264t, fh := merged.AtFloatHistogram(nil)
1265actual = append(actual, fhSample{t, fh})
1266}
1267s, err := ExpandSamples(merged, nil)
1268require.NoError(t, err)
1269actual = append(actual, s...)
1270require.Equal(t, tc.expected, actual)
1271})
1272}
1273}
1274}
1275
1276func makeSeries(numSeries, numSamples int) []Series {
1277series := []Series{}
1278for j := 0; j < numSeries; j++ {
1279labels := labels.FromStrings("foo", fmt.Sprintf("bar%d", j))
1280samples := []chunks.Sample{}
1281for k := 0; k < numSamples; k++ {
1282samples = append(samples, fSample{t: int64(k), f: float64(k)})
1283}
1284series = append(series, NewListSeries(labels, samples))
1285}
1286return series
1287}
1288
1289func makeMergeSeriesSet(serieses [][]Series) SeriesSet {
1290seriesSets := make([]genericSeriesSet, len(serieses))
1291for i, s := range serieses {
1292seriesSets[i] = &genericSeriesSetAdapter{NewMockSeriesSet(s...)}
1293}
1294return &seriesSetAdapter{newGenericMergeSeriesSet(seriesSets, (&seriesMergerAdapter{VerticalSeriesMergeFunc: ChainedSeriesMerge}).Merge)}
1295}
1296
1297func benchmarkDrain(b *testing.B, makeSeriesSet func() SeriesSet) {
1298var err error
1299var t int64
1300var v float64
1301var iter chunkenc.Iterator
1302for n := 0; n < b.N; n++ {
1303seriesSet := makeSeriesSet()
1304for seriesSet.Next() {
1305iter = seriesSet.At().Iterator(iter)
1306for iter.Next() == chunkenc.ValFloat {
1307t, v = iter.At()
1308}
1309err = iter.Err()
1310}
1311require.NoError(b, err)
1312require.NotEqual(b, t, v) // To ensure the inner loop doesn't get optimised away.
1313}
1314}
1315
1316func BenchmarkNoMergeSeriesSet_100_100(b *testing.B) {
1317series := makeSeries(100, 100)
1318benchmarkDrain(b, func() SeriesSet { return NewMockSeriesSet(series...) })
1319}
1320
1321func BenchmarkMergeSeriesSet(b *testing.B) {
1322for _, bm := range []struct {
1323numSeriesSets, numSeries, numSamples int
1324}{
1325{1, 100, 100},
1326{10, 100, 100},
1327{100, 100, 100},
1328} {
1329serieses := [][]Series{}
1330for i := 0; i < bm.numSeriesSets; i++ {
1331serieses = append(serieses, makeSeries(bm.numSeries, bm.numSamples))
1332}
1333b.Run(fmt.Sprintf("%d_%d_%d", bm.numSeriesSets, bm.numSeries, bm.numSamples), func(b *testing.B) {
1334benchmarkDrain(b, func() SeriesSet { return makeMergeSeriesSet(serieses) })
1335})
1336}
1337}
1338
1339type mockGenericQuerier struct {
1340mtx sync.Mutex
1341
1342closed bool
1343labelNamesCalls int
1344labelNamesRequested []labelNameRequest
1345sortedSeriesRequested []bool
1346
1347resp []string
1348warnings annotations.Annotations
1349err error
1350}
1351
1352type labelNameRequest struct {
1353name string
1354matchers []*labels.Matcher
1355}
1356
1357func (m *mockGenericQuerier) Select(_ context.Context, b bool, _ *SelectHints, _ ...*labels.Matcher) genericSeriesSet {
1358m.mtx.Lock()
1359m.sortedSeriesRequested = append(m.sortedSeriesRequested, b)
1360m.mtx.Unlock()
1361return &mockGenericSeriesSet{resp: m.resp, warnings: m.warnings, err: m.err}
1362}
1363
1364func (m *mockGenericQuerier) LabelValues(_ context.Context, name string, matchers ...*labels.Matcher) ([]string, annotations.Annotations, error) {
1365m.mtx.Lock()
1366m.labelNamesRequested = append(m.labelNamesRequested, labelNameRequest{
1367name: name,
1368matchers: matchers,
1369})
1370m.mtx.Unlock()
1371return m.resp, m.warnings, m.err
1372}
1373
1374func (m *mockGenericQuerier) LabelNames(context.Context, ...*labels.Matcher) ([]string, annotations.Annotations, error) {
1375m.mtx.Lock()
1376m.labelNamesCalls++
1377m.mtx.Unlock()
1378return m.resp, m.warnings, m.err
1379}
1380
1381func (m *mockGenericQuerier) Close() error {
1382m.closed = true
1383return nil
1384}
1385
1386type mockGenericSeriesSet struct {
1387resp []string
1388warnings annotations.Annotations
1389err error
1390
1391curr int
1392}
1393
1394func (m *mockGenericSeriesSet) Next() bool {
1395if m.err != nil {
1396return false
1397}
1398if m.curr >= len(m.resp) {
1399return false
1400}
1401m.curr++
1402return true
1403}
1404
1405func (m *mockGenericSeriesSet) Err() error { return m.err }
1406func (m *mockGenericSeriesSet) Warnings() annotations.Annotations { return m.warnings }
1407
1408func (m *mockGenericSeriesSet) At() Labels {
1409return mockLabels(m.resp[m.curr-1])
1410}
1411
1412type mockLabels string
1413
1414func (l mockLabels) Labels() labels.Labels {
1415return labels.FromStrings("test", string(l))
1416}
1417
1418func unwrapMockGenericQuerier(t *testing.T, qr genericQuerier) *mockGenericQuerier {
1419m, ok := qr.(*mockGenericQuerier)
1420if !ok {
1421s, ok := qr.(*secondaryQuerier)
1422require.True(t, ok, "expected secondaryQuerier got something else")
1423m, ok = s.genericQuerier.(*mockGenericQuerier)
1424require.True(t, ok, "expected mockGenericQuerier got something else")
1425}
1426return m
1427}
1428
1429func TestMergeGenericQuerierWithSecondaries_ErrorHandling(t *testing.T) {
1430var (
1431errStorage = errors.New("storage error")
1432warnStorage = errors.New("storage warning")
1433ctx = context.Background()
1434)
1435for _, tcase := range []struct {
1436name string
1437queriers []genericQuerier
1438
1439expectedSelectsSeries []labels.Labels
1440expectedLabels []string
1441
1442expectedWarnings annotations.Annotations
1443expectedErrs [4]error
1444}{
1445{
1446name: "one successful primary querier",
1447queriers: []genericQuerier{&mockGenericQuerier{resp: []string{"a", "b"}, warnings: nil, err: nil}},
1448expectedSelectsSeries: []labels.Labels{
1449labels.FromStrings("test", "a"),
1450labels.FromStrings("test", "b"),
1451},
1452expectedLabels: []string{"a", "b"},
1453},
1454{
1455name: "multiple successful primary queriers",
1456queriers: []genericQuerier{
1457&mockGenericQuerier{resp: []string{"a", "b"}, warnings: nil, err: nil},
1458&mockGenericQuerier{resp: []string{"b", "c"}, warnings: nil, err: nil},
1459},
1460expectedSelectsSeries: []labels.Labels{
1461labels.FromStrings("test", "a"),
1462labels.FromStrings("test", "b"),
1463labels.FromStrings("test", "c"),
1464},
1465expectedLabels: []string{"a", "b", "c"},
1466},
1467{
1468name: "one failed primary querier",
1469queriers: []genericQuerier{&mockGenericQuerier{warnings: nil, err: errStorage}},
1470expectedErrs: [4]error{errStorage, errStorage, errStorage, errStorage},
1471},
1472{
1473name: "one successful primary querier with successful secondaries",
1474queriers: []genericQuerier{
1475&mockGenericQuerier{resp: []string{"a", "b"}, warnings: nil, err: nil},
1476&secondaryQuerier{genericQuerier: &mockGenericQuerier{resp: []string{"b"}, warnings: nil, err: nil}},
1477&secondaryQuerier{genericQuerier: &mockGenericQuerier{resp: []string{"c"}, warnings: nil, err: nil}},
1478},
1479expectedSelectsSeries: []labels.Labels{
1480labels.FromStrings("test", "a"),
1481labels.FromStrings("test", "b"),
1482labels.FromStrings("test", "c"),
1483},
1484expectedLabels: []string{"a", "b", "c"},
1485},
1486{
1487name: "one successful primary querier with empty response and successful secondaries",
1488queriers: []genericQuerier{
1489&mockGenericQuerier{resp: []string{}, warnings: nil, err: nil},
1490&secondaryQuerier{genericQuerier: &mockGenericQuerier{resp: []string{"b"}, warnings: nil, err: nil}},
1491&secondaryQuerier{genericQuerier: &mockGenericQuerier{resp: []string{"c"}, warnings: nil, err: nil}},
1492},
1493expectedSelectsSeries: []labels.Labels{
1494labels.FromStrings("test", "b"),
1495labels.FromStrings("test", "c"),
1496},
1497expectedLabels: []string{"b", "c"},
1498},
1499{
1500name: "one failed primary querier with successful secondaries",
1501queriers: []genericQuerier{
1502&mockGenericQuerier{warnings: nil, err: errStorage},
1503&secondaryQuerier{genericQuerier: &mockGenericQuerier{resp: []string{"b"}, warnings: nil, err: nil}},
1504&secondaryQuerier{genericQuerier: &mockGenericQuerier{resp: []string{"c"}, warnings: nil, err: nil}},
1505},
1506expectedErrs: [4]error{errStorage, errStorage, errStorage, errStorage},
1507},
1508{
1509name: "one successful primary querier with failed secondaries",
1510queriers: []genericQuerier{
1511&mockGenericQuerier{resp: []string{"a"}, warnings: nil, err: nil},
1512&secondaryQuerier{genericQuerier: &mockGenericQuerier{resp: []string{"b"}, warnings: nil, err: errStorage}},
1513&secondaryQuerier{genericQuerier: &mockGenericQuerier{resp: []string{"c"}, warnings: nil, err: errStorage}},
1514},
1515expectedSelectsSeries: []labels.Labels{
1516labels.FromStrings("test", "a"),
1517},
1518expectedLabels: []string{"a"},
1519expectedWarnings: annotations.New().Add(errStorage),
1520},
1521{
1522name: "successful queriers with warnings",
1523queriers: []genericQuerier{
1524&mockGenericQuerier{resp: []string{"a"}, warnings: annotations.New().Add(warnStorage), err: nil},
1525&secondaryQuerier{genericQuerier: &mockGenericQuerier{resp: []string{"b"}, warnings: annotations.New().Add(warnStorage), err: nil}},
1526},
1527expectedSelectsSeries: []labels.Labels{
1528labels.FromStrings("test", "a"),
1529labels.FromStrings("test", "b"),
1530},
1531expectedLabels: []string{"a", "b"},
1532expectedWarnings: annotations.New().Add(warnStorage),
1533},
1534} {
1535t.Run(tcase.name, func(t *testing.T) {
1536q := &mergeGenericQuerier{
1537queriers: tcase.queriers,
1538mergeFn: func(l ...Labels) Labels { return l[0] },
1539}
1540
1541t.Run("Select", func(t *testing.T) {
1542res := q.Select(context.Background(), false, nil)
1543var lbls []labels.Labels
1544for res.Next() {
1545lbls = append(lbls, res.At().Labels())
1546}
1547require.Subset(t, tcase.expectedWarnings, res.Warnings())
1548require.Equal(t, tcase.expectedErrs[0], res.Err())
1549require.ErrorIs(t, res.Err(), tcase.expectedErrs[0], "expected error doesn't match")
1550require.Equal(t, tcase.expectedSelectsSeries, lbls)
1551
1552for _, qr := range q.queriers {
1553m := unwrapMockGenericQuerier(t, qr)
1554
1555exp := []bool{true}
1556if len(q.queriers) == 1 {
1557exp[0] = false
1558}
1559require.Equal(t, exp, m.sortedSeriesRequested)
1560}
1561})
1562t.Run("LabelNames", func(t *testing.T) {
1563res, w, err := q.LabelNames(ctx)
1564require.Subset(t, tcase.expectedWarnings, w)
1565require.ErrorIs(t, err, tcase.expectedErrs[1], "expected error doesn't match")
1566require.Equal(t, tcase.expectedLabels, res)
1567
1568if err != nil {
1569return
1570}
1571for _, qr := range q.queriers {
1572m := unwrapMockGenericQuerier(t, qr)
1573
1574require.Equal(t, 1, m.labelNamesCalls)
1575}
1576})
1577t.Run("LabelValues", func(t *testing.T) {
1578res, w, err := q.LabelValues(ctx, "test")
1579require.Subset(t, tcase.expectedWarnings, w)
1580require.ErrorIs(t, err, tcase.expectedErrs[2], "expected error doesn't match")
1581require.Equal(t, tcase.expectedLabels, res)
1582
1583if err != nil {
1584return
1585}
1586for _, qr := range q.queriers {
1587m := unwrapMockGenericQuerier(t, qr)
1588
1589require.Equal(t, []labelNameRequest{{name: "test"}}, m.labelNamesRequested)
1590}
1591})
1592t.Run("LabelValuesWithMatchers", func(t *testing.T) {
1593matcher := labels.MustNewMatcher(labels.MatchEqual, "otherLabel", "someValue")
1594res, w, err := q.LabelValues(ctx, "test2", matcher)
1595require.Subset(t, tcase.expectedWarnings, w)
1596require.ErrorIs(t, err, tcase.expectedErrs[3], "expected error doesn't match")
1597require.Equal(t, tcase.expectedLabels, res)
1598
1599if err != nil {
1600return
1601}
1602for _, qr := range q.queriers {
1603m := unwrapMockGenericQuerier(t, qr)
1604
1605require.Equal(t, []labelNameRequest{
1606{name: "test"},
1607{name: "test2", matchers: []*labels.Matcher{matcher}},
1608}, m.labelNamesRequested)
1609}
1610})
1611})
1612}
1613}
1614
1615type errIterator struct {
1616err error
1617}
1618
1619func (e errIterator) Next() chunkenc.ValueType {
1620return chunkenc.ValNone
1621}
1622
1623func (e errIterator) Seek(t int64) chunkenc.ValueType {
1624return chunkenc.ValNone
1625}
1626
1627func (e errIterator) At() (int64, float64) {
1628return 0, 0
1629}
1630
1631func (e errIterator) AtHistogram(*histogram.Histogram) (int64, *histogram.Histogram) {
1632return 0, nil
1633}
1634
1635func (e errIterator) AtFloatHistogram(*histogram.FloatHistogram) (int64, *histogram.FloatHistogram) {
1636return 0, nil
1637}
1638
1639func (e errIterator) AtT() int64 {
1640return 0
1641}
1642
1643func (e errIterator) Err() error {
1644return e.err
1645}
1646