mirror of
https://github.com/prometheus/prometheus.git
synced 2026-10-28 10:44:59 +08:00
Merge branch 'release-3.15'
Signed-off-by: Owen Williams <owen.williams@grafana.com>
This commit is contained in:
12 files changed
+723
-10
No files matched your search
@@ -6,6 +6,94 @@
|
||||
- [ENHANCEMENT] promtool: Add `--allow-incompatible-block-duration` so users can specify any max-block-duration instead of being restricted to the default Prometheus block durations.
|
||||
- [ENHANCEMENT] Remote write / Alertmanager: Upgrade `github.com/prometheus/sigv4` to v0.5.0, adding `session_name` and `tags` fields to the `sigv4` configuration block for STS AssumeRole sessions. The existing but previously undocumented `service_name` field is also documented.
|
||||
|
||||
## 3.15.1 / 2026-10-21
|
||||
|
||||
- [SECURITY] Container images: Update CA certificates to distrust "emSign ECC Root CA - G3" and "Comodo AAA Services root" following the compromise of eMudhra intermediate CA private keys.
|
||||
- [SECURITY] Build with Go 1.27.3 to fix vulnerabilities in `net/http`, `net/textproto`, `crypto/tls` and `html/template`.
|
||||
- [SECURITY] Bump golang.org/x/net to v0.60.0 to fix HTTP/2 vulnerabilities (GO-2026-6603, GO-2026-6610, GO-2026-6611, GO-2026-6612, GO-2026-6617).
|
||||
|
||||
## 3.15.0 / 2026-09-24
|
||||
- [CHANGE] PromQL: A range query whose `end` was not aligned to `step` caused subqueries inside it to evaluate past the parent's last actual step, inflating `peakSamples` in the query stats and against the `query.max-samples` limit, and wasting storage I/O reading samples that were never used in the result. Add tests to prevent regression of the fix made in #18081. #18598
|
||||
- [CHANGE] PromQL: Do not register a start timestamp reset if the start timestamp hasn't changed between subsequent samples. #19454
|
||||
- [CHANGE] Logging: Deprecate `--log.level`; use `runtime.log_level` configuration to supply the default level. #19511
|
||||
- [FEATURE] Configuration: Allow changing the process log level through `runtime.log_level` on configuration reload. #19511
|
||||
- [FEATURE] Prometheus: Add `--auto-gomemlimit.refresh-interval` flag to periodically re-detect the container or system memory limit and update `GOMEMLIMIT` at runtime. #18843
|
||||
- [FEATURE] Scraping: Add support for scraping targets via Unix Domain Sockets. #12024. #18091
|
||||
- [FEATURE] scrape: Implement OM2.0 scrape format. #18606
|
||||
- [ENHANCEMENT] Reduce TSDB head CPU utilization when initializing. #18001
|
||||
- [ENHANCEMENT] Docker SD: Add labels `__meta_docker_container_image` and `__meta_docker_container_image_id`. #19386
|
||||
- [ENHANCEMENT] Mixin: Add a p95/p99 remote-write send-batch latency panel to the remote-write dashboard. #19500
|
||||
- [ENHANCEMENT] Mixin: Support native histograms in the remote-write send-batch latency panel. #19522
|
||||
- [ENHANCEMENT] PromQL/TSDB: The `--enable-feature=st-storage` flag now automatically enables XOR2 float chunk encoding and ST-capable histogram chunk encoding, so you no longer need to pass `xor2-encoding` and `histograms-st-encoding` alongside it. #19518
|
||||
- [ENHANCEMENT] Remote write / Alertmanager: upgrade sigv4 to v0.5.0, adding `session_name` and `tags` fields for STS AssumeRole sessions. The previously undocumented `service_name` field is now also documented. #19569
|
||||
- [ENHANCEMENT] Scraping: Support zstd-compressed scrape responses, enabled via feature flag `zstd-scrape`. #19502
|
||||
- [ENHANCEMENT] TSDB: Stabilize the XOR2 float chunk encoding. `--enable-feature=xor2-encoding` is deprecated; use `storage.tsdb.chunk_encoding.floats: xor2` instead. Check that other software reading the TSDB directly (e.g. Thanos sidecar) supports XOR2 before enabling. #19461
|
||||
- [ENHANCEMENT] TSDB: add `prometheus_tsdb_head_appenders_created_total` metric. #19411
|
||||
- [ENHANCEMENT] Tracing: add more spans to scrapes, API queries and rule evaluations. #19410
|
||||
- [ENHANCEMENT] UI: Show the effective configuration for each scrape pool on the Targets and Service Discovery pages. #19384
|
||||
- [ENHANCEMENT] scrape: Enable start time synthesis for summary `_count` and `_sum` series in scrape appender v2. #19323
|
||||
- [ENHANCEMENT] scrape: stop all pools in parallel for faster shutdowns. #19295
|
||||
- [ENHANCEMENT] storage/remote: Add undocumented failed_request_logging config field to debug log remote write V2 requests on send errors. #19249
|
||||
- [ENHANCEMENT] TSDB: Add fast path for XOR chunk decompression to speed up queries. #18049
|
||||
- [ENHANCEMENT] UI: Improve native histogram table formatting and add a background bar indicating the bucket count. #19332
|
||||
- [ENHANCEMENT] TSDB: Add `prometheus_tsdb_head_series_pending_commit_underflow_total` to report pending-sample reservation underflows. #19470
|
||||
- [PERF] AWS SD: Build RDS cluster labels once per cluster instead of once per instance. #19504
|
||||
- [PERF] AWS SD: Describe RDS instances of different clusters concurrently, bounded by `request_concurrency`. #19506
|
||||
- [PERF] AWS SD: Describe each ElastiCache resource once per refresh instead of twice. #19585
|
||||
- [PERF] Remote read: Avoid cloning labels for sampled reads when no external labels are configured. #19503
|
||||
- [PERF] Remote write: Reuse OTLP converter scratch state between requests. #19388
|
||||
- [PERF] scrape: conversion from classic to native histograms should only parse start times when enabled. #19446
|
||||
- [BUGFIX] PromQL: Fix `info()` enrichment for composite expressions with mixed `@`/offset references or selector-free vector branches, preventing metadata from being evaluated at an unrelated timestamp. #19387
|
||||
- [BUGFIX] PromQL: Fix info() enrichment when input series use different subsets of identifying labels. #19557
|
||||
- [BUGFIX] PromQL: Preserve metric-name dropping through the info function when delayed name removal is enabled. #19413
|
||||
- [BUGFIX] TSDB: Don't silently drop samples when head garbage collection removes a series while it is being appended to. #19272
|
||||
- [BUGFIX] TSDB: Keep series with uncommitted samples during selected- and stale-series compaction, including when appenders overlap. #19470
|
||||
- [BUGFIX] TSDB: Do not retain head series after a synthetic start-timestamp zero sample is rejected. #19470
|
||||
- [BUGFIX] TSDB: Prevent query panics during series eviction after WAL replay. #19664
|
||||
- [BUGFIX] TSDB: fix potential deadlock between mmapSeriesChunks and gcSeries. #19460
|
||||
- [BUGFIX] TSDB: fix default block reload interval for custom options. #19368
|
||||
- [BUGFIX] AWS SD: Do not crash on serverless MSK clusters or MSK clusters without Open Monitoring. #19194
|
||||
- [BUGFIX] AWS SD: Do not panic when the ElastiCache API omits optional fields of a serverless cache or cache cluster. #19435
|
||||
- [BUGFIX] AWS SD: Reject non-positive `request_concurrency` instead of hanging service discovery indefinitely. #19524
|
||||
- [BUGFIX] Discovery/AWS: Avoid a panic when discovering standalone ECS tasks with custom task groups. #19302
|
||||
- [BUGFIX] Discovery: Do not panic in AWS Lightsail service discovery when an instance is missing optional fields such as availability zone, blueprint, bundle, name, state or support code. #19324
|
||||
- [BUGFIX] Discovery: delete the stale `prometheus_sd_last_update_timestamp_seconds` series for a config that is removed on reload. #19131
|
||||
- [BUGFIX] HTTP: Avoid truncating compressed responses when handlers set Content-Length. #19661
|
||||
- [BUGFIX] IONOS SD: Do not panic when the API response omits the server, NIC or volume collections, or a server's properties. #19438
|
||||
- [BUGFIX] Metadata will not affect the number of Remote Write v2 shards. #19218
|
||||
- [BUGFIX] Mixins: Fix label mismatches that prevented PrometheusHAGroupNotIngestingSamples and PrometheusHAGroupCrashlooping from firing. #19444
|
||||
- [BUGFIX] Native histograms: `DetectReset` no longer misses a counter reset when a populated bucket behind an empty one disappears, which could make histogram `rate()`/`increase()` undercount. #19367
|
||||
- [BUGFIX] Never skip histogram buckets for histogram_stddev and histogram_stdvar functions. #19521
|
||||
- [BUGFIX] OTLP: Do not abort an entire OTLP payload ingestion if one metric has no datapoints. #19343
|
||||
- [BUGFIX] PromQL: Fix FastRegexMatcher false-positive match when a capturing group is directly adjacent to a literal (e.g. `.*\|(foo)\|.*`). #19516
|
||||
- [BUGFIX] PromQL: Fix a panic in range selectors using the experimental `anchored` or `smoothed` modifier when the selected series has no samples inside the query window, for example a query evaluated inside a scrape gap. #19431
|
||||
- [BUGFIX] PromQL: Fix empty results when a subquery with `@` is used as the matrix argument of a call that is not step-invariant (for example `quantile_over_time(scalar(x), metric[...:...] @ T)`). #19187
|
||||
- [BUGFIX] PromQL: Make the "found duplicate series for the match group" many-to-many matching error message deterministic by sorting the two duplicate labels. #18810
|
||||
- [BUGFIX] PromQL: Preserve parentheses around duration literals on Expr.String() round-trip. #19403
|
||||
- [BUGFIX] PromQL: Reject duration-expression offsets and `@ start()` / `@ end()` before range selectors, matching the existing rejection of literal offsets and `@ <timestamp>`. #19406
|
||||
- [BUGFIX] PromQL: Report the position of the histogram argument rather than of a scalar argument in the native histogram NaN observation annotations of `histogram_quantile` and `histogram_fraction`. #19330
|
||||
- [BUGFIX] PromQL: info() now applies the @ modifier/offset when evaluating the info series, so info(v @ T) enriches as of T consistently instead of depending on the query start/eval time. #19266
|
||||
- [BUGFIX] Rules: Fix a panic when a rule manager created without a logger loads a rule file containing multiple YAML documents. #19433
|
||||
- [BUGFIX] Scrape: Do not append a stale marker for a series that is still exposed when the storage returns a new series reference for it. #19328
|
||||
- [BUGFIX] Scrape: JSON log formatter correctly format scrape target info. #19472
|
||||
- [BUGFIX] TSDB: Fix in-order chunk ID overflow by wrapping head chunk IDs modulo 2^23 so they never collide with the out-of-order flag bit. #19450
|
||||
- [BUGFIX] TSDB: Fix out-of-order chunk ID overflow by wrapping `firstOOOChunkID` modulo 2^23 instead of growing unbounded. #19216
|
||||
- [BUGFIX] UI: Remove an extraneous X-axis tick mark in the native histogram chart when using the "linear" display mode. #19326
|
||||
- [BUGFIX] discovery/aws: Avoid a panic when an ElastiCache ARN is missing its resource ID. #19333
|
||||
- [BUGFIX] discovery/aws: Do not panic on MSK clusters whose optional API fields are absent. #19584
|
||||
- [BUGFIX] discovery/aws: Do not panic when the EC2 API omits optional instance fields. #19512
|
||||
- [BUGFIX] discovery/aws: Don't panic on ECS tasks with absent optional fields. #19396
|
||||
- [BUGFIX] discovery/aws: MSK Optional Custom Configuration Fields #19422. #19422
|
||||
- [BUGFIX] discovery/ionos: Fix panic when the IONOS API omits optional server fields. #19418
|
||||
- [BUGFIX] discovery/kubernetes: Populate `__meta_kubernetes_service_loadbalancer_ip` from `status.loadBalancer.ingress`, falling back to deprecated `spec.loadBalancerIP`. #19404
|
||||
- [BUGFIX] histogram: Fix Compact moving buckets to wrong indices, and producing negative bucket counts for integer histograms, when more than one span is merged in the same pass. #19312
|
||||
- [BUGFIX] promtool: Fixed `tsdb dump` silently dropping native histogram samples. #18051
|
||||
- [BUGFIX] scrape: fix data race in `Manager.TargetsDroppedCounts` to avoid miscounting dropped targets. #19304
|
||||
- [BUGFIX] scrape: fix nil histogram when native and classic histograms are mixed in one metric family. #19452
|
||||
- [BUGFIX] TSDB: Fix WAL and GC log messages to emit human-readable duration strings instead of nanosecond integers. #19307
|
||||
- [BUGFIX] TSDB: Avoid WAL corruption after a failed WAL write in Agent mode. #19700
|
||||
- [BUGFIX] Agent: Ignore unknown WAL record types, to help users rolling back. #19814
|
||||
|
||||
## 3.14.0 / 2026-08-17
|
||||
|
||||
- [CHANGE] API: Deprecate the `stats` query parameter of `/api/v1/query` and `/api/v1/query_range` for values other than `true` and `all`. Other values still enable basic statistics but now return a deprecation warning; they will be rejected in the next major release. #19124
|
||||
|
||||
@@ -92,7 +92,7 @@ func (c *Client) Write(samples model.Samples) error {
|
||||
t := float64(s.Timestamp.UnixNano()) / 1e9
|
||||
v := float64(s.Value)
|
||||
if math.IsNaN(v) || math.IsInf(v, 0) {
|
||||
c.logger.Debug("Cannot send value to Graphite, skipping sample", "value", v, "sample", s) //nolint:loggercheck
|
||||
c.logger.Debug("Cannot send value to Graphite, skipping sample", "value", v, "sample", s) //nolint:loggercheck // False positive for structured logging.
|
||||
continue
|
||||
}
|
||||
fmt.Fprintf(&buf, "%s %f %f\n", k, v, t)
|
||||
|
||||
@@ -89,7 +89,7 @@ func (c *Client) Write(samples model.Samples) error {
|
||||
for _, s := range samples {
|
||||
v := float64(s.Value)
|
||||
if math.IsNaN(v) || math.IsInf(v, 0) {
|
||||
c.logger.Debug("Cannot send to InfluxDB, skipping sample", "value", v, "sample", s) //nolint:loggercheck
|
||||
c.logger.Debug("Cannot send to InfluxDB, skipping sample", "value", v, "sample", s) //nolint:loggercheck // False positive for structured logging.
|
||||
c.ignoredSamples.Inc()
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@ func (c *Client) Write(samples model.Samples) error {
|
||||
for _, s := range samples {
|
||||
v := float64(s.Value)
|
||||
if math.IsNaN(v) || math.IsInf(v, 0) {
|
||||
c.logger.Debug("Cannot send value to OpenTSDB, skipping sample", "value", v, "sample", s) //nolint:loggercheck
|
||||
c.logger.Debug("Cannot send value to OpenTSDB, skipping sample", "value", v, "sample", s) //nolint:loggercheck // False positive for structured logging.
|
||||
continue
|
||||
}
|
||||
metric := TagValue(s.Metric[model.MetricNameLabel])
|
||||
|
||||
@@ -45,7 +45,7 @@ func (a alertmanagerLabels) url() *url.URL {
|
||||
|
||||
// AlertmanagerFromGroup extracts a list of alertmanagers from a target group
|
||||
// and an associated AlertmanagerConfig.
|
||||
func AlertmanagerFromGroup(tg *targetgroup.Group, cfg *config.AlertmanagerConfig) ([]alertmanager, []alertmanager, error) { //nolint:revive // unexported-return
|
||||
func AlertmanagerFromGroup(tg *targetgroup.Group, cfg *config.AlertmanagerConfig) ([]alertmanager, []alertmanager, error) { //nolint:revive // Preserve the existing API with an unexported return type.
|
||||
var res []alertmanager
|
||||
var droppedAlertManagers []alertmanager
|
||||
lb := labels.NewBuilder(labels.EmptyLabels())
|
||||
|
||||
@@ -190,6 +190,630 @@ func TestUnsupportedFunctions(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestCommit(t *testing.T) {
|
||||
const (
|
||||
numDatapoints = 1000
|
||||
numHistograms = 100
|
||||
numSeries = 8
|
||||
)
|
||||
|
||||
s := createTestAgentDB(t, nil, DefaultOptions())
|
||||
app := s.Appender(context.TODO())
|
||||
|
||||
lbls := labelsForTest(t.Name(), numSeries)
|
||||
for _, l := range lbls {
|
||||
lset := labels.New(l...)
|
||||
|
||||
for i := range numDatapoints {
|
||||
sample := chunks.GenerateSamples(0, 1)
|
||||
ref, err := app.Append(0, lset, sample[0].T(), sample[0].F())
|
||||
require.NoError(t, err)
|
||||
|
||||
e := exemplar.Exemplar{
|
||||
Labels: lset,
|
||||
Ts: sample[0].T() + int64(i),
|
||||
Value: sample[0].F(),
|
||||
HasTs: true,
|
||||
}
|
||||
_, err = app.AppendExemplar(ref, lset, e)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
}
|
||||
|
||||
lbls = labelsForTest(t.Name()+"_histogram", numSeries)
|
||||
for _, l := range lbls {
|
||||
lset := labels.New(l...)
|
||||
|
||||
histograms := tsdbutil.GenerateTestHistograms(numHistograms)
|
||||
|
||||
for i := range numHistograms {
|
||||
_, err := app.AppendHistogram(0, lset, int64(i), histograms[i], nil)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
}
|
||||
|
||||
lbls = labelsForTest(t.Name()+"_custom_buckets_histogram", numSeries)
|
||||
for _, l := range lbls {
|
||||
lset := labels.New(l...)
|
||||
|
||||
customBucketHistograms := tsdbutil.GenerateTestCustomBucketsHistograms(numHistograms)
|
||||
|
||||
for i := range numHistograms {
|
||||
_, err := app.AppendHistogram(0, lset, int64(i), customBucketHistograms[i], nil)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
}
|
||||
|
||||
lbls = labelsForTest(t.Name()+"_float_histogram", numSeries)
|
||||
for _, l := range lbls {
|
||||
lset := labels.New(l...)
|
||||
|
||||
floatHistograms := tsdbutil.GenerateTestFloatHistograms(numHistograms)
|
||||
|
||||
for i := range numHistograms {
|
||||
_, err := app.AppendHistogram(0, lset, int64(i), nil, floatHistograms[i])
|
||||
require.NoError(t, err)
|
||||
}
|
||||
}
|
||||
|
||||
lbls = labelsForTest(t.Name()+"_custom_buckets_float_histogram", numSeries)
|
||||
for _, l := range lbls {
|
||||
lset := labels.New(l...)
|
||||
|
||||
customBucketFloatHistograms := tsdbutil.GenerateTestCustomBucketsFloatHistograms(numHistograms)
|
||||
|
||||
for i := range numHistograms {
|
||||
_, err := app.AppendHistogram(0, lset, int64(i), nil, customBucketFloatHistograms[i])
|
||||
require.NoError(t, err)
|
||||
}
|
||||
}
|
||||
|
||||
require.NoError(t, app.Commit())
|
||||
require.NoError(t, s.Close())
|
||||
|
||||
sr, err := wlog.NewSegmentsReader(s.wal.Dir())
|
||||
require.NoError(t, err)
|
||||
defer func() {
|
||||
require.NoError(t, sr.Close())
|
||||
}()
|
||||
|
||||
// Read records from WAL and check for expected count of series, samples, and exemplars.
|
||||
var (
|
||||
r = wlog.NewReader(sr)
|
||||
dec = record.NewDecoder(labels.NewSymbolTable(), promslog.NewNopLogger())
|
||||
|
||||
walSeriesCount, walSamplesCount, walExemplarsCount, walHistogramCount, walFloatHistogramCount int
|
||||
)
|
||||
for r.Next() {
|
||||
rec := r.Record()
|
||||
switch dec.Type(rec) {
|
||||
case record.Series:
|
||||
var series []record.RefSeries
|
||||
series, err = dec.Series(rec, series)
|
||||
require.NoError(t, err)
|
||||
walSeriesCount += len(series)
|
||||
|
||||
case record.Samples, record.SamplesV2:
|
||||
var samples []record.RefSample
|
||||
samples, err = dec.Samples(rec, samples)
|
||||
require.NoError(t, err)
|
||||
walSamplesCount += len(samples)
|
||||
|
||||
case record.HistogramSamples, record.CustomBucketsHistogramSamples, record.HistogramSamplesV2:
|
||||
var histograms []record.RefHistogramSample
|
||||
histograms, err = dec.HistogramSamples(rec, histograms)
|
||||
require.NoError(t, err)
|
||||
walHistogramCount += len(histograms)
|
||||
|
||||
case record.FloatHistogramSamples, record.CustomBucketsFloatHistogramSamples, record.FloatHistogramSamplesV2:
|
||||
var floatHistograms []record.RefFloatHistogramSample
|
||||
floatHistograms, err = dec.FloatHistogramSamples(rec, floatHistograms)
|
||||
require.NoError(t, err)
|
||||
walFloatHistogramCount += len(floatHistograms)
|
||||
|
||||
case record.Exemplars:
|
||||
var exemplars []record.RefExemplar
|
||||
exemplars, err = dec.Exemplars(rec, exemplars)
|
||||
require.NoError(t, err)
|
||||
walExemplarsCount += len(exemplars)
|
||||
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
// Check that the WAL contained the same number of committed series/samples/exemplars.
|
||||
require.Equal(t, numSeries*5, walSeriesCount, "unexpected number of series")
|
||||
require.Equal(t, numSeries*numDatapoints, walSamplesCount, "unexpected number of samples")
|
||||
require.Equal(t, numSeries*numDatapoints, walExemplarsCount, "unexpected number of exemplars")
|
||||
require.Equal(t, numSeries*numHistograms*2, walHistogramCount, "unexpected number of histograms")
|
||||
require.Equal(t, numSeries*numHistograms*2, walFloatHistogramCount, "unexpected number of float histograms")
|
||||
|
||||
// Check that we can get another appender after this - see https://github.com/prometheus/prometheus/issues/17800.
|
||||
_ = s.Appender(context.TODO())
|
||||
}
|
||||
|
||||
func TestRollback(t *testing.T) {
|
||||
const (
|
||||
numDatapoints = 1000
|
||||
numHistograms = 100
|
||||
numSeries = 8
|
||||
)
|
||||
|
||||
s := createTestAgentDB(t, nil, DefaultOptions())
|
||||
app := s.Appender(context.TODO())
|
||||
|
||||
lbls := labelsForTest(t.Name(), numSeries)
|
||||
for _, l := range lbls {
|
||||
lset := labels.New(l...)
|
||||
|
||||
for range numDatapoints {
|
||||
sample := chunks.GenerateSamples(0, 1)
|
||||
_, err := app.Append(0, lset, sample[0].T(), sample[0].F())
|
||||
require.NoError(t, err)
|
||||
}
|
||||
}
|
||||
|
||||
lbls = labelsForTest(t.Name()+"_histogram", numSeries)
|
||||
for _, l := range lbls {
|
||||
lset := labels.New(l...)
|
||||
|
||||
histograms := tsdbutil.GenerateTestHistograms(numHistograms)
|
||||
|
||||
for i := range numHistograms {
|
||||
_, err := app.AppendHistogram(0, lset, int64(i), histograms[i], nil)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
}
|
||||
|
||||
lbls = labelsForTest(t.Name()+"_custom_buckets_histogram", numSeries)
|
||||
for _, l := range lbls {
|
||||
lset := labels.New(l...)
|
||||
|
||||
histograms := tsdbutil.GenerateTestCustomBucketsHistograms(numHistograms)
|
||||
|
||||
for i := range numHistograms {
|
||||
_, err := app.AppendHistogram(0, lset, int64(i), histograms[i], nil)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
}
|
||||
|
||||
lbls = labelsForTest(t.Name()+"_float_histogram", numSeries)
|
||||
for _, l := range lbls {
|
||||
lset := labels.New(l...)
|
||||
|
||||
floatHistograms := tsdbutil.GenerateTestFloatHistograms(numHistograms)
|
||||
|
||||
for i := range numHistograms {
|
||||
_, err := app.AppendHistogram(0, lset, int64(i), nil, floatHistograms[i])
|
||||
require.NoError(t, err)
|
||||
}
|
||||
}
|
||||
|
||||
lbls = labelsForTest(t.Name()+"_custom_buckets_float_histogram", numSeries)
|
||||
for _, l := range lbls {
|
||||
lset := labels.New(l...)
|
||||
|
||||
floatHistograms := tsdbutil.GenerateTestCustomBucketsFloatHistograms(numHistograms)
|
||||
|
||||
for i := range numHistograms {
|
||||
_, err := app.AppendHistogram(0, lset, int64(i), nil, floatHistograms[i])
|
||||
require.NoError(t, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Do a rollback, which should clear uncommitted data. A followup call to
|
||||
// commit should persist nothing to the WAL.
|
||||
require.NoError(t, app.Rollback())
|
||||
require.NoError(t, app.Commit())
|
||||
require.NoError(t, s.Close())
|
||||
|
||||
sr, err := wlog.NewSegmentsReader(s.wal.Dir())
|
||||
require.NoError(t, err)
|
||||
defer func() {
|
||||
require.NoError(t, sr.Close())
|
||||
}()
|
||||
|
||||
// Read records from WAL and check for expected count of series and samples.
|
||||
var (
|
||||
r = wlog.NewReader(sr)
|
||||
dec = record.NewDecoder(labels.NewSymbolTable(), promslog.NewNopLogger())
|
||||
|
||||
walSeriesCount, walSamplesCount, walHistogramCount, walFloatHistogramCount, walExemplarsCount int
|
||||
)
|
||||
for r.Next() {
|
||||
rec := r.Record()
|
||||
switch dec.Type(rec) {
|
||||
case record.Series:
|
||||
var series []record.RefSeries
|
||||
series, err = dec.Series(rec, series)
|
||||
require.NoError(t, err)
|
||||
walSeriesCount += len(series)
|
||||
|
||||
case record.Samples, record.SamplesV2:
|
||||
var samples []record.RefSample
|
||||
samples, err = dec.Samples(rec, samples)
|
||||
require.NoError(t, err)
|
||||
walSamplesCount += len(samples)
|
||||
|
||||
case record.Exemplars:
|
||||
var exemplars []record.RefExemplar
|
||||
exemplars, err = dec.Exemplars(rec, exemplars)
|
||||
require.NoError(t, err)
|
||||
walExemplarsCount += len(exemplars)
|
||||
|
||||
case record.HistogramSamples, record.CustomBucketsHistogramSamples, record.HistogramSamplesV2:
|
||||
var histograms []record.RefHistogramSample
|
||||
histograms, err = dec.HistogramSamples(rec, histograms)
|
||||
require.NoError(t, err)
|
||||
walHistogramCount += len(histograms)
|
||||
|
||||
case record.FloatHistogramSamples, record.CustomBucketsFloatHistogramSamples, record.FloatHistogramSamplesV2:
|
||||
var floatHistograms []record.RefFloatHistogramSample
|
||||
floatHistograms, err = dec.FloatHistogramSamples(rec, floatHistograms)
|
||||
require.NoError(t, err)
|
||||
walFloatHistogramCount += len(floatHistograms)
|
||||
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
// Check that only series get stored after calling Rollback.
|
||||
require.Equal(t, numSeries*5, walSeriesCount, "series should have been written to WAL")
|
||||
require.Equal(t, 0, walSamplesCount, "samples should not have been written to WAL")
|
||||
require.Equal(t, 0, walExemplarsCount, "exemplars should not have been written to WAL")
|
||||
require.Equal(t, 0, walHistogramCount, "histograms should not have been written to WAL")
|
||||
require.Equal(t, 0, walFloatHistogramCount, "float histograms should not have been written to WAL")
|
||||
}
|
||||
|
||||
func TestFullTruncateWAL(t *testing.T) {
|
||||
const (
|
||||
numDatapoints = 1000
|
||||
numHistograms = 100
|
||||
numSeries = 800
|
||||
lastTs = 500
|
||||
)
|
||||
|
||||
reg := prometheus.NewRegistry()
|
||||
opts := DefaultOptions()
|
||||
opts.TruncateFrequency = time.Minute * 2
|
||||
|
||||
s := createTestAgentDB(t, reg, opts)
|
||||
defer func() {
|
||||
require.NoError(t, s.Close())
|
||||
}()
|
||||
app := s.Appender(context.TODO())
|
||||
|
||||
lbls := labelsForTest(t.Name(), numSeries)
|
||||
for _, l := range lbls {
|
||||
lset := labels.New(l...)
|
||||
|
||||
for range numDatapoints {
|
||||
_, err := app.Append(0, lset, int64(lastTs), 0)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
require.NoError(t, app.Commit())
|
||||
}
|
||||
|
||||
lbls = labelsForTest(t.Name()+"_histogram", numSeries)
|
||||
for _, l := range lbls {
|
||||
lset := labels.New(l...)
|
||||
|
||||
histograms := tsdbutil.GenerateTestHistograms(numHistograms)
|
||||
|
||||
for i := range numHistograms {
|
||||
_, err := app.AppendHistogram(0, lset, int64(lastTs), histograms[i], nil)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
require.NoError(t, app.Commit())
|
||||
}
|
||||
|
||||
lbls = labelsForTest(t.Name()+"_custom_buckets_histogram", numSeries)
|
||||
for _, l := range lbls {
|
||||
lset := labels.New(l...)
|
||||
|
||||
histograms := tsdbutil.GenerateTestCustomBucketsHistograms(numHistograms)
|
||||
|
||||
for i := range numHistograms {
|
||||
_, err := app.AppendHistogram(0, lset, int64(lastTs), histograms[i], nil)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
require.NoError(t, app.Commit())
|
||||
}
|
||||
|
||||
lbls = labelsForTest(t.Name()+"_float_histogram", numSeries)
|
||||
for _, l := range lbls {
|
||||
lset := labels.New(l...)
|
||||
|
||||
floatHistograms := tsdbutil.GenerateTestFloatHistograms(numHistograms)
|
||||
|
||||
for i := range numHistograms {
|
||||
_, err := app.AppendHistogram(0, lset, int64(lastTs), nil, floatHistograms[i])
|
||||
require.NoError(t, err)
|
||||
}
|
||||
require.NoError(t, app.Commit())
|
||||
}
|
||||
|
||||
lbls = labelsForTest(t.Name()+"_custom_buckets_float_histogram", numSeries)
|
||||
for _, l := range lbls {
|
||||
lset := labels.New(l...)
|
||||
|
||||
floatHistograms := tsdbutil.GenerateTestCustomBucketsFloatHistograms(numHistograms)
|
||||
|
||||
for i := range numHistograms {
|
||||
_, err := app.AppendHistogram(0, lset, int64(lastTs), nil, floatHistograms[i])
|
||||
require.NoError(t, err)
|
||||
}
|
||||
require.NoError(t, app.Commit())
|
||||
}
|
||||
|
||||
// Truncate WAL with mint to GC all the samples.
|
||||
s.truncate(lastTs + 1)
|
||||
|
||||
m := gatherFamily(t, reg, "prometheus_agent_deleted_series")
|
||||
require.Equal(t, float64(numSeries*5), m.Metric[0].Gauge.GetValue(), "agent wal truncate mismatch of deleted series count")
|
||||
}
|
||||
|
||||
func TestPartialTruncateWAL(t *testing.T) {
|
||||
const (
|
||||
numDatapoints = 1000
|
||||
numSeries = 800
|
||||
)
|
||||
|
||||
opts := DefaultOptions()
|
||||
|
||||
reg := prometheus.NewRegistry()
|
||||
s := createTestAgentDB(t, reg, opts)
|
||||
defer func() {
|
||||
require.NoError(t, s.Close())
|
||||
}()
|
||||
app := s.Appender(context.TODO())
|
||||
|
||||
// Create first batch of 800 series with 1000 data-points with a fixed lastTs as 500.
|
||||
var lastTs int64 = 500
|
||||
lbls := labelsForTest(t.Name()+"batch-1", numSeries)
|
||||
for _, l := range lbls {
|
||||
lset := labels.New(l...)
|
||||
|
||||
for range numDatapoints {
|
||||
_, err := app.Append(0, lset, lastTs, 0)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
require.NoError(t, app.Commit())
|
||||
}
|
||||
|
||||
lbls = labelsForTest(t.Name()+"_histogram_batch-1", numSeries)
|
||||
for _, l := range lbls {
|
||||
lset := labels.New(l...)
|
||||
|
||||
histograms := tsdbutil.GenerateTestHistograms(numDatapoints)
|
||||
|
||||
for i := range numDatapoints {
|
||||
_, err := app.AppendHistogram(0, lset, lastTs, histograms[i], nil)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
require.NoError(t, app.Commit())
|
||||
}
|
||||
|
||||
lbls = labelsForTest(t.Name()+"_custom_buckets_histogram_batch-1", numSeries)
|
||||
for _, l := range lbls {
|
||||
lset := labels.New(l...)
|
||||
|
||||
histograms := tsdbutil.GenerateTestCustomBucketsHistograms(numDatapoints)
|
||||
|
||||
for i := range numDatapoints {
|
||||
_, err := app.AppendHistogram(0, lset, lastTs, histograms[i], nil)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
require.NoError(t, app.Commit())
|
||||
}
|
||||
|
||||
lbls = labelsForTest(t.Name()+"_float_histogram_batch-1", numSeries)
|
||||
for _, l := range lbls {
|
||||
lset := labels.New(l...)
|
||||
|
||||
floatHistograms := tsdbutil.GenerateTestFloatHistograms(numDatapoints)
|
||||
|
||||
for i := range numDatapoints {
|
||||
_, err := app.AppendHistogram(0, lset, lastTs, nil, floatHistograms[i])
|
||||
require.NoError(t, err)
|
||||
}
|
||||
require.NoError(t, app.Commit())
|
||||
}
|
||||
|
||||
lbls = labelsForTest(t.Name()+"_custom_buckets_float_histogram_batch-1", numSeries)
|
||||
for _, l := range lbls {
|
||||
lset := labels.New(l...)
|
||||
|
||||
floatHistograms := tsdbutil.GenerateTestCustomBucketsFloatHistograms(numDatapoints)
|
||||
|
||||
for i := range numDatapoints {
|
||||
_, err := app.AppendHistogram(0, lset, lastTs, nil, floatHistograms[i])
|
||||
require.NoError(t, err)
|
||||
}
|
||||
require.NoError(t, app.Commit())
|
||||
}
|
||||
|
||||
// Create second batch of 800 series with 1000 data-points with a fixed lastTs as 600.
|
||||
lastTs = 600
|
||||
lbls = labelsForTest(t.Name()+"batch-2", numSeries)
|
||||
for _, l := range lbls {
|
||||
lset := labels.New(l...)
|
||||
|
||||
for range numDatapoints {
|
||||
_, err := app.Append(0, lset, lastTs, 0)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
require.NoError(t, app.Commit())
|
||||
}
|
||||
|
||||
lbls = labelsForTest(t.Name()+"_histogram_batch-2", numSeries)
|
||||
for _, l := range lbls {
|
||||
lset := labels.New(l...)
|
||||
|
||||
histograms := tsdbutil.GenerateTestHistograms(numDatapoints)
|
||||
|
||||
for i := range numDatapoints {
|
||||
_, err := app.AppendHistogram(0, lset, lastTs, histograms[i], nil)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
require.NoError(t, app.Commit())
|
||||
}
|
||||
|
||||
lbls = labelsForTest(t.Name()+"_custom_buckets_histogram_batch-2", numSeries)
|
||||
for _, l := range lbls {
|
||||
lset := labels.New(l...)
|
||||
|
||||
histograms := tsdbutil.GenerateTestCustomBucketsHistograms(numDatapoints)
|
||||
|
||||
for i := range numDatapoints {
|
||||
_, err := app.AppendHistogram(0, lset, lastTs, histograms[i], nil)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
require.NoError(t, app.Commit())
|
||||
}
|
||||
|
||||
lbls = labelsForTest(t.Name()+"_float_histogram_batch-2", numSeries)
|
||||
for _, l := range lbls {
|
||||
lset := labels.New(l...)
|
||||
|
||||
floatHistograms := tsdbutil.GenerateTestFloatHistograms(numDatapoints)
|
||||
|
||||
for i := range numDatapoints {
|
||||
_, err := app.AppendHistogram(0, lset, lastTs, nil, floatHistograms[i])
|
||||
require.NoError(t, err)
|
||||
}
|
||||
require.NoError(t, app.Commit())
|
||||
}
|
||||
|
||||
lbls = labelsForTest(t.Name()+"_custom_buckets_float_histogram_batch-2", numSeries)
|
||||
for _, l := range lbls {
|
||||
lset := labels.New(l...)
|
||||
|
||||
floatHistograms := tsdbutil.GenerateTestCustomBucketsFloatHistograms(numDatapoints)
|
||||
|
||||
for i := range numDatapoints {
|
||||
_, err := app.AppendHistogram(0, lset, lastTs, nil, floatHistograms[i])
|
||||
require.NoError(t, err)
|
||||
}
|
||||
require.NoError(t, app.Commit())
|
||||
}
|
||||
|
||||
// Truncate WAL with mint to GC only the first batch of 800 series and retaining 2nd batch of 800 series.
|
||||
s.truncate(lastTs - 1)
|
||||
|
||||
m := gatherFamily(t, reg, "prometheus_agent_deleted_series")
|
||||
require.Len(t, m.Metric, 1)
|
||||
require.Equal(t, float64(numSeries*5), m.Metric[0].Gauge.GetValue(), "agent wal truncate mismatch of deleted series count")
|
||||
}
|
||||
|
||||
func TestWALReplay(t *testing.T) {
|
||||
for _, unknownRecord := range []bool{false, true} {
|
||||
t.Run(fmt.Sprintf("unknownRecord=%t", unknownRecord), func(t *testing.T) {
|
||||
const (
|
||||
numDatapoints = 1000
|
||||
numHistograms = 100
|
||||
numSeries = 8
|
||||
lastTs = 500
|
||||
)
|
||||
|
||||
s := createTestAgentDB(t, nil, DefaultOptions())
|
||||
if unknownRecord {
|
||||
// Valid records following an unknown type must still be replayed.
|
||||
require.NoError(t, s.wal.Log([]byte{255, 1, 2, 3}))
|
||||
}
|
||||
app := s.Appender(context.TODO())
|
||||
|
||||
lbls := labelsForTest(t.Name(), numSeries)
|
||||
for _, l := range lbls {
|
||||
lset := labels.New(l...)
|
||||
|
||||
for range numDatapoints {
|
||||
_, err := app.Append(0, lset, lastTs, 0)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
}
|
||||
|
||||
lbls = labelsForTest(t.Name()+"_histogram", numSeries)
|
||||
for _, l := range lbls {
|
||||
lset := labels.New(l...)
|
||||
|
||||
histograms := tsdbutil.GenerateTestHistograms(numHistograms)
|
||||
|
||||
for i := range numHistograms {
|
||||
_, err := app.AppendHistogram(0, lset, lastTs, histograms[i], nil)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
}
|
||||
|
||||
lbls = labelsForTest(t.Name()+"_custom_buckets_histogram", numSeries)
|
||||
for _, l := range lbls {
|
||||
lset := labels.New(l...)
|
||||
|
||||
histograms := tsdbutil.GenerateTestCustomBucketsHistograms(numHistograms)
|
||||
|
||||
for i := range numHistograms {
|
||||
_, err := app.AppendHistogram(0, lset, lastTs, histograms[i], nil)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
}
|
||||
|
||||
lbls = labelsForTest(t.Name()+"_float_histogram", numSeries)
|
||||
for _, l := range lbls {
|
||||
lset := labels.New(l...)
|
||||
|
||||
floatHistograms := tsdbutil.GenerateTestFloatHistograms(numHistograms)
|
||||
|
||||
for i := range numHistograms {
|
||||
_, err := app.AppendHistogram(0, lset, lastTs, nil, floatHistograms[i])
|
||||
require.NoError(t, err)
|
||||
}
|
||||
}
|
||||
|
||||
lbls = labelsForTest(t.Name()+"_custom_buckets_float_histogram", numSeries)
|
||||
for _, l := range lbls {
|
||||
lset := labels.New(l...)
|
||||
|
||||
floatHistograms := tsdbutil.GenerateTestCustomBucketsFloatHistograms(numHistograms)
|
||||
|
||||
for i := range numHistograms {
|
||||
_, err := app.AppendHistogram(0, lset, lastTs, nil, floatHistograms[i])
|
||||
require.NoError(t, err)
|
||||
}
|
||||
}
|
||||
|
||||
require.NoError(t, app.Commit())
|
||||
require.NoError(t, s.Close())
|
||||
|
||||
// Hack: s.wal.Dir() is the /wal subdirectory of the original storage path.
|
||||
// We need the original directory so we can recreate the storage for replay.
|
||||
storageDir := filepath.Dir(s.wal.Dir())
|
||||
|
||||
// Replay twice to ensure unknown records do not trigger destructive WAL repair.
|
||||
for range 2 {
|
||||
reg := prometheus.NewRegistry()
|
||||
replayStorage, err := Open(s.logger, reg, nil, storageDir, s.opts)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create storage for the agent: %v", err)
|
||||
}
|
||||
|
||||
// Check if all the series are retrieved back from the WAL.
|
||||
m := gatherFamily(t, reg, "prometheus_agent_active_series")
|
||||
require.Equal(t, float64(numSeries*5), m.Metric[0].Gauge.GetValue(), "agent wal replay mismatch of active series count")
|
||||
|
||||
// Check if lastTs of the samples retrieved from the WAL is retained.
|
||||
metrics := replayStorage.series.series
|
||||
for i := range metrics {
|
||||
mp := metrics[i]
|
||||
for _, v := range mp {
|
||||
require.Equal(t, v.lastTs, int64(lastTs))
|
||||
}
|
||||
}
|
||||
require.NoError(t, replayStorage.Close())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestWALTruncationTime(t *testing.T) {
|
||||
opts := DefaultOptions()
|
||||
nowTS := time.Now().UnixMilli()
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@prometheus-io/mantine-ui",
|
||||
"private": true,
|
||||
"version": "0.314.0",
|
||||
"version": "0.315.1",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"start": "vite",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@prometheus-io/codemirror-promql",
|
||||
"version": "0.314.0",
|
||||
"version": "0.315.1",
|
||||
"description": "a CodeMirror mode for the PromQL language",
|
||||
"types": "dist/esm/index.d.ts",
|
||||
"module": "dist/esm/index.js",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@prometheus-io/lezer-promql",
|
||||
"version": "0.314.0",
|
||||
"version": "0.315.1",
|
||||
"description": "lezer-based PromQL grammar",
|
||||
"main": "dist/index.cjs",
|
||||
"type": "module",
|
||||
|
||||
+2
-1
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "prometheus-io",
|
||||
"description": "Monorepo for the Prometheus UI",
|
||||
"version": "0.314.0",
|
||||
"version": "0.315.1",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "bash build_ui.sh --all",
|
||||
@@ -20,6 +20,7 @@
|
||||
"@typescript/native": "npm:typescript@^7.0.2",
|
||||
"@typescript-eslint/eslint-plugin": "^8.71.2",
|
||||
"@typescript-eslint/parser": "^8.71.2",
|
||||
"@typescript/native": "npm:typescript@^7.0.0",
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
"prettier": "^3.9.10",
|
||||
"ts-jest": "^29.4.15",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@prometheus-io/app",
|
||||
"version": "0.313.2",
|
||||
"version": "0.315.1",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@codemirror/autocomplete": "^6.20.3",
|
||||
|
||||
Reference in new issue
Block a user