mirror of
https://github.com/prometheus/prometheus.git
synced 2026-06-09 08:32:26 -04:00
Test that initializing a chunk appender from an existing chunk works correctly. Signed-off-by: György Krajcsovits <gyorgy.krajcsovits@grafana.com>
156 lines
4.6 KiB
Go
156 lines
4.6 KiB
Go
// Copyright The Prometheus Authors
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package chunkenc
|
|
|
|
import (
|
|
"fmt"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/prometheus/prometheus/model/histogram"
|
|
)
|
|
|
|
// testChunkSTHandling tests handling of start times in chunks.
|
|
// It uses 0-4 samples with timestamp 1000,2000,3000,4000 and monotonically
|
|
// increasing start times that are chosen from 0-(ts-500) for each sample.
|
|
// All combinations of start times are tested for each number of samples.
|
|
func testChunkSTHandling(t *testing.T, vt ValueType, chunkFactory func() Chunk) {
|
|
sampleAppend := func(app Appender, vt ValueType, st, ts int64, v float64) {
|
|
switch vt {
|
|
case ValFloat:
|
|
app.Append(st, ts, v)
|
|
case ValHistogram:
|
|
_, recoded, _, err := app.AppendHistogram(nil, st, ts, &histogram.Histogram{Sum: v, Count: uint64(v * 10)}, false)
|
|
require.NoError(t, err)
|
|
require.False(t, recoded)
|
|
case ValFloatHistogram:
|
|
_, recoded, _, err := app.AppendFloatHistogram(nil, st, ts, &histogram.FloatHistogram{Sum: v, Count: v * 10}, false)
|
|
require.NoError(t, err)
|
|
require.False(t, recoded)
|
|
default:
|
|
t.Fatalf("unsupported value type %v", vt)
|
|
}
|
|
}
|
|
|
|
get := func(it Iterator, vt ValueType) (int64, int64, float64) {
|
|
switch vt {
|
|
case ValFloat:
|
|
ts, v := it.At()
|
|
return it.AtST(), ts, v
|
|
case ValHistogram:
|
|
ts, h := it.AtHistogram(nil)
|
|
return it.AtST(), ts, float64(h.Sum)
|
|
case ValFloatHistogram:
|
|
ts, fh := it.AtFloatHistogram(nil)
|
|
return it.AtST(), ts, fh.Sum
|
|
default:
|
|
t.Fatalf("unsupported value type %v", vt)
|
|
return 0, 0, 0
|
|
}
|
|
}
|
|
|
|
runCase := func(t *testing.T, samples []triple) {
|
|
chunk := chunkFactory()
|
|
app, err := chunk.Appender()
|
|
require.NoError(t, err)
|
|
var clone []byte
|
|
for i, s := range samples {
|
|
if i == len(samples)-1 {
|
|
clone = append(clone, chunk.Bytes()...)
|
|
}
|
|
sampleAppend(app, vt, s.st, s.t, s.v)
|
|
}
|
|
chunksToTest := []Chunk{chunk}
|
|
|
|
if len(samples) > 0 {
|
|
// If there are samples, also test that appending to a chunk cloned from the original chunk works correctly.
|
|
// This tests resuming the appender from a previous chunk.
|
|
cloneChunk := chunkFactory()
|
|
cloneChunk.Reset(clone)
|
|
cloneApp, err := cloneChunk.Appender()
|
|
require.NoError(t, err)
|
|
sampleAppend(cloneApp, vt, samples[len(samples)-1].st, samples[len(samples)-1].t, samples[len(samples)-1].v)
|
|
chunksToTest = append(chunksToTest, cloneChunk)
|
|
}
|
|
|
|
printChunkName := func(i int) string {
|
|
if i == 0 {
|
|
return "original"
|
|
}
|
|
return "cloned"
|
|
}
|
|
|
|
for ci, chk := range chunksToTest {
|
|
require.Equal(t, len(samples), chk.NumSamples(), "%s chunk: number of samples mismatch", printChunkName(ci))
|
|
it := chk.Iterator(nil)
|
|
for i, s := range samples {
|
|
require.Equal(t, vt, it.Next(), "%s[%d]: value type mismatch", printChunkName(ci), i)
|
|
st, ts, f := get(it, vt)
|
|
require.Equal(t, s.t, ts, "%s[%d]: timestamp mismatch", printChunkName(ci), i)
|
|
require.Equal(t, s.st, st, "%s[%d]: start time mismatch", printChunkName(ci), i)
|
|
require.InDelta(t, s.v, f, 1e-9, "%s[%d]: value mismatch", printChunkName(ci), i)
|
|
}
|
|
require.Equal(t, ValNone, it.Next())
|
|
require.NoError(t, it.Err())
|
|
}
|
|
}
|
|
|
|
t.Run("manual for debugging", func(t *testing.T) {
|
|
samples := []triple{
|
|
{st: 0, t: 1000, v: 1.5},
|
|
{st: 0, t: 2000, v: 2.5},
|
|
{st: 0, t: 3000, v: 3.5},
|
|
{st: 0, t: 4000, v: 4.5},
|
|
}
|
|
runCase(t, samples)
|
|
})
|
|
|
|
stTimes := []int64{0, 500, 1000, 1500, 2000, 2500, 3000, 3500, 4000}
|
|
|
|
ts := func(j int) int64 {
|
|
return int64(1000 * (j + 1))
|
|
}
|
|
|
|
for numberOfSamples := range 5 {
|
|
samples := make([]triple, numberOfSamples)
|
|
sampleSTidx := make([]int, numberOfSamples)
|
|
for {
|
|
for j := range numberOfSamples {
|
|
samples[j] = triple{
|
|
st: stTimes[sampleSTidx[j]],
|
|
t: ts(j),
|
|
v: float64(j) + 0.5,
|
|
}
|
|
}
|
|
|
|
t.Run(fmt.Sprintf("%v", samples), func(t *testing.T) {
|
|
runCase(t, samples)
|
|
})
|
|
|
|
exhausted := true
|
|
for j := numberOfSamples - 1; j >= 0; j-- {
|
|
if stTimes[sampleSTidx[j]] < ts(j) {
|
|
sampleSTidx[j]++
|
|
exhausted = false
|
|
break
|
|
}
|
|
sampleSTidx[j] = 0
|
|
}
|
|
if exhausted {
|
|
break
|
|
}
|
|
}
|
|
}
|
|
}
|