prometheus
397 строк · 10.4 Кб
1// Copyright 2017 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"math/rand"
18"testing"
19
20"github.com/stretchr/testify/require"
21
22"github.com/prometheus/prometheus/model/histogram"
23"github.com/prometheus/prometheus/tsdb/chunkenc"
24"github.com/prometheus/prometheus/tsdb/tsdbutil"
25)
26
27func TestSampleRing(t *testing.T) {
28cases := []struct {
29input []int64
30delta int64
31size int
32}{
33{
34input: []int64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
35delta: 2,
36size: 1,
37},
38{
39input: []int64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
40delta: 2,
41size: 2,
42},
43{
44input: []int64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
45delta: 7,
46size: 3,
47},
48{
49input: []int64{1, 2, 3, 4, 5, 16, 17, 18, 19, 20},
50delta: 7,
51size: 1,
52},
53{
54input: []int64{1, 2, 3, 4, 6},
55delta: 4,
56size: 4,
57},
58}
59for _, c := range cases {
60r := newSampleRing(c.delta, c.size, chunkenc.ValFloat)
61
62input := []fSample{}
63for _, t := range c.input {
64input = append(input, fSample{
65t: t,
66f: float64(rand.Intn(100)),
67})
68}
69
70for i, s := range input {
71r.add(s)
72buffered := r.samples()
73
74for _, sold := range input[:i] {
75found := false
76for _, bs := range buffered {
77if bs.T() == sold.t && bs.F() == sold.f {
78found = true
79break
80}
81}
82
83if found {
84require.GreaterOrEqual(t, sold.t, s.t-c.delta, "%d: unexpected sample %d in buffer; buffer %v", i, sold.t, buffered)
85} else {
86require.Less(t, sold.t, s.t-c.delta, "%d: expected sample %d to be in buffer but was not; buffer %v", i, sold.t, buffered)
87}
88}
89}
90}
91}
92
93func TestSampleRingMixed(t *testing.T) {
94h1 := tsdbutil.GenerateTestHistogram(1)
95h2 := tsdbutil.GenerateTestHistogram(2)
96
97// With ValNone as the preferred type, nothing should be initialized.
98r := newSampleRing(10, 2, chunkenc.ValNone)
99require.Zero(t, len(r.fBuf))
100require.Zero(t, len(r.hBuf))
101require.Zero(t, len(r.fhBuf))
102require.Zero(t, len(r.iBuf))
103
104// But then mixed adds should work as expected.
105r.addF(fSample{t: 1, f: 3.14})
106r.addH(hSample{t: 2, h: h1})
107
108it := r.iterator()
109
110require.Equal(t, chunkenc.ValFloat, it.Next())
111ts, f := it.At()
112require.Equal(t, int64(1), ts)
113require.Equal(t, 3.14, f)
114require.Equal(t, chunkenc.ValHistogram, it.Next())
115var h *histogram.Histogram
116ts, h = it.AtHistogram()
117require.Equal(t, int64(2), ts)
118require.Equal(t, h1, h)
119require.Equal(t, chunkenc.ValNone, it.Next())
120
121r.reset()
122it = r.iterator()
123require.Equal(t, chunkenc.ValNone, it.Next())
124
125r.addF(fSample{t: 3, f: 4.2})
126r.addH(hSample{t: 4, h: h2})
127
128it = r.iterator()
129
130require.Equal(t, chunkenc.ValFloat, it.Next())
131ts, f = it.At()
132require.Equal(t, int64(3), ts)
133require.Equal(t, 4.2, f)
134require.Equal(t, chunkenc.ValHistogram, it.Next())
135ts, h = it.AtHistogram()
136require.Equal(t, int64(4), ts)
137require.Equal(t, h2, h)
138require.Equal(t, chunkenc.ValNone, it.Next())
139}
140
141func TestSampleRingAtFloatHistogram(t *testing.T) {
142fh1 := tsdbutil.GenerateTestFloatHistogram(1)
143fh2 := tsdbutil.GenerateTestFloatHistogram(2)
144h1 := tsdbutil.GenerateTestHistogram(3)
145h2 := tsdbutil.GenerateTestHistogram(4)
146
147// With ValNone as the preferred type, nothing should be initialized.
148r := newSampleRing(10, 2, chunkenc.ValNone)
149require.Zero(t, len(r.fBuf))
150require.Zero(t, len(r.hBuf))
151require.Zero(t, len(r.fhBuf))
152require.Zero(t, len(r.iBuf))
153
154var (
155h *histogram.Histogram
156fh *histogram.FloatHistogram
157ts int64
158)
159
160it := r.iterator()
161require.Equal(t, chunkenc.ValNone, it.Next())
162
163r.addFH(fhSample{t: 1, fh: fh1})
164r.addFH(fhSample{t: 2, fh: fh2})
165
166it = r.iterator()
167
168require.Equal(t, chunkenc.ValFloatHistogram, it.Next())
169ts, fh = it.AtFloatHistogram(fh)
170require.Equal(t, int64(1), ts)
171require.Equal(t, fh1, fh)
172require.Equal(t, chunkenc.ValFloatHistogram, it.Next())
173ts, fh = it.AtFloatHistogram(fh)
174require.Equal(t, int64(2), ts)
175require.Equal(t, fh2, fh)
176require.Equal(t, chunkenc.ValNone, it.Next())
177
178r.reset()
179it = r.iterator()
180require.Equal(t, chunkenc.ValNone, it.Next())
181
182r.addH(hSample{t: 3, h: h1})
183r.addH(hSample{t: 4, h: h2})
184
185it = r.iterator()
186
187require.Equal(t, chunkenc.ValHistogram, it.Next())
188ts, h = it.AtHistogram()
189require.Equal(t, int64(3), ts)
190require.Equal(t, h1, h)
191ts, fh = it.AtFloatHistogram(fh)
192require.Equal(t, int64(3), ts)
193require.Equal(t, h1.ToFloat(nil), fh)
194require.Equal(t, chunkenc.ValHistogram, it.Next())
195ts, h = it.AtHistogram()
196require.Equal(t, int64(4), ts)
197require.Equal(t, h2, h)
198ts, fh = it.AtFloatHistogram(fh)
199require.Equal(t, int64(4), ts)
200require.Equal(t, h2.ToFloat(nil), fh)
201require.Equal(t, chunkenc.ValNone, it.Next())
202}
203
204func TestBufferedSeriesIterator(t *testing.T) {
205var it *BufferedSeriesIterator
206
207bufferEq := func(exp []fSample) {
208var b []fSample
209bit := it.Buffer()
210for bit.Next() == chunkenc.ValFloat {
211t, f := bit.At()
212b = append(b, fSample{t: t, f: f})
213}
214require.Equal(t, exp, b, "buffer mismatch")
215}
216sampleEq := func(ets int64, ev float64) {
217ts, v := it.At()
218require.Equal(t, ets, ts, "timestamp mismatch")
219require.Equal(t, ev, v, "value mismatch")
220}
221prevSampleEq := func(ets int64, ev float64, eok bool) {
222s, ok := it.PeekBack(1)
223require.Equal(t, eok, ok, "exist mismatch")
224require.Equal(t, ets, s.T(), "timestamp mismatch")
225require.Equal(t, ev, s.F(), "value mismatch")
226}
227
228it = NewBufferIterator(NewListSeriesIterator(samples{
229fSample{t: 1, f: 2},
230fSample{t: 2, f: 3},
231fSample{t: 3, f: 4},
232fSample{t: 4, f: 5},
233fSample{t: 5, f: 6},
234fSample{t: 99, f: 8},
235fSample{t: 100, f: 9},
236fSample{t: 101, f: 10},
237}), 2)
238
239require.Equal(t, chunkenc.ValFloat, it.Seek(-123), "seek failed")
240sampleEq(1, 2)
241prevSampleEq(0, 0, false)
242bufferEq(nil)
243
244require.Equal(t, chunkenc.ValFloat, it.Next(), "next failed")
245sampleEq(2, 3)
246prevSampleEq(1, 2, true)
247bufferEq([]fSample{{t: 1, f: 2}})
248
249require.Equal(t, chunkenc.ValFloat, it.Next(), "next failed")
250require.Equal(t, chunkenc.ValFloat, it.Next(), "next failed")
251require.Equal(t, chunkenc.ValFloat, it.Next(), "next failed")
252sampleEq(5, 6)
253prevSampleEq(4, 5, true)
254bufferEq([]fSample{{t: 2, f: 3}, {t: 3, f: 4}, {t: 4, f: 5}})
255
256require.Equal(t, chunkenc.ValFloat, it.Seek(5), "seek failed")
257sampleEq(5, 6)
258prevSampleEq(4, 5, true)
259bufferEq([]fSample{{t: 2, f: 3}, {t: 3, f: 4}, {t: 4, f: 5}})
260
261require.Equal(t, chunkenc.ValFloat, it.Seek(101), "seek failed")
262sampleEq(101, 10)
263prevSampleEq(100, 9, true)
264bufferEq([]fSample{{t: 99, f: 8}, {t: 100, f: 9}})
265
266require.Equal(t, chunkenc.ValNone, it.Next(), "next succeeded unexpectedly")
267require.Equal(t, chunkenc.ValNone, it.Seek(1024), "seek succeeded unexpectedly")
268}
269
270// At() should not be called once Next() returns false.
271func TestBufferedSeriesIteratorNoBadAt(t *testing.T) {
272done := false
273
274m := &mockSeriesIterator{
275seek: func(int64) chunkenc.ValueType { return chunkenc.ValNone },
276at: func() (int64, float64) {
277require.False(t, done, "unexpectedly done")
278done = true
279return 0, 0
280},
281next: func() chunkenc.ValueType {
282if done {
283return chunkenc.ValNone
284}
285return chunkenc.ValFloat
286},
287err: func() error { return nil },
288}
289
290it := NewBufferIterator(m, 60)
291it.Next()
292it.Next()
293}
294
295func TestBufferedSeriesIteratorMixedHistograms(t *testing.T) {
296histograms := tsdbutil.GenerateTestHistograms(2)
297
298it := NewBufferIterator(NewListSeriesIterator(samples{
299fhSample{t: 1, fh: histograms[0].ToFloat(nil)},
300hSample{t: 2, h: histograms[1]},
301}), 2)
302
303require.Equal(t, chunkenc.ValNone, it.Seek(3))
304require.NoError(t, it.Err())
305
306buf := it.Buffer()
307
308require.Equal(t, chunkenc.ValFloatHistogram, buf.Next())
309_, fh := buf.AtFloatHistogram(nil)
310require.Equal(t, histograms[0].ToFloat(nil), fh)
311
312require.Equal(t, chunkenc.ValHistogram, buf.Next())
313_, fh = buf.AtFloatHistogram(nil)
314require.Equal(t, histograms[1].ToFloat(nil), fh)
315}
316
317func BenchmarkBufferedSeriesIterator(b *testing.B) {
318// Simulate a 5 minute rate.
319it := NewBufferIterator(newFakeSeriesIterator(int64(b.N), 30), 5*60)
320
321b.SetBytes(16)
322b.ReportAllocs()
323b.ResetTimer()
324
325for it.Next() != chunkenc.ValNone {
326// Scan everything.
327}
328require.NoError(b, it.Err())
329}
330
331type mockSeriesIterator struct {
332seek func(int64) chunkenc.ValueType
333at func() (int64, float64)
334next func() chunkenc.ValueType
335err func() error
336}
337
338func (m *mockSeriesIterator) Seek(t int64) chunkenc.ValueType { return m.seek(t) }
339func (m *mockSeriesIterator) At() (int64, float64) { return m.at() }
340func (m *mockSeriesIterator) Next() chunkenc.ValueType { return m.next() }
341func (m *mockSeriesIterator) Err() error { return m.err() }
342
343func (m *mockSeriesIterator) AtHistogram(*histogram.Histogram) (int64, *histogram.Histogram) {
344return 0, nil // Not really mocked.
345}
346
347func (m *mockSeriesIterator) AtFloatHistogram(*histogram.FloatHistogram) (int64, *histogram.FloatHistogram) {
348return 0, nil // Not really mocked.
349}
350
351func (m *mockSeriesIterator) AtT() int64 {
352return 0 // Not really mocked.
353}
354
355type fakeSeriesIterator struct {
356nsamples int64
357step int64
358idx int64
359}
360
361func newFakeSeriesIterator(nsamples, step int64) *fakeSeriesIterator {
362return &fakeSeriesIterator{nsamples: nsamples, step: step, idx: -1}
363}
364
365func (it *fakeSeriesIterator) At() (int64, float64) {
366return it.idx * it.step, 123 // Value doesn't matter.
367}
368
369func (it *fakeSeriesIterator) AtHistogram(*histogram.Histogram) (int64, *histogram.Histogram) {
370return it.idx * it.step, &histogram.Histogram{} // Value doesn't matter.
371}
372
373func (it *fakeSeriesIterator) AtFloatHistogram(*histogram.FloatHistogram) (int64, *histogram.FloatHistogram) {
374return it.idx * it.step, &histogram.FloatHistogram{} // Value doesn't matter.
375}
376
377func (it *fakeSeriesIterator) AtT() int64 {
378return it.idx * it.step
379}
380
381func (it *fakeSeriesIterator) Next() chunkenc.ValueType {
382it.idx++
383if it.idx >= it.nsamples {
384return chunkenc.ValNone
385}
386return chunkenc.ValFloat
387}
388
389func (it *fakeSeriesIterator) Seek(t int64) chunkenc.ValueType {
390it.idx = t / it.step
391if it.idx >= it.nsamples {
392return chunkenc.ValNone
393}
394return chunkenc.ValFloat
395}
396
397func (it *fakeSeriesIterator) Err() error { return nil }
398