From 83846b57386cce5e783b69c7938ac771e12df483 Mon Sep 17 00:00:00 2001 From: xander Date: Wed, 6 Aug 2025 15:28:48 +0100 Subject: [PATCH 01/31] chore: update fsnotify Signed-off-by: xander --- go.mod | 5 +---- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index e993819936..49f5487afd 100644 --- a/go.mod +++ b/go.mod @@ -21,7 +21,7 @@ require ( github.com/envoyproxy/go-control-plane/envoy v1.32.4 github.com/envoyproxy/protoc-gen-validate v1.2.1 github.com/facette/natsort v0.0.0-20181210072756-2cd4dd1e2dcb - github.com/fsnotify/fsnotify v1.8.0 + github.com/fsnotify/fsnotify v1.9.0 github.com/go-openapi/strfmt v0.23.0 github.com/go-zookeeper/zk v1.0.4 github.com/gogo/protobuf v1.3.2 @@ -246,6 +246,3 @@ exclude ( github.com/grpc-ecosystem/grpc-gateway v1.14.7 google.golang.org/api v0.30.0 ) - -// Pin until https://github.com/fsnotify/fsnotify/issues/656 is resolved. -replace github.com/fsnotify/fsnotify v1.8.0 => github.com/fsnotify/fsnotify v1.7.0 diff --git a/go.sum b/go.sum index 14a9b22ff1..d61c5e9416 100644 --- a/go.sum +++ b/go.sum @@ -139,8 +139,8 @@ github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= -github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= +github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= From 8e48f43e0662e54c2c3cfb712502fe43486c353b Mon Sep 17 00:00:00 2001 From: cuiweixie Date: Thu, 21 Aug 2025 13:38:50 +0800 Subject: [PATCH 02/31] discovery: refactor to use reflect.TypeFor Signed-off-by: cuiweixie --- discovery/registry.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/discovery/registry.go b/discovery/registry.go index 92fa3d3d16..ae32cb7c0a 100644 --- a/discovery/registry.go +++ b/discovery/registry.go @@ -42,8 +42,8 @@ var ( configTypesMu sync.Mutex configTypes = make(map[reflect.Type]reflect.Type) - emptyStructType = reflect.TypeOf(struct{}{}) - configsType = reflect.TypeOf(Configs{}) + emptyStructType = reflect.TypeFor[struct{}]() + configsType = reflect.TypeFor[Configs]() ) // RegisterConfig registers the given Config type for YAML marshaling and unmarshaling. @@ -54,7 +54,7 @@ func RegisterConfig(config Config) { func init() { // N.B.: static_configs is the only Config type implemented by default. // All other types are registered at init by their implementing packages. - elemTyp := reflect.TypeOf(&targetgroup.Group{}) + elemTyp := reflect.TypeFor[*targetgroup.Group]() registerConfig(staticConfigsKey, elemTyp, StaticConfig{}) } From bdf547ae9c25fe95f755b53343bcaa6b3d6bbbd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gy=C3=B6rgy=20Krajcsovits?= Date: Sat, 13 Sep 2025 16:25:21 +0200 Subject: [PATCH 03/31] fix(nativehistograms): validation should fail on unsupported schemas MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Histogram.Validate and FloatHistogram.Validate now return error on unsupported schemas. Scrape and remote-write handler reduces the schema to the maximum allowed if it is above the maximum, but below theoretical maximum of 52. For scrape the maximum is a configuration option, for remote-write it is 8. Note: OTLP endpont already does the reduction, without checking that it is below 52 as the spec does not specify a maximum. Signed-off-by: György Krajcsovits --- model/histogram/float_histogram.go | 7 +- model/histogram/generic.go | 13 ++- model/histogram/histogram.go | 7 +- model/histogram/histogram_test.go | 12 +++ scrape/target.go | 4 +- .../prometheusremotewrite/histograms.go | 10 +- storage/remote/write_handler.go | 21 ++-- storage/remote/write_handler_test.go | 97 +++++++++++++++++++ 8 files changed, 150 insertions(+), 21 deletions(-) diff --git a/model/histogram/float_histogram.go b/model/histogram/float_histogram.go index 2b78c6d630..b0e512fbc5 100644 --- a/model/histogram/float_histogram.go +++ b/model/histogram/float_histogram.go @@ -798,7 +798,8 @@ func (h *FloatHistogram) AllReverseBucketIterator() BucketIterator[float64] { // create false positives here. func (h *FloatHistogram) Validate() error { var nCount, pCount float64 - if h.UsesCustomBuckets() { + switch { + case IsCustomBucketsSchema(h.Schema): if err := checkHistogramCustomBounds(h.CustomValues, h.PositiveSpans, len(h.PositiveBuckets)); err != nil { return fmt.Errorf("custom buckets: %w", err) } @@ -814,7 +815,7 @@ func (h *FloatHistogram) Validate() error { if len(h.NegativeBuckets) > 0 { return errors.New("custom buckets: must not have negative buckets") } - } else { + case IsExponentialSchema(h.Schema): if err := checkHistogramSpans(h.PositiveSpans, len(h.PositiveBuckets)); err != nil { return fmt.Errorf("positive side: %w", err) } @@ -828,6 +829,8 @@ func (h *FloatHistogram) Validate() error { if h.CustomValues != nil { return errors.New("histogram with exponential schema must not have custom bounds") } + default: + return fmt.Errorf("schema %d: %w", h.Schema, ErrHistogramsInvalidSchema) } err := checkHistogramBuckets(h.PositiveBuckets, &pCount, false) if err != nil { diff --git a/model/histogram/generic.go b/model/histogram/generic.go index 90a94a5600..4c0940a1f6 100644 --- a/model/histogram/generic.go +++ b/model/histogram/generic.go @@ -21,9 +21,11 @@ import ( ) const ( - ExponentialSchemaMax int32 = 8 - ExponentialSchemaMin int32 = -4 - CustomBucketsSchema int32 = -53 + ExponentialSchemaMax int32 = 8 + ExponentialSchemaMaxReserved int32 = 52 + ExponentialSchemaMin int32 = -4 + ExponentialSchemaMinReserved int32 = -9 + CustomBucketsSchema int32 = -53 ) var ( @@ -37,6 +39,7 @@ var ( ErrHistogramCustomBucketsInfinite = errors.New("histogram custom bounds must be finite") ErrHistogramsIncompatibleSchema = errors.New("cannot apply this operation on histograms with a mix of exponential and custom bucket schemas") ErrHistogramsIncompatibleBounds = errors.New("cannot apply this operation on custom buckets histograms with different custom bounds") + ErrHistogramsInvalidSchema = errors.New("histogram has an invalid schema, which must be between -4 and 8 for exponential buckets, or -53 for custom buckets") ) func IsCustomBucketsSchema(s int32) bool { @@ -47,6 +50,10 @@ func IsExponentialSchema(s int32) bool { return s >= ExponentialSchemaMin && s <= ExponentialSchemaMax } +func IsExponentialSchemaReserved(s int32) bool { + return s >= ExponentialSchemaMinReserved && s <= ExponentialSchemaMaxReserved +} + // BucketCount is a type constraint for the count in a bucket, which can be // float64 (for type FloatHistogram) or uint64 (for type Histogram). type BucketCount interface { diff --git a/model/histogram/histogram.go b/model/histogram/histogram.go index cfb63e6341..169be9a6ac 100644 --- a/model/histogram/histogram.go +++ b/model/histogram/histogram.go @@ -425,7 +425,8 @@ func resize[T any](items []T, n int) []T { // the total h.Count). func (h *Histogram) Validate() error { var nCount, pCount uint64 - if h.UsesCustomBuckets() { + switch { + case IsCustomBucketsSchema(h.Schema): if err := checkHistogramCustomBounds(h.CustomValues, h.PositiveSpans, len(h.PositiveBuckets)); err != nil { return fmt.Errorf("custom buckets: %w", err) } @@ -441,7 +442,7 @@ func (h *Histogram) Validate() error { if len(h.NegativeBuckets) > 0 { return errors.New("custom buckets: must not have negative buckets") } - } else { + case IsExponentialSchema(h.Schema): if err := checkHistogramSpans(h.PositiveSpans, len(h.PositiveBuckets)); err != nil { return fmt.Errorf("positive side: %w", err) } @@ -455,6 +456,8 @@ func (h *Histogram) Validate() error { if h.CustomValues != nil { return errors.New("histogram with exponential schema must not have custom bounds") } + default: + return fmt.Errorf("schema %d: %w", h.Schema, ErrHistogramsInvalidSchema) } err := checkHistogramBuckets(h.PositiveBuckets, &pCount, true) if err != nil { diff --git a/model/histogram/histogram_test.go b/model/histogram/histogram_test.go index edc8663c94..35603bc01c 100644 --- a/model/histogram/histogram_test.go +++ b/model/histogram/histogram_test.go @@ -1565,6 +1565,18 @@ func TestHistogramValidation(t *testing.T) { CustomValues: []float64{1, 2, 3, 4, 5, 6, 7, 8}, }, }, + "schema too high": { + h: &Histogram{ + Schema: 10, + }, + errMsg: `schema 10: histogram has an invalid schema, which must be between -4 and 8 for exponential buckets, or -53 for custom buckets`, + }, + "schema too low": { + h: &Histogram{ + Schema: -10, + }, + errMsg: `schema -10: histogram has an invalid schema, which must be between -4 and 8 for exponential buckets, or -53 for custom buckets`, + }, } for testName, tc := range tests { diff --git a/scrape/target.go b/scrape/target.go index 73fed40498..0af2b8ba14 100644 --- a/scrape/target.go +++ b/scrape/target.go @@ -414,12 +414,12 @@ type maxSchemaAppender struct { func (app *maxSchemaAppender) AppendHistogram(ref storage.SeriesRef, lset labels.Labels, t int64, h *histogram.Histogram, fh *histogram.FloatHistogram) (storage.SeriesRef, error) { if h != nil { - if histogram.IsExponentialSchema(h.Schema) && h.Schema > app.maxSchema { + if histogram.IsExponentialSchemaReserved(h.Schema) && h.Schema > app.maxSchema { h = h.ReduceResolution(app.maxSchema) } } if fh != nil { - if histogram.IsExponentialSchema(fh.Schema) && fh.Schema > app.maxSchema { + if histogram.IsExponentialSchemaReserved(fh.Schema) && fh.Schema > app.maxSchema { fh = fh.ReduceResolution(app.maxSchema) } } diff --git a/storage/remote/otlptranslator/prometheusremotewrite/histograms.go b/storage/remote/otlptranslator/prometheusremotewrite/histograms.go index a694d2067a..0bc8a876e4 100644 --- a/storage/remote/otlptranslator/prometheusremotewrite/histograms.go +++ b/storage/remote/otlptranslator/prometheusremotewrite/histograms.go @@ -86,16 +86,16 @@ func (c *PrometheusConverter) addExponentialHistogramDataPoints(ctx context.Cont func exponentialToNativeHistogram(p pmetric.ExponentialHistogramDataPoint, temporality pmetric.AggregationTemporality) (*histogram.Histogram, annotations.Annotations, error) { var annots annotations.Annotations scale := p.Scale() - if scale < -4 { + if scale < histogram.ExponentialSchemaMin { return nil, annots, fmt.Errorf("cannot convert exponential to native histogram."+ - " Scale must be >= -4, was %d", scale) + " Scale must be >= %d, was %d", histogram.ExponentialSchemaMin, scale) } var scaleDown int32 - if scale > 8 { - scaleDown = scale - 8 - scale = 8 + if scale > histogram.ExponentialSchemaMax { + scaleDown = scale - histogram.ExponentialSchemaMax + scale = histogram.ExponentialSchemaMax } pSpans, pDeltas := convertBucketsLayout(p.Positive().BucketCounts().AsRaw(), p.Positive().Offset(), scaleDown, true) diff --git a/storage/remote/write_handler.go b/storage/remote/write_handler.go index 7681655e61..ad0a2a13e0 100644 --- a/storage/remote/write_handler.go +++ b/storage/remote/write_handler.go @@ -229,7 +229,7 @@ func (h *writeHandler) write(ctx context.Context, req *prompb.WriteRequest) (err samplesWithInvalidLabels := 0 samplesAppended := 0 - app := &timeLimitAppender{ + app := &remoteWriteAppender{ Appender: h.appendable.Appender(ctx), maxTime: timestamp.FromTime(time.Now().Add(maxAheadTime)), } @@ -344,7 +344,7 @@ func (h *writeHandler) appendV1Histograms(app storage.Appender, hh []prompb.Hist // NOTE(bwplotka): TSDB storage is NOT idempotent, so we don't allow "partial retry-able" errors. // Once we have 5xx type of error, we immediately stop and rollback all appends. func (h *writeHandler) writeV2(ctx context.Context, req *writev2.Request) (_ WriteResponseStats, errHTTPCode int, _ error) { - app := &timeLimitAppender{ + app := &remoteWriteAppender{ Appender: h.appendable.Appender(ctx), maxTime: timestamp.FromTime(time.Now().Add(maxAheadTime)), } @@ -616,7 +616,7 @@ type rwExporter struct { func (rw *rwExporter) ConsumeMetrics(ctx context.Context, md pmetric.Metrics) error { otlpCfg := rw.config().OTLPConfig - app := &timeLimitAppender{ + app := &remoteWriteAppender{ Appender: rw.appendable.Appender(ctx), maxTime: timestamp.FromTime(time.Now().Add(maxAheadTime)), } @@ -719,13 +719,13 @@ func hasDelta(md pmetric.Metrics) bool { return false } -type timeLimitAppender struct { +type remoteWriteAppender struct { storage.Appender maxTime int64 } -func (app *timeLimitAppender) Append(ref storage.SeriesRef, lset labels.Labels, t int64, v float64) (storage.SeriesRef, error) { +func (app *remoteWriteAppender) Append(ref storage.SeriesRef, lset labels.Labels, t int64, v float64) (storage.SeriesRef, error) { if t > app.maxTime { return 0, fmt.Errorf("%w: timestamp is too far in the future", storage.ErrOutOfBounds) } @@ -737,11 +737,18 @@ func (app *timeLimitAppender) Append(ref storage.SeriesRef, lset labels.Labels, return ref, nil } -func (app *timeLimitAppender) AppendHistogram(ref storage.SeriesRef, l labels.Labels, t int64, h *histogram.Histogram, fh *histogram.FloatHistogram) (storage.SeriesRef, error) { +func (app *remoteWriteAppender) AppendHistogram(ref storage.SeriesRef, l labels.Labels, t int64, h *histogram.Histogram, fh *histogram.FloatHistogram) (storage.SeriesRef, error) { if t > app.maxTime { return 0, fmt.Errorf("%w: timestamp is too far in the future", storage.ErrOutOfBounds) } + if h != nil && histogram.IsExponentialSchemaReserved(h.Schema) && h.Schema > histogram.ExponentialSchemaMax { + h = h.ReduceResolution(histogram.ExponentialSchemaMax) + } + if fh != nil && histogram.IsExponentialSchemaReserved(fh.Schema) && fh.Schema > histogram.ExponentialSchemaMax { + fh = fh.ReduceResolution(histogram.ExponentialSchemaMax) + } + ref, err := app.Appender.AppendHistogram(ref, l, t, h, fh) if err != nil { return 0, err @@ -749,7 +756,7 @@ func (app *timeLimitAppender) AppendHistogram(ref storage.SeriesRef, l labels.La return ref, nil } -func (app *timeLimitAppender) AppendExemplar(ref storage.SeriesRef, l labels.Labels, e exemplar.Exemplar) (storage.SeriesRef, error) { +func (app *remoteWriteAppender) AppendExemplar(ref storage.SeriesRef, l labels.Labels, e exemplar.Exemplar) (storage.SeriesRef, error) { if e.Ts > app.maxTime { return 0, fmt.Errorf("%w: timestamp is too far in the future", storage.ErrOutOfBounds) } diff --git a/storage/remote/write_handler_test.go b/storage/remote/write_handler_test.go index 5631e80732..78cbcdccf7 100644 --- a/storage/remote/write_handler_test.go +++ b/storage/remote/write_handler_test.go @@ -1134,3 +1134,100 @@ func (m *mockAppendable) AppendCTZeroSample(_ storage.SeriesRef, l labels.Labels m.samples = append(m.samples, mockSample{l, ct, 0}) return storage.SeriesRef(hash), nil } + +var ( + highSchemaHistogram = &histogram.Histogram{ + Schema: 10, + PositiveSpans: []histogram.Span{ + { + Offset: -1, + Length: 2, + }, + }, + PositiveBuckets: []int64{1, 2}, + NegativeSpans: []histogram.Span{ + { + Offset: 0, + Length: 1, + }, + }, + NegativeBuckets: []int64{1}, + } + reducedSchemaHistogram = &histogram.Histogram{ + Schema: 8, + PositiveSpans: []histogram.Span{ + { + Offset: 0, + Length: 1, + }, + }, + PositiveBuckets: []int64{4}, + NegativeSpans: []histogram.Span{ + { + Offset: 0, + Length: 1, + }, + }, + NegativeBuckets: []int64{1}, + } +) + +func TestHistogramsReduction(t *testing.T) { + for _, protoMsg := range []config.RemoteWriteProtoMsg{config.RemoteWriteProtoMsgV1, config.RemoteWriteProtoMsgV2} { + t.Run(string(protoMsg), func(t *testing.T) { + appendable := &mockAppendable{} + handler := NewWriteHandler(promslog.NewNopLogger(), nil, appendable, []config.RemoteWriteProtoMsg{protoMsg}, false) + + var ( + err error + payload []byte + ) + + if protoMsg == config.RemoteWriteProtoMsgV1 { + payload, _, _, err = buildWriteRequest(nil, []prompb.TimeSeries{ + { + Labels: []prompb.Label{{Name: "__name__", Value: "test_metric1"}}, + Histograms: []prompb.Histogram{prompb.FromIntHistogram(1, highSchemaHistogram)}, + }, + { + Labels: []prompb.Label{{Name: "__name__", Value: "test_metric2"}}, + Histograms: []prompb.Histogram{prompb.FromFloatHistogram(2, highSchemaHistogram.ToFloat(nil))}, + }, + }, nil, nil, nil, nil, "snappy") + } else { + payload, _, _, err = buildV2WriteRequest(promslog.NewNopLogger(), []writev2.TimeSeries{ + { + LabelsRefs: []uint32{0, 1}, + Histograms: []writev2.Histogram{writev2.FromIntHistogram(1, highSchemaHistogram)}, + }, + { + LabelsRefs: []uint32{0, 2}, + Histograms: []writev2.Histogram{writev2.FromFloatHistogram(2, highSchemaHistogram.ToFloat(nil))}, + }, + }, []string{"__name__", "test_metric1", "test_metric2"}, + nil, nil, nil, "snappy") + } + require.NoError(t, err) + + req, err := http.NewRequest("", "", bytes.NewReader(payload)) + require.NoError(t, err) + + req.Header.Set("Content-Type", remoteWriteContentTypeHeaders[protoMsg]) + + recorder := httptest.NewRecorder() + handler.ServeHTTP(recorder, req) + + resp := recorder.Result() + body, err := io.ReadAll(resp.Body) + require.NoError(t, err) + require.Equal(t, http.StatusNoContent, resp.StatusCode) + require.Empty(t, body) + + require.Len(t, appendable.histograms, 2) + require.Equal(t, int64(1), appendable.histograms[0].t) + require.Equal(t, reducedSchemaHistogram, appendable.histograms[0].h) + require.Equal(t, int64(2), appendable.histograms[1].t) + require.Equal(t, reducedSchemaHistogram.ToFloat(nil), appendable.histograms[1].fh) + }) + } +} From a14faab4351025b486fb5dd80362c3653cc319ff Mon Sep 17 00:00:00 2001 From: dancer1325 Date: Mon, 15 Sep 2025 17:25:12 +0200 Subject: [PATCH 04/31] docs(): fix gettingStarted outdated graph reference /graph does NOT exist anymore in the new React app. It has been refactored within /query Signed-off-by: dancer1325 --- docs/getting_started.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/getting_started.md b/docs/getting_started.md index aeba295da8..35f1a88a7d 100644 --- a/docs/getting_started.md +++ b/docs/getting_started.md @@ -79,7 +79,7 @@ navigating to its metrics endpoint: Let us explore data that Prometheus has collected about itself. To use Prometheus's built-in expression browser, navigate to -http://localhost:9090/graph and choose the "Table" view within the "Graph" tab. +http://localhost:9090/query and choose the "Graph" tab. As you can gather from [localhost:9090/metrics](http://localhost:9090/metrics), one metric that Prometheus exports about itself is named @@ -113,7 +113,7 @@ For more about the expression language, see the ## Using the graphing interface -To graph expressions, navigate to http://localhost:9090/graph and use the "Graph" +To graph expressions, navigate to http://localhost:9090/query and use the "Graph" tab. For example, enter the following expression to graph the per-second rate of chunks From aa922ce3b66af7065a4cef9bece23e6bce7e1357 Mon Sep 17 00:00:00 2001 From: Andrew Hall Date: Tue, 16 Sep 2025 19:28:19 +0800 Subject: [PATCH 05/31] Added support for string literals and range results for instant queries in test scripting framework (#17055) Signed-off-by: Andrew Hall Co-authored-by: Charles Korn Co-authored-by: Arve Knudsen --- promql/engine_test.go | 83 --------- promql/promqltest/README.md | 36 ++++ promql/promqltest/test.go | 157 +++++++++++++++--- promql/promqltest/test_test.go | 138 +++++++++++++++ promql/promqltest/testdata/literals.test | 15 ++ promql/promqltest/testdata/range_queries.test | 34 ++++ 6 files changed, 355 insertions(+), 108 deletions(-) diff --git a/promql/engine_test.go b/promql/engine_test.go index f70036e3c0..e2aadd7a5d 100644 --- a/promql/engine_test.go +++ b/promql/engine_test.go @@ -3195,89 +3195,6 @@ func TestEngine_Close(t *testing.T) { }) } -func TestInstantQueryWithRangeVectorSelector(t *testing.T) { - engine := newTestEngine(t) - - baseT := timestamp.Time(0) - storage := promqltest.LoadedStorage(t, ` - load 1m - some_metric{env="1"} 0+1x4 - some_metric{env="2"} 0+2x4 - some_metric{env="3"} {{count:0}}+{{count:1}}x4 - some_metric_with_stale_marker 0 1 stale 3 - `) - t.Cleanup(func() { require.NoError(t, storage.Close()) }) - - testCases := map[string]struct { - expr string - expected promql.Matrix - ts time.Time - }{ - "matches series with points in range": { - expr: "some_metric[2m]", - ts: baseT.Add(2 * time.Minute), - expected: promql.Matrix{ - { - Metric: labels.FromStrings("__name__", "some_metric", "env", "1"), - Floats: []promql.FPoint{ - {T: timestamp.FromTime(baseT.Add(time.Minute)), F: 1}, - {T: timestamp.FromTime(baseT.Add(2 * time.Minute)), F: 2}, - }, - }, - { - Metric: labels.FromStrings("__name__", "some_metric", "env", "2"), - Floats: []promql.FPoint{ - {T: timestamp.FromTime(baseT.Add(time.Minute)), F: 2}, - {T: timestamp.FromTime(baseT.Add(2 * time.Minute)), F: 4}, - }, - }, - { - Metric: labels.FromStrings("__name__", "some_metric", "env", "3"), - Histograms: []promql.HPoint{ - {T: timestamp.FromTime(baseT.Add(time.Minute)), H: &histogram.FloatHistogram{Count: 1, CounterResetHint: histogram.NotCounterReset}}, - {T: timestamp.FromTime(baseT.Add(2 * time.Minute)), H: &histogram.FloatHistogram{Count: 2, CounterResetHint: histogram.NotCounterReset}}, - }, - }, - }, - }, - "matches no series": { - expr: "some_nonexistent_metric[1m]", - ts: baseT, - expected: promql.Matrix{}, - }, - "no samples in range": { - expr: "some_metric[1m]", - ts: baseT.Add(20 * time.Minute), - expected: promql.Matrix{}, - }, - "metric with stale marker": { - expr: "some_metric_with_stale_marker[3m]", - ts: baseT.Add(3 * time.Minute), - expected: promql.Matrix{ - { - Metric: labels.FromStrings("__name__", "some_metric_with_stale_marker"), - Floats: []promql.FPoint{ - {T: timestamp.FromTime(baseT.Add(time.Minute)), F: 1}, - {T: timestamp.FromTime(baseT.Add(3 * time.Minute)), F: 3}, - }, - }, - }, - }, - } - - for name, testCase := range testCases { - t.Run(name, func(t *testing.T) { - q, err := engine.NewInstantQuery(context.Background(), storage, nil, testCase.expr, testCase.ts) - require.NoError(t, err) - defer q.Close() - - res := q.Exec(context.Background()) - require.NoError(t, res.Err) - testutil.RequireEqual(t, testCase.expected, res.Value) - }) - } -} - func TestQueryLookbackDelta(t *testing.T) { var ( load = `load 5m diff --git a/promql/promqltest/README.md b/promql/promqltest/README.md index 84a0e69f3a..d26c01c6f1 100644 --- a/promql/promqltest/README.md +++ b/promql/promqltest/README.md @@ -106,8 +106,44 @@ eval range from to step * `` and `` specify the time range of the range query, and use the same syntax as `