// 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 }