prometheus/util/testwal/records.go
bwplotka 3cf43337dc post merge conflict fixes
Signed-off-by: bwplotka <bwplotka@gmail.com>
2026-03-12 09:03:08 +00:00

162 lines
5.3 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 testwal
import (
"fmt"
"github.com/prometheus/common/model"
"github.com/prometheus/prometheus/model/histogram"
"github.com/prometheus/prometheus/model/labels"
"github.com/prometheus/prometheus/tsdb/chunks"
"github.com/prometheus/prometheus/tsdb/record"
)
// RecordsCase represents record generation option in a form of a test case.
//
// Generated Series will have refs that monotonic and deterministic, in range of [RefPadding, RefPadding+Series).
type RecordsCase struct {
Name string
Series int
SamplesPerSeries int
HistogramsPerSeries int
FloatHistogramsPerSeries int
ExemplarsPerSeries int
ExtraLabels []labels.Label
// RefPadding represents a padding to add to Series refs.
RefPadding int
// LabelsFn allows injecting custom labels, by default it's a test_metric_%d with ExtraLabels.
LabelsFn func(lb *labels.ScratchBuilder, ref int) labels.Labels
// TsFn allows injecting custom sample timestamps. j represents the sample index within the series.
// By default, it injects j.
TsFn func(ref, j int) int64
// HistogramFn source histogram for histogram and float histogram records.
// By default, newTestHist is used (exponential bucketing)
HistogramFn func(ref int) *histogram.Histogram
// NoST controls if ref samples should skip generating Start Timestamps. If true, ST is 0.
NoST bool
}
// Records represents batches of generated WAL records.
type Records struct {
Series []record.RefSeries
Samples []record.RefSample
Histograms []record.RefHistogramSample
FloatHistograms []record.RefFloatHistogramSample
Exemplars []record.RefExemplar
Metadata []record.RefMetadata
}
func newTestHist(i int) *histogram.Histogram {
return &histogram.Histogram{
Schema: 2,
ZeroThreshold: 1e-128,
ZeroCount: 0,
Count: 2,
Sum: 0,
PositiveSpans: []histogram.Span{{Offset: 0, Length: 1}},
PositiveBuckets: []int64{int64(i) + 1},
NegativeSpans: []histogram.Span{{Offset: 0, Length: 1}},
NegativeBuckets: []int64{int64(-i) - 1},
}
}
// GenerateRecords generates batches of WAL records for a given RecordsCase.
// Batches represents set of series with the given number of counter samples, histograms, etc. per each series ref.
func GenerateRecords(c RecordsCase) (ret Records) {
ret.Series = make([]record.RefSeries, c.Series)
ret.Metadata = make([]record.RefMetadata, c.Series)
ret.Samples = make([]record.RefSample, c.Series*c.SamplesPerSeries)
ret.Histograms = make([]record.RefHistogramSample, c.Series*c.HistogramsPerSeries)
ret.FloatHistograms = make([]record.RefFloatHistogramSample, c.Series*c.FloatHistogramsPerSeries)
ret.Exemplars = make([]record.RefExemplar, c.Series*c.ExemplarsPerSeries)
if c.LabelsFn == nil {
c.LabelsFn = func(lb *labels.ScratchBuilder, i int) labels.Labels {
// Create series with labels that contains name of series plus any extra labels supplied.
name := fmt.Sprintf("test_metric_%d", i)
lb.Reset()
lb.Add(model.MetricNameLabel, name)
for _, l := range c.ExtraLabels {
lb.Add(l.Name, l.Value)
}
lb.Sort()
return lb.Labels()
}
}
if c.TsFn == nil {
c.TsFn = func(_, j int) int64 { return int64(j) }
}
if c.HistogramFn == nil {
c.HistogramFn = newTestHist
}
lb := labels.NewScratchBuilder(1 + len(c.ExtraLabels))
for i := range ret.Series {
ref := c.RefPadding + i
ret.Series[i] = record.RefSeries{
Ref: chunks.HeadSeriesRef(ref),
Labels: c.LabelsFn(&lb, ref),
}
ret.Metadata[i] = record.RefMetadata{
Ref: chunks.HeadSeriesRef(ref),
Type: uint8(record.Counter),
Unit: "unit text",
Help: fmt.Sprintf("help text for %d", ref),
}
for j := range c.SamplesPerSeries {
ts := c.TsFn(ref, j)
// Keep ST simple for now; we don't test the exact semantics.
// We can improve later (e.g. STsFN).
sts := ts - 1
if c.NoST {
sts = 0
}
ret.Samples[i*c.SamplesPerSeries+j] = record.RefSample{
Ref: chunks.HeadSeriesRef(ref),
ST: sts, T: ts,
V: float64(ref),
}
}
h := c.HistogramFn(ref)
for j := range c.HistogramsPerSeries {
ret.Histograms[i*c.HistogramsPerSeries+j] = record.RefHistogramSample{
Ref: chunks.HeadSeriesRef(ref),
T: c.TsFn(ref, j),
H: h,
}
}
for j := range c.FloatHistogramsPerSeries {
ret.FloatHistograms[i*c.FloatHistogramsPerSeries+j] = record.RefFloatHistogramSample{
Ref: chunks.HeadSeriesRef(ref),
T: c.TsFn(ref, j),
FH: h.ToFloat(nil),
}
}
for j := range c.ExemplarsPerSeries {
ret.Exemplars[i*c.ExemplarsPerSeries+j] = record.RefExemplar{
Ref: chunks.HeadSeriesRef(ref),
T: c.TsFn(ref, j),
V: float64(ref),
Labels: labels.FromStrings("trace_id", fmt.Sprintf("trace-%d", ref)),
}
}
}
return ret
}