diff --git a/storage/buffer.go b/storage/buffer.go index a692570ed..d2d89e042 100644 --- a/storage/buffer.go +++ b/storage/buffer.go @@ -284,7 +284,8 @@ func newSampleRing(delta int64, size int, typ chunkenc.ValueType) *sampleRing { case chunkenc.ValFloatHistogram: r.fhBuf = make([]fhSample, size) default: - r.iBuf = make([]chunks.Sample, size) + // Do not initialize anything because the 1st sample will be + // added to one of the other bufs anyway. } return r } @@ -294,6 +295,12 @@ func (r *sampleRing) reset() { r.i = -1 r.f = 0 r.bufInUse = noBuf + + // The first sample after the reset will always go to a specialized + // buffer. If we later need to change to the interface buffer, we'll + // copy from the specialized buffer to the interface buffer. For that to + // work properly, we have to reset the interface buffer here, too. + r.iBuf = r.iBuf[:0] } // Returns the current iterator. Invalidates previously returned iterators. @@ -441,6 +448,7 @@ func (r *sampleRing) add(s chunks.Sample) { } // The new sample isn't a fit for the already existing // ones. Copy the latter into the interface buffer where needed. + // The interface buffer is assumed to be of length zero at this point. switch r.bufInUse { case fBuf: for _, s := range r.fBuf { diff --git a/storage/buffer_test.go b/storage/buffer_test.go index bc79a79ba..77981545b 100644 --- a/storage/buffer_test.go +++ b/storage/buffer_test.go @@ -90,6 +90,54 @@ func TestSampleRing(t *testing.T) { } } +func TestSampleRingMixed(t *testing.T) { + h1 := tsdbutil.GenerateTestHistogram(1) + h2 := tsdbutil.GenerateTestHistogram(2) + + // With ValNone as the preferred type, nothing should be initialized. + r := newSampleRing(10, 2, chunkenc.ValNone) + require.Zero(t, len(r.fBuf)) + require.Zero(t, len(r.hBuf)) + require.Zero(t, len(r.fhBuf)) + require.Zero(t, len(r.iBuf)) + + // But then mixed adds should work as expected. + r.addF(fSample{t: 1, f: 3.14}) + r.addH(hSample{t: 2, h: h1}) + + it := r.iterator() + + require.Equal(t, chunkenc.ValFloat, it.Next()) + ts, f := it.At() + require.Equal(t, int64(1), ts) + require.Equal(t, 3.14, f) + require.Equal(t, chunkenc.ValHistogram, it.Next()) + var h *histogram.Histogram + ts, h = it.AtHistogram() + require.Equal(t, int64(2), ts) + require.Equal(t, h1, h) + require.Equal(t, chunkenc.ValNone, it.Next()) + + r.reset() + it = r.iterator() + require.Equal(t, chunkenc.ValNone, it.Next()) + + r.addF(fSample{t: 3, f: 4.2}) + r.addH(hSample{t: 4, h: h2}) + + it = r.iterator() + + require.Equal(t, chunkenc.ValFloat, it.Next()) + ts, f = it.At() + require.Equal(t, int64(3), ts) + require.Equal(t, 4.2, f) + require.Equal(t, chunkenc.ValHistogram, it.Next()) + ts, h = it.AtHistogram() + require.Equal(t, int64(4), ts) + require.Equal(t, h2, h) + require.Equal(t, chunkenc.ValNone, it.Next()) +} + func TestBufferedSeriesIterator(t *testing.T) { var it *BufferedSeriesIterator