diff --git a/go.mod b/go.mod
index ff195426da8..a4d850615f8 100644
--- a/go.mod
+++ b/go.mod
@@ -24,7 +24,7 @@ require (
github.com/gorilla/mux v1.8.1
github.com/grafana/regexp v0.0.0-20250905093917-f7b3be9d1853
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0
- github.com/hashicorp/consul/api v1.32.0
+ github.com/hashicorp/consul/api v1.32.1
github.com/hashicorp/go-cleanhttp v0.5.2
github.com/hashicorp/go-metrics v0.5.4
github.com/hashicorp/go-sockaddr v1.0.7
@@ -44,7 +44,7 @@ require (
github.com/prometheus/client_model v0.6.2
github.com/prometheus/common v0.67.5
// Prometheus maps version 3.x.y to tags v0.30x.y.
- github.com/prometheus/prometheus v0.308.1
+ github.com/prometheus/prometheus v0.309.1
github.com/segmentio/fasthash v1.0.3
github.com/sony/gobreaker v1.0.0
github.com/spf13/afero v1.11.0
@@ -94,7 +94,7 @@ require (
github.com/prometheus/procfs v0.16.1
github.com/sercand/kuberesolver/v5 v5.1.1
github.com/tjhop/slog-gokit v0.1.4
- go.opentelemetry.io/collector/pdata v1.45.0
+ go.opentelemetry.io/collector/pdata v1.48.0
go.uber.org/automaxprocs v1.6.0
google.golang.org/protobuf v1.36.11
)
@@ -108,11 +108,11 @@ require (
cloud.google.com/go/iam v1.5.2 // indirect
cloud.google.com/go/monitoring v1.24.2 // indirect
cloud.google.com/go/storage v1.50.0 // indirect
- github.com/Azure/azure-sdk-for-go/sdk/azcore v1.19.1 // indirect
- github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.12.0 // indirect
+ github.com/Azure/azure-sdk-for-go/sdk/azcore v1.20.0 // indirect
+ github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 // indirect
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.1 // indirect
- github.com/AzureAD/microsoft-authentication-library-for-go v1.5.0 // indirect
+ github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0 // indirect
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.31.0 // indirect
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.50.0 // indirect
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.52.0 // indirect
@@ -185,9 +185,9 @@ require (
github.com/golang-jwt/jwt/v5 v5.3.0 // indirect
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
github.com/google/btree v1.1.3 // indirect
- github.com/google/pprof v0.0.0-20250923004556-9e5a51aed1e8 // indirect
+ github.com/google/pprof v0.0.0-20251213031049-b05bdaca462f // indirect
github.com/google/s2a-go v0.1.9 // indirect
- github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect
+ github.com/googleapis/enterprise-certificate-proxy v0.3.7 // indirect
github.com/googleapis/gax-go/v2 v2.15.0 // indirect
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 // indirect
@@ -197,7 +197,7 @@ require (
github.com/hashicorp/go-msgpack v0.5.5 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/go-rootcerts v1.0.2 // indirect
- github.com/hashicorp/go-version v1.7.0 // indirect
+ github.com/hashicorp/go-version v1.8.0 // indirect
github.com/hashicorp/golang-lru v0.6.0 // indirect
github.com/hashicorp/serf v0.10.1 // indirect
github.com/jessevdk/go-flags v1.6.1 // indirect
@@ -217,7 +217,7 @@ require (
github.com/mdlayher/socket v0.5.1 // indirect
github.com/mdlayher/vsock v1.2.1 // indirect
github.com/metalmatze/signal v0.0.0-20210307161603-1c9aa721a97a // indirect
- github.com/miekg/dns v1.1.68 // indirect
+ github.com/miekg/dns v1.1.69 // indirect
github.com/minio/crc64nvme v1.1.1 // indirect
github.com/minio/md5-simd v1.1.2 // indirect
github.com/minio/sha256-simd v1.0.1 // indirect
@@ -230,9 +230,9 @@ require (
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f // indirect
github.com/ncw/swift v1.0.53 // indirect
github.com/oklog/run v1.2.0 // indirect
- github.com/open-telemetry/opentelemetry-collector-contrib/internal/exp/metrics v0.139.0 // indirect
- github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil v0.139.0 // indirect
- github.com/open-telemetry/opentelemetry-collector-contrib/processor/deltatocumulativeprocessor v0.139.0 // indirect
+ github.com/open-telemetry/opentelemetry-collector-contrib/internal/exp/metrics v0.142.0 // indirect
+ github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil v0.142.0 // indirect
+ github.com/open-telemetry/opentelemetry-collector-contrib/processor/deltatocumulativeprocessor v0.142.0 // indirect
github.com/parquet-go/bitpack v1.0.0 // indirect
github.com/parquet-go/jsonlite v1.0.0 // indirect
github.com/philhofer/fwd v1.2.0 // indirect
@@ -263,13 +263,13 @@ require (
github.com/yuin/gopher-lua v1.1.1 // indirect
go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
- go.opentelemetry.io/collector/component v1.45.0 // indirect
- go.opentelemetry.io/collector/confmap v1.45.0 // indirect
- go.opentelemetry.io/collector/confmap/xconfmap v0.139.0 // indirect
- go.opentelemetry.io/collector/consumer v1.45.0 // indirect
- go.opentelemetry.io/collector/featuregate v1.45.0 // indirect
- go.opentelemetry.io/collector/pipeline v1.45.0 // indirect
- go.opentelemetry.io/collector/processor v1.45.0 // indirect
+ go.opentelemetry.io/collector/component v1.48.0 // indirect
+ go.opentelemetry.io/collector/confmap v1.48.0 // indirect
+ go.opentelemetry.io/collector/confmap/xconfmap v0.142.0 // indirect
+ go.opentelemetry.io/collector/consumer v1.48.0 // indirect
+ go.opentelemetry.io/collector/featuregate v1.48.0 // indirect
+ go.opentelemetry.io/collector/pipeline v1.48.0 // indirect
+ go.opentelemetry.io/collector/processor v1.48.0 // indirect
go.opentelemetry.io/collector/semconv v0.128.0 // indirect
go.opentelemetry.io/contrib/detectors/gcp v1.39.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 // indirect
@@ -285,7 +285,7 @@ require (
go.opentelemetry.io/proto/otlp v1.9.0 // indirect
go.uber.org/goleak v1.3.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
- go.uber.org/zap v1.27.0 // indirect
+ go.uber.org/zap v1.27.1 // indirect
go.yaml.in/yaml/v2 v2.4.3 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect
go4.org/intern v0.0.0-20230525184215-6c62f75575cb // indirect
@@ -298,13 +298,13 @@ require (
golang.org/x/text v0.35.0 // indirect
golang.org/x/tools v0.42.0 // indirect
gonum.org/v1/gonum v0.17.0 // indirect
- google.golang.org/api v0.252.0 // indirect
+ google.golang.org/api v0.257.0 // indirect
google.golang.org/genproto v0.0.0-20250603155806-513f23925822 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 // indirect
gopkg.in/telebot.v3 v3.3.8 // indirect
- k8s.io/apimachinery v0.34.1 // indirect
- k8s.io/client-go v0.34.1 // indirect
+ k8s.io/apimachinery v0.34.3 // indirect
+ k8s.io/client-go v0.34.3 // indirect
k8s.io/klog/v2 v2.130.1 // indirect
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 // indirect
)
diff --git a/go.sum b/go.sum
index 725e2bbbf99..6225f9fa1d4 100644
--- a/go.sum
+++ b/go.sum
@@ -76,10 +76,10 @@ cloud.google.com/go/storage v1.50.0/go.mod h1:l7XeiD//vx5lfqE3RavfmU9yvk5Pp0Zhcv
cloud.google.com/go/trace v1.11.6 h1:2O2zjPzqPYAHrn3OKl029qlqG6W8ZdYaOWRyr8NgMT4=
cloud.google.com/go/trace v1.11.6/go.mod h1:GA855OeDEBiBMzcckLPE2kDunIpC72N+Pq8WFieFjnI=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
-github.com/Azure/azure-sdk-for-go/sdk/azcore v1.19.1 h1:5YTBM8QDVIBN3sxBil89WfdAAqDZbyJTgh688DSxX5w=
-github.com/Azure/azure-sdk-for-go/sdk/azcore v1.19.1/go.mod h1:YD5h/ldMsG0XiIw7PdyNhLxaM317eFh5yNLccNfGdyw=
-github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.12.0 h1:wL5IEG5zb7BVv1Kv0Xm92orq+5hB5Nipn3B5tn4Rqfk=
-github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.12.0/go.mod h1:J7MUC/wtRpfGVbQ5sIItY5/FuVWmvzlY21WAOfQnq/I=
+github.com/Azure/azure-sdk-for-go/sdk/azcore v1.20.0 h1:JXg2dwJUmPB9JmtVmdEB16APJ7jurfbY5jnfXpJoRMc=
+github.com/Azure/azure-sdk-for-go/sdk/azcore v1.20.0/go.mod h1:YD5h/ldMsG0XiIw7PdyNhLxaM317eFh5yNLccNfGdyw=
+github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1 h1:Hk5QBxZQC1jb2Fwj6mpzme37xbCDdNTxU7O9eb5+LB4=
+github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1/go.mod h1:IYus9qsFobWIc2YVwe/WPjcnyCkPKtnHAqUYeebc8z0=
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2 h1:yz1bePFlP5Vws5+8ez6T3HWXPmwOK7Yvq8QxDBD3SKY=
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2/go.mod h1:Pa9ZNPuoNu/GztvBSKk9J1cDJW6vk/n0zLtV4mgd8N8=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 h1:9iefClla7iYpfYWdzPCRDozdmndjTm8DXdpCzPajMgA=
@@ -96,8 +96,8 @@ github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1 h1:WJTmL004Abzc5wDB5VtZG2PJk5ndYDgVacGqfirKxjM=
github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mod h1:tCcJZ0uHAmvjsVYzEFivsRTN00oz5BEsRgQHu5JZ9WE=
-github.com/AzureAD/microsoft-authentication-library-for-go v1.5.0 h1:XkkQbfMyuH2jTSjQjSoihryI8GINRcs4xp8lNawg0FI=
-github.com/AzureAD/microsoft-authentication-library-for-go v1.5.0/go.mod h1:HKpQxkWaGLJ+D/5H8QRpyQXA1eKjxkFlOMwck5+33Jk=
+github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0 h1:XRzhVemXdgvJqCH0sFfrBUTnUJSBrBf7++ypk+twtRs=
+github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0/go.mod h1:HKpQxkWaGLJ+D/5H8QRpyQXA1eKjxkFlOMwck5+33Jk=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/Code-Hex/go-generics-cache v1.5.1 h1:6vhZGc5M7Y/YD8cIUcY8kcuQLB4cHR7U+0KMqAA0KcU=
@@ -165,18 +165,18 @@ github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6 h1:qYQ4pzQ2Oz6WpQ8T3HvGHnZydA72
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6/go.mod h1:O3h0IK87yXci+kg6flUKzJnWeziQUKciKrLjcatSNcY=
github.com/aws/aws-sdk-go-v2/service/dynamodb v1.50.1 h1:MXUnj1TKjwQvotPPHFMfynlUljcpl5UccMrkiauKdWI=
github.com/aws/aws-sdk-go-v2/service/dynamodb v1.50.1/go.mod h1:fe3UQAYwylCQRlGnihsqU/tTQkrc2nrW/IhWYwlW9vg=
-github.com/aws/aws-sdk-go-v2/service/ec2 v1.262.0 h1:5qBb1XV/D18qtCHd3bmmxoVglI+fZ4QWuS/EB8kIXYQ=
-github.com/aws/aws-sdk-go-v2/service/ec2 v1.262.0/go.mod h1:NDdDLLW5PtLLXN661gKcvJvqAH5OBXsfhMlmKVu1/pY=
-github.com/aws/aws-sdk-go-v2/service/ecs v1.67.2 h1:oeICOX/+D0XXV1aMYJPXVe3CO37zYr7fB6HFgxchleU=
-github.com/aws/aws-sdk-go-v2/service/ecs v1.67.2/go.mod h1:rrhqfkXfa2DSNq0RyFhnnFEAyI+yJB4+2QlZKeJvMjs=
+github.com/aws/aws-sdk-go-v2/service/ec2 v1.277.0 h1:RHJSkRXDGkAKrV4CTEsZsZkOmSpxXKO4aKx4rXd94K4=
+github.com/aws/aws-sdk-go-v2/service/ec2 v1.277.0/go.mod h1:Wg68QRgy2gEGGdmTPU/UbVpdv8sM14bUZmF64KFwAsY=
+github.com/aws/aws-sdk-go-v2/service/ecs v1.69.5 h1:5nkhwt0d/gjuT3AQ2LUK0aFRNB3MGlzB2elqy/ZsKP4=
+github.com/aws/aws-sdk-go-v2/service/ecs v1.69.5/go.mod h1:LQMlcWBoiFVD3vUVEz42ST0yTiaDujv2dRE6sXt1yPE=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 h1:5EniKhLZe4xzL7a+fU3C2tfUN4nWIqlLesfrjkuPFTY=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7/go.mod h1:x0nZssQ3qZSnIcePWLvcoFisRXJzcTVvYpAAdYX8+GI=
github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.11.6 h1:34ojKW9OV123FZ6Q8Nua3Uwy6yVTcshZ+gLE4gpMDEs=
github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.11.6/go.mod h1:sXXWh1G9LKKkNbuR0f0ZPd/IvDXlMGiag40opt4XEgY=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.21 h1:c31//R3xgIJMSC8S6hEVq+38DcvUlgFY0FM6mSI5oto=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.21/go.mod h1:r6+pf23ouCB718FUxaqzZdbpYFyDtehyZcmP5KL9FkA=
-github.com/aws/aws-sdk-go-v2/service/lightsail v1.50.4 h1:/1o2AYwHJojUDeMvQNyJiKZwcWCc3e4kQuTXqRLuThc=
-github.com/aws/aws-sdk-go-v2/service/lightsail v1.50.4/go.mod h1:Nn2xx6HojGuNMtUFxxz/nyNLSS+tHMRsMhe3+W3wB5k=
+github.com/aws/aws-sdk-go-v2/service/lightsail v1.50.10 h1:MQuZZ6Tq1qQabPlkVxrCMdyVl70Ogl4AERZKo+y9Wzo=
+github.com/aws/aws-sdk-go-v2/service/lightsail v1.50.10/go.mod h1:U5C3JME1ibKESmpzBAqlRpTYZfVbTqrb5ICJm+sVVd8=
github.com/aws/aws-sdk-go-v2/service/signin v1.0.9 h1:QKZH0S178gCmFEgst8hN0mCX1KxLgHBKKY/CLqwP8lg=
github.com/aws/aws-sdk-go-v2/service/signin v1.0.9/go.mod h1:7yuQJoT+OoH8aqIxw9vwF+8KpvLZ8AWmvmUWHsGQZvI=
github.com/aws/aws-sdk-go-v2/service/sns v1.39.15 h1:rOWMUrXJPcTXnk75ja6Bxv1P+j83dPhIWjfJ2cujj34=
@@ -275,8 +275,8 @@ github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/r
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/dhui/dktest v0.4.3 h1:wquqUxAFdcUgabAVLvSCOKOlag5cIZuaOjYIBOWdsR0=
github.com/dhui/dktest v0.4.3/go.mod h1:zNK8IwktWzQRm6I/l2Wjp7MakiyaFWv4G1hjmodmMTs=
-github.com/digitalocean/godo v1.168.0 h1:mlORtUcPD91LQeJoznrH3XvfvgK3t8Wvrpph9giUT/Q=
-github.com/digitalocean/godo v1.168.0/go.mod h1:xQsWpVCCbkDrWisHA72hPzPlnC+4W5w/McZY5ij9uvU=
+github.com/digitalocean/godo v1.171.0 h1:QwpkwWKr3v7yxc8D4NQG973NoR9APCEWjYnLOQeXVpQ=
+github.com/digitalocean/godo v1.171.0/go.mod h1:xQsWpVCCbkDrWisHA72hPzPlnC+4W5w/McZY5ij9uvU=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
github.com/docker/docker v28.5.2+incompatible h1:DBX0Y0zAjZbSrm1uzOkdr1onVghKaftjlSWt4AFexzM=
@@ -424,8 +424,8 @@ github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+
github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI=
github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=
-github.com/go-resty/resty/v2 v2.16.5 h1:hBKqmWrr7uRc3euHVqmh1HTHcKn99Smr7o5spptdhTM=
-github.com/go-resty/resty/v2 v2.16.5/go.mod h1:hkJtXbA2iKHzJheXYvQ8snQES5ZLGKMwQ07xAwp/fiA=
+github.com/go-resty/resty/v2 v2.17.1 h1:x3aMpHK1YM9e4va/TMDRlusDDoZiQ+ViDu/WpA6xTM4=
+github.com/go-resty/resty/v2 v2.17.1/go.mod h1:kCKZ3wWmwJaNc7S29BRtUhJwy7iqmn+2mLtQrOyQlVA=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro=
github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
@@ -539,16 +539,16 @@ github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLe
github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20240227163752-401108e1b7e7/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
-github.com/google/pprof v0.0.0-20250923004556-9e5a51aed1e8 h1:ZI8gCoCjGzPsum4L21jHdQs8shFBIQih1TM9Rd/c+EQ=
-github.com/google/pprof v0.0.0-20250923004556-9e5a51aed1e8/go.mod h1:I6V7YzU0XDpsHqbsyrghnFZLO1gwK6NPTNvmetQIk9U=
+github.com/google/pprof v0.0.0-20251213031049-b05bdaca462f h1:HU1RgM6NALf/KW9HEY6zry3ADbDKcmpQ+hJedoNGQYQ=
+github.com/google/pprof v0.0.0-20251213031049-b05bdaca462f/go.mod h1:67FPmZWbr+KDT/VlpWtw6sO9XSjpJmLuHpoLmWiTGgY=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0=
github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/googleapis/enterprise-certificate-proxy v0.3.6 h1:GW/XbdyBFQ8Qe+YAmFU9uHLo7OnF5tL52HFAgMmyrf4=
-github.com/googleapis/enterprise-certificate-proxy v0.3.6/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA=
+github.com/googleapis/enterprise-certificate-proxy v0.3.7 h1:zrn2Ee/nWmHulBx5sAVrGgAa0f2/R35S4DJwfFaUPFQ=
+github.com/googleapis/enterprise-certificate-proxy v0.3.7/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0=
@@ -559,8 +559,8 @@ github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK
github.com/googleapis/gax-go/v2 v2.15.0 h1:SyjDc1mGgZU5LncH8gimWo9lW1DtIfPibOG81vgd/bo=
github.com/googleapis/gax-go/v2 v2.15.0/go.mod h1:zVVkkxAQHa1RQpg9z2AUCMnKhi0Qld9rcmyfL1OZhoc=
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
-github.com/gophercloud/gophercloud/v2 v2.8.0 h1:of2+8tT6+FbEYHfYC8GBu8TXJNsXYSNm9KuvpX7Neqo=
-github.com/gophercloud/gophercloud/v2 v2.8.0/go.mod h1:Ki/ILhYZr/5EPebrPL9Ej+tUg4lqx71/YH2JWVeU+Qk=
+github.com/gophercloud/gophercloud/v2 v2.9.0 h1:Y9OMrwKF9EDERcHFSOTpf/6XGoAI0yOxmsLmQki4LPM=
+github.com/gophercloud/gophercloud/v2 v2.9.0/go.mod h1:Ki/ILhYZr/5EPebrPL9Ej+tUg4lqx71/YH2JWVeU+Qk=
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5THxAzdVpqr6/geYxZytqFMBCOtn/ujyeo=
@@ -580,8 +580,8 @@ github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFb
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 h1:HWRh5R2+9EifMyIHV7ZV+MIZqgz+PMpZ14Jynv3O2Zs=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0/go.mod h1:JfhWUomR1baixubs02l85lZYYOm7LV6om4ceouMv45c=
github.com/hashicorp/consul/api v1.12.0/go.mod h1:6pVBMo0ebnYdt2S3H87XhekM/HHrUoTD2XXb/VrZVy0=
-github.com/hashicorp/consul/api v1.32.0 h1:5wp5u780Gri7c4OedGEPzmlUEzi0g2KyiPphSr6zjVg=
-github.com/hashicorp/consul/api v1.32.0/go.mod h1:Z8YgY0eVPukT/17ejW+l+C7zJmKwgPHtjU1q16v/Y40=
+github.com/hashicorp/consul/api v1.32.1 h1:0+osr/3t/aZNAdJX558crU3PEjVrG4x6715aZHRgceE=
+github.com/hashicorp/consul/api v1.32.1/go.mod h1:mXUWLnxftwTmDv4W3lzxYCPD199iNLLUyLfLGFJbtl4=
github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms=
github.com/hashicorp/consul/sdk v0.16.1 h1:V8TxTnImoPD5cj0U9Spl0TUxcytjcbbJeADFF07KdHg=
github.com/hashicorp/consul/sdk v0.16.1/go.mod h1:fSXvwxB2hmh1FMZCNl6PwX0Q/1wdWtHJcZ7Ea5tns0s=
@@ -623,8 +623,8 @@ github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/b
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
-github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY=
-github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
+github.com/hashicorp/go-version v1.8.0 h1:KAkNb1HAiZd1ukkxDFGmokVZe1Xy9HG6NUp+bPle2i4=
+github.com/hashicorp/go-version v1.8.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
@@ -635,14 +635,14 @@ github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyf
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc=
-github.com/hashicorp/nomad/api v0.0.0-20250930071859-eaa0fe0e27af h1:ScAYf8O+9xTqTJPZH8MIlUfO+ak8cb31rW1aYJgS+jE=
-github.com/hashicorp/nomad/api v0.0.0-20250930071859-eaa0fe0e27af/go.mod h1:sldFTIgs+FsUeKU3LwVjviAIuksxD8TzDOn02MYwslE=
+github.com/hashicorp/nomad/api v0.0.0-20251216171439-1dee0671280e h1:wGl06iy/H90NSbWjfXWeRwk9SJOks0u4voIryeJFlSA=
+github.com/hashicorp/nomad/api v0.0.0-20251216171439-1dee0671280e/go.mod h1:sldFTIgs+FsUeKU3LwVjviAIuksxD8TzDOn02MYwslE=
github.com/hashicorp/serf v0.9.6/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4=
github.com/hashicorp/serf v0.9.7/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4=
github.com/hashicorp/serf v0.10.1 h1:Z1H2J60yRKvfDYAOZLd2MU0ND4AH/WDz7xYHDWQsIPY=
github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4=
-github.com/hetznercloud/hcloud-go/v2 v2.29.0 h1:LzNFw5XLBfftyu3WM1sdSLjOZBlWORtz2hgGydHaYV8=
-github.com/hetznercloud/hcloud-go/v2 v2.29.0/go.mod h1:XBU4+EDH2KVqu2KU7Ws0+ciZcX4ygukQl/J0L5GS8P8=
+github.com/hetznercloud/hcloud-go/v2 v2.32.0 h1:BRe+k7ESdYv3xQLBGdKUfk+XBFRJNGKzq70nJI24ciM=
+github.com/hetznercloud/hcloud-go/v2 v2.32.0/go.mod h1:hAanyyfn9M0cMmZ68CXzPCF54KRb9EXd8eiE2FHKGIE=
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
github.com/huaweicloud/huaweicloud-sdk-go-obs v3.25.4+incompatible h1:yNjwdvn9fwuN6Ouxr0xHM0cVu03YMUWUyFmu2van/Yc=
@@ -716,8 +716,8 @@ github.com/leesper/go_rng v0.0.0-20190531154944-a612b043e353/go.mod h1:N0SVk0uhy
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
github.com/lib/pq v1.11.2 h1:x6gxUeu39V0BHZiugWe8LXZYZ+Utk7hSJGThs8sdzfs=
github.com/lib/pq v1.11.2/go.mod h1:/p+8NSbOcwzAEI7wiMXFlgydTwcgTr3OSKMsD2BitpA=
-github.com/linode/linodego v1.60.0 h1:SgsebJFRCi+lSmYy+C40wmKZeJllGGm+W12Qw4+yVdI=
-github.com/linode/linodego v1.60.0/go.mod h1:1+Bt0oTz5rBnDOJbGhccxn7LYVytXTIIfAy7QYmijDs=
+github.com/linode/linodego v1.63.0 h1:MdjizfXNJDVJU6ggoJmMO5O9h4KGPGivNX0fzrAnstk=
+github.com/linode/linodego v1.63.0/go.mod h1:GoiwLVuLdBQcAebxAVKVL3mMYUgJZR/puOUSla04xBE=
github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
@@ -748,8 +748,8 @@ github.com/metalmatze/signal v0.0.0-20210307161603-1c9aa721a97a h1:0usWxe5SGXKQo
github.com/metalmatze/signal v0.0.0-20210307161603-1c9aa721a97a/go.mod h1:3OETvrxfELvGsU2RoGGWercfeZ4bCL3+SOwzIWtJH/Q=
github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI=
-github.com/miekg/dns v1.1.68 h1:jsSRkNozw7G/mnmXULynzMNIsgY2dHC8LO6U6Ij2JEA=
-github.com/miekg/dns v1.1.68/go.mod h1:fujopn7TB3Pu3JM69XaawiU0wqjpL9/8xGop5UrTPps=
+github.com/miekg/dns v1.1.69 h1:Kb7Y/1Jo+SG+a2GtfoFUfDkG//csdRPwRLkCsxDG9Sc=
+github.com/miekg/dns v1.1.69/go.mod h1:7OyjD9nEba5OkqQ/hB4fy3PIoxafSZJtducccIelz3g=
github.com/minio/crc64nvme v1.1.1 h1:8dwx/Pz49suywbO+auHCBpCtlW1OfpcLN7wYgVR6wAI=
github.com/minio/crc64nvme v1.1.1/go.mod h1:eVfm2fAzLlxMdUGc0EEBGSMmPwmXD5XiNRpnu9J3bvg=
github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=
@@ -809,12 +809,12 @@ github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
github.com/onsi/gomega v1.36.2 h1:koNYke6TVk6ZmnyHrCXba/T/MoLBXFjeC1PtvYgw0A8=
github.com/onsi/gomega v1.36.2/go.mod h1:DdwyADRjrc825LhMEkD76cHR5+pUnjhUN8GlHlRPHzY=
-github.com/open-telemetry/opentelemetry-collector-contrib/internal/exp/metrics v0.139.0 h1:D5aGQCErSCb4sKIHoZhgR4El6AzgviTRYlHUpbSFqDo=
-github.com/open-telemetry/opentelemetry-collector-contrib/internal/exp/metrics v0.139.0/go.mod h1:ZjeRsA5oaVk89fg5D+iXStx2QncmhAvtGbdSumT07H4=
-github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil v0.139.0 h1:6/j0Ta8ZJnmAFVEoC3aZ1Hs19RB4fHzlN6kOZhsBJqM=
-github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil v0.139.0/go.mod h1:VfA8xHz4xg7Fyj5bBsCDbOO3iVYzDn9wP/QFsjcAE5c=
-github.com/open-telemetry/opentelemetry-collector-contrib/processor/deltatocumulativeprocessor v0.139.0 h1:iRNX/ueuad1psOVgnNkxuQmXxvF3ze5ZZCP66xKFk/w=
-github.com/open-telemetry/opentelemetry-collector-contrib/processor/deltatocumulativeprocessor v0.139.0/go.mod h1:bW09lo3WgHsPsZ1mgsJvby9wCefT5o13patM5phdfIU=
+github.com/open-telemetry/opentelemetry-collector-contrib/internal/exp/metrics v0.142.0 h1:agYk41V3eIfV6aIMxIeRQ7SFhfaW5k2O96HEebpmPwM=
+github.com/open-telemetry/opentelemetry-collector-contrib/internal/exp/metrics v0.142.0/go.mod h1:ZmMdcBia20ih8NYia5b4dNhfNLT68xHgaqF+fNW+TLM=
+github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil v0.142.0 h1:bLp+Ii1UQ9cNr+Dm1jKzbcklhd0eBnPuIFQY6NPzkZ0=
+github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil v0.142.0/go.mod h1:6N36UrFd9Yiz2aYpXM5xiK7Eqp2RyAr3O8lUE+wK2Y8=
+github.com/open-telemetry/opentelemetry-collector-contrib/processor/deltatocumulativeprocessor v0.142.0 h1:fL8LBVeje+nbts2VIInvRa4T5LlsC0BZCI60wNGoS+Y=
+github.com/open-telemetry/opentelemetry-collector-contrib/processor/deltatocumulativeprocessor v0.142.0/go.mod h1:fSnKuTN91I68Ou1Lgfwe3Mt6BGl9kcA8PYCpnGkPnsY=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
@@ -902,8 +902,8 @@ github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4O
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg=
github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=
-github.com/prometheus/prometheus v0.308.1 h1:ApMNI/3/es3Ze90Z7CMb+wwU2BsSYur0m5VKeqHj7h4=
-github.com/prometheus/prometheus v0.308.1/go.mod h1:aHjYCDz9zKRyoUXvMWvu13K9XHOkBB12XrEqibs3e0A=
+github.com/prometheus/prometheus v0.309.1 h1:jutK6eCYDpWdPTUbVbkcQsNCMO9CCkSwjQRMLds4jSo=
+github.com/prometheus/prometheus v0.309.1/go.mod h1:d+dOGiVhuNDa4MaFXHVdnUBy/CzqlcNTooR8oM1wdTU=
github.com/prometheus/sigv4 v0.4.1 h1:EIc3j+8NBea9u1iV6O5ZAN8uvPq2xOIUPcqCTivHuXs=
github.com/prometheus/sigv4 v0.4.1/go.mod h1:eu+ZbRvsc5TPiHwqh77OWuCnWK73IdkETYY46P4dXOU=
github.com/puzpuzpuz/xsync/v3 v3.5.1 h1:GJYJZwO6IdxN/IKbneznS6yPkVC+c3zyY/j19c++5Fg=
@@ -954,8 +954,8 @@ github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An
github.com/spf13/viper v1.13.0/go.mod h1:Icm2xNL3/8uyh/wFuB1jI7TiTNKp8632Nwegu+zgdYw=
github.com/spiffe/go-spiffe/v2 v2.6.0 h1:l+DolpxNWYgruGQVV0xsfeya3CsC7m8iBzDnMpsbLuo=
github.com/spiffe/go-spiffe/v2 v2.6.0/go.mod h1:gm2SeUoMZEtpnzPNs2Csc0D/gX33k1xIx7lEzqblHEs=
-github.com/stackitcloud/stackit-sdk-go/core v0.17.3 h1:GsZGmRRc/3GJLmCUnsZswirr5wfLRrwavbnL/renOqg=
-github.com/stackitcloud/stackit-sdk-go/core v0.17.3/go.mod h1:HBCXJGPgdRulplDzhrmwC+Dak9B/x0nzNtmOpu+1Ahg=
+github.com/stackitcloud/stackit-sdk-go/core v0.20.1 h1:odiuhhRXmxvEvnVTeZSN9u98edvw2Cd3DcnkepncP3M=
+github.com/stackitcloud/stackit-sdk-go/core v0.20.1/go.mod h1:fqto7M82ynGhEnpZU6VkQKYWYoFG5goC076JWXTUPRQ=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
@@ -1037,38 +1037,40 @@ go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
-go.opentelemetry.io/collector/component v1.45.0 h1:gGFfVdbQ+1YuyUkJjWo85I7euu3H/CiupuzCHv8OgHA=
-go.opentelemetry.io/collector/component v1.45.0/go.mod h1:xoNFnRKE8Iv6gmlqAKgjayWraRnDcYLLgrPt9VgyO2g=
-go.opentelemetry.io/collector/component/componentstatus v0.139.0 h1:bQmkv1t7xW7uIDireE0a2Am4IMOprXm6zQr/qDtGCIA=
-go.opentelemetry.io/collector/component/componentstatus v0.139.0/go.mod h1:ibZOohpG0u081/NaT/jMCTsKwRbbwwxWrjZml+owpyM=
-go.opentelemetry.io/collector/component/componenttest v0.139.0 h1:x9Yu2eYhrHxdZ7sFXWtAWVjQ3UIraje557LgNurDC2I=
-go.opentelemetry.io/collector/component/componenttest v0.139.0/go.mod h1:S9cj+qkf9FgHMzjvlYsLwQKd9BiS7B7oLZvxvlENM/c=
-go.opentelemetry.io/collector/confmap v1.45.0 h1:7M7TTlpzX4r+mIzP/ARdxZBAvI4N+1V96phDane+akU=
-go.opentelemetry.io/collector/confmap v1.45.0/go.mod h1:AE1dnkjv0T9gptsh5+mTX0XFGdXx0n7JS4b7CcPfJ6Q=
-go.opentelemetry.io/collector/confmap/xconfmap v0.139.0 h1:uQGpFuWnTCXqdMbI3gDSvkwU66/kF/aoC0kVMrit1EM=
-go.opentelemetry.io/collector/confmap/xconfmap v0.139.0/go.mod h1:d0ucaeNq2rojFRSQsCHF/gkT3cgBx5H2bVkPQMj57ck=
-go.opentelemetry.io/collector/consumer v1.45.0 h1:TtqXxgW+1GSCwdoohq0fzqnfqrZBKbfo++1XRj8mrEA=
-go.opentelemetry.io/collector/consumer v1.45.0/go.mod h1:pJzqTWBubwLt8mVou+G4/Hs23b3m425rVmld3LqOYpY=
-go.opentelemetry.io/collector/consumer/consumertest v0.139.0 h1:06mu43mMO7l49ASJ/GEbKgTWcV3py5zE/pKhNBZ1b3k=
-go.opentelemetry.io/collector/consumer/consumertest v0.139.0/go.mod h1:gaeCpRQGbCFYTeLzi+Z2cTDt40GiIa3hgIEgLEmiC78=
-go.opentelemetry.io/collector/consumer/xconsumer v0.139.0 h1:FhzDv+idglnrfjqPvnUw3YAEOkXSNv/FuNsuMiXQwcY=
-go.opentelemetry.io/collector/consumer/xconsumer v0.139.0/go.mod h1:yWrg/6FE/A4Q7eo/Mg++CzkBoSILHdeMnTlxV3serI0=
-go.opentelemetry.io/collector/featuregate v1.45.0 h1:D06hpf1F2KzKC+qXLmVv5e8IZpgCyZVeVVC8iOQxVmw=
-go.opentelemetry.io/collector/featuregate v1.45.0/go.mod h1:d0tiRzVYrytB6LkcYgz2ESFTv7OktRPQe0QEQcPt1L4=
-go.opentelemetry.io/collector/pdata v1.45.0 h1:q4XaISpeX640BcwXwb2mKOVw/gb67r22HjGWl8sbWsk=
-go.opentelemetry.io/collector/pdata v1.45.0/go.mod h1:5q2f001YhwMQO8QvpFhCOa4Cq/vtwX9W4HRMsXkU/nE=
-go.opentelemetry.io/collector/pdata/pprofile v0.139.0 h1:UA5TgFzYmRuJN3Wz0GR1efLUfjbs5rH0HTaxfASpTR8=
-go.opentelemetry.io/collector/pdata/pprofile v0.139.0/go.mod h1:sI5qHt+zzE2fhOWFdJIaiDBR0yGGjD4A4ZvDFU0tiHk=
-go.opentelemetry.io/collector/pdata/testdata v0.139.0 h1:n7O5bmLLhc3T6PePV4447fFcI/6QWcMhBsLtfCaD0do=
-go.opentelemetry.io/collector/pdata/testdata v0.139.0/go.mod h1:fxZ2VrhYLYBLHYBHC1XQRKZ6IJXwy0I2rPaaRlebYaY=
-go.opentelemetry.io/collector/pipeline v1.45.0 h1:sn9JJAEBe3XABTkWechMk0eH60QMBjjNe5V+ccBl+Uo=
-go.opentelemetry.io/collector/pipeline v1.45.0/go.mod h1:xUrAqiebzYbrgxyoXSkk6/Y3oi5Sy3im2iCA51LwUAI=
-go.opentelemetry.io/collector/processor v1.45.0 h1:GH5km9BkDQOoz7MR0jzTnzB1Kb5vtKzPwa/wDmRg2dQ=
-go.opentelemetry.io/collector/processor v1.45.0/go.mod h1:wdlaTTC3wqlZIJP9R9/SLc2q7h+MFGARsxfjgPtwbes=
-go.opentelemetry.io/collector/processor/processortest v0.139.0 h1:30akUdruFNG7EDpayuBhXoX2lV+hcfxW9Gl3Z6MYHb0=
-go.opentelemetry.io/collector/processor/processortest v0.139.0/go.mod h1:RTll3UKHrqj/VS6RGjTHtuGIJzyLEwFhbw8KuCL3pjo=
-go.opentelemetry.io/collector/processor/xprocessor v0.139.0 h1:O9x9RF/OG8gZ+HrOcB4f6F1fjniby484xf2D8GBxgqU=
-go.opentelemetry.io/collector/processor/xprocessor v0.139.0/go.mod h1:hqGhEZ1/PftD/QHaYna0o1xAqZUsb7GhqpOiaTTDJnQ=
+go.opentelemetry.io/collector/component v1.48.0 h1:0hZKOvT6fIlXoE+6t40UXbXOH7r/h9jyE3eIt0W19Qg=
+go.opentelemetry.io/collector/component v1.48.0/go.mod h1:Kmc9Z2CT53M2oRRf+WXHUHHgjCC+ADbiqfPO5mgZe3g=
+go.opentelemetry.io/collector/component/componentstatus v0.142.0 h1:a1KkLCtShI5SfhO2ga75VqWjjBRGgrerelt/2JXWLBI=
+go.opentelemetry.io/collector/component/componentstatus v0.142.0/go.mod h1:IRWKvFcUrFrkz1gJEV+cKAdE2ZBT128gk1sHt0OzKI4=
+go.opentelemetry.io/collector/component/componenttest v0.142.0 h1:a8XclEutO5dv4AnzThHK8dfqR4lDWjJKLtRNM2aVUFM=
+go.opentelemetry.io/collector/component/componenttest v0.142.0/go.mod h1:JhX/zKaEbjhFcsiV2ha2spzo24A6RL/jqNBS0svURD0=
+go.opentelemetry.io/collector/confmap v1.48.0 h1:vGhg25NEUX5DiYziJEw2siwdzsvtXBRZVuYyLVinFR8=
+go.opentelemetry.io/collector/confmap v1.48.0/go.mod h1:8tJHJowmvUkJ8AHzZ6SaH61dcWbdfRE9Sd/hwsKLgRE=
+go.opentelemetry.io/collector/confmap/xconfmap v0.142.0 h1:SNfuFP8TA0PmUkx6ryY63uNjLN2HMh5VeGO++IYdPgA=
+go.opentelemetry.io/collector/confmap/xconfmap v0.142.0/go.mod h1:FXuX6B8b7Ub7qkLqloWKanmPhADL18EEkaFptcd4eDQ=
+go.opentelemetry.io/collector/consumer v1.48.0 h1:g1uroz2AA0cqnEsjqFTSZG+y8uH1gQBqqyzk8kd3QiM=
+go.opentelemetry.io/collector/consumer v1.48.0/go.mod h1:lC6PnVXBwI456SV5WtvJqE7vjCNN6DAUc8xjFQ9wUV4=
+go.opentelemetry.io/collector/consumer/consumertest v0.142.0 h1:TRt8zR57Vk1PTjtqjHOwOAMbIl+IeloHxWAuF8sWdRw=
+go.opentelemetry.io/collector/consumer/consumertest v0.142.0/go.mod h1:yq2dhMxFUlCFkRN7LES3fzsTmUDw9VaunyRAka2TEaY=
+go.opentelemetry.io/collector/consumer/xconsumer v0.142.0 h1:qOoQnLZXQ9sRLexTkkmBx3qfaOmEgco9VBPmryg5UhA=
+go.opentelemetry.io/collector/consumer/xconsumer v0.142.0/go.mod h1:oPN0yJzEpovwlWvmSaiYgtDqGuOmMMLmmg352sqZdsE=
+go.opentelemetry.io/collector/featuregate v1.48.0 h1:jiGRcl93yzUFgZVDuskMAftFraE21jANdxXTQfSQScc=
+go.opentelemetry.io/collector/featuregate v1.48.0/go.mod h1:/1bclXgP91pISaEeNulRxzzmzMTm4I5Xih2SnI4HRSo=
+go.opentelemetry.io/collector/internal/testutil v0.142.0 h1:MHnAVRimQdsfYqYHC3YuJRkIUap4VmSpJkkIT2N7jJA=
+go.opentelemetry.io/collector/internal/testutil v0.142.0/go.mod h1:YAD9EAkwh/l5asZNbEBEUCqEjoL1OKMjAMoPjPqH76c=
+go.opentelemetry.io/collector/pdata v1.48.0 h1:CKZ+9v/lGTX/cTGx2XVp8kp0E8R//60kHFCBdZudrTg=
+go.opentelemetry.io/collector/pdata v1.48.0/go.mod h1:jaf2JQGpfUreD1TOtGBPsq00ecOqM66NG15wALmdxKA=
+go.opentelemetry.io/collector/pdata/pprofile v0.142.0 h1:Ivyw7WY8SIIWqzXsnNmjEgz3ysVs/OkIf0KIpJUnuuo=
+go.opentelemetry.io/collector/pdata/pprofile v0.142.0/go.mod h1:94GAph54K4WDpYz9xirhroHB3ptNLuPiY02k8fyoNUI=
+go.opentelemetry.io/collector/pdata/testdata v0.142.0 h1:+jf9RyLWl8WyhIVjpg7yuH+bRdQH4mW20cPtCMlY1cI=
+go.opentelemetry.io/collector/pdata/testdata v0.142.0/go.mod h1:kgAu5ZLEcVuPH3RFiHDg23RGitgm1M0cUAVwiGX4SB8=
+go.opentelemetry.io/collector/pipeline v1.48.0 h1:E4zyQ7+4FTGvdGS4pruUnItuyRTGhN0Qqk1CN71lfW0=
+go.opentelemetry.io/collector/pipeline v1.48.0/go.mod h1:xUrAqiebzYbrgxyoXSkk6/Y3oi5Sy3im2iCA51LwUAI=
+go.opentelemetry.io/collector/processor v1.48.0 h1:3Kttw79mnrf463QKJGoGZzFfiNzQuMWK0p2nHuvOhaQ=
+go.opentelemetry.io/collector/processor v1.48.0/go.mod h1:A3OsW6ga+a48J1mrnVNH5L5kB0v+n9nVFlmOQB5/Jwk=
+go.opentelemetry.io/collector/processor/processortest v0.142.0 h1:wQnJeXDejBL6r8ov66AYAGf8Q0/JspjuqAjPVBdCUoI=
+go.opentelemetry.io/collector/processor/processortest v0.142.0/go.mod h1:QU5SWj0L+92MSvQxZDjwWCsKssNDm+nD6SHn7IvviUE=
+go.opentelemetry.io/collector/processor/xprocessor v0.142.0 h1:7a1Crxrd5iBMVnebTxkcqxVkRHAlOBUUmNTUVUTnlCU=
+go.opentelemetry.io/collector/processor/xprocessor v0.142.0/go.mod h1:LY/GS2DiJILJKS3ynU3eOLLWSP8CmN1FtdpAMsVV8AU=
go.opentelemetry.io/collector/semconv v0.128.0 h1:MzYOz7Vgb3Kf5D7b49pqqgeUhEmOCuT10bIXb/Cc+k4=
go.opentelemetry.io/collector/semconv v0.128.0/go.mod h1:OPXer4l43X23cnjLXIZnRj/qQOjSuq4TgBLI76P9hns=
go.opentelemetry.io/contrib/detectors/gcp v1.39.0 h1:kWRNZMsfBHZ+uHjiH4y7Etn2FK26LAGkNFw7RHv1DhE=
@@ -1112,12 +1114,12 @@ go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLh
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
go.opentelemetry.io/proto/otlp v1.9.0 h1:l706jCMITVouPOqEnii2fIAuO3IVGBRPV5ICjceRb/A=
go.opentelemetry.io/proto/otlp v1.9.0/go.mod h1:xE+Cx5E/eEHw+ISFkwPLwCZefwVjY+pqKg1qcK03+/4=
-go.opentelemetry.io/proto/slim/otlp v1.8.0 h1:afcLwp2XOeCbGrjufT1qWyruFt+6C9g5SOuymrSPUXQ=
-go.opentelemetry.io/proto/slim/otlp v1.8.0/go.mod h1:Yaa5fjYm1SMCq0hG0x/87wV1MP9H5xDuG/1+AhvBcsI=
-go.opentelemetry.io/proto/slim/otlp/collector/profiles/v1development v0.1.0 h1:Uc+elixz922LHx5colXGi1ORbsW8DTIGM+gg+D9V7HE=
-go.opentelemetry.io/proto/slim/otlp/collector/profiles/v1development v0.1.0/go.mod h1:VyU6dTWBWv6h9w/+DYgSZAPMabWbPTFTuxp25sM8+s0=
-go.opentelemetry.io/proto/slim/otlp/profiles/v1development v0.1.0 h1:i8YpvWGm/Uq1koL//bnbJ/26eV3OrKWm09+rDYo7keU=
-go.opentelemetry.io/proto/slim/otlp/profiles/v1development v0.1.0/go.mod h1:pQ70xHY/ZVxNUBPn+qUWPl8nwai87eWdqL3M37lNi9A=
+go.opentelemetry.io/proto/slim/otlp v1.9.0 h1:fPVMv8tP3TrsqlkH1HWYUpbCY9cAIemx184VGkS6vlE=
+go.opentelemetry.io/proto/slim/otlp v1.9.0/go.mod h1:xXdeJJ90Gqyll+orzUkY4bOd2HECo5JofeoLpymVqdI=
+go.opentelemetry.io/proto/slim/otlp/collector/profiles/v1development v0.2.0 h1:o13nadWDNkH/quoDomDUClnQBpdQQ2Qqv0lQBjIXjE8=
+go.opentelemetry.io/proto/slim/otlp/collector/profiles/v1development v0.2.0/go.mod h1:Gyb6Xe7FTi/6xBHwMmngGoHqL0w29Y4eW8TGFzpefGA=
+go.opentelemetry.io/proto/slim/otlp/profiles/v1development v0.2.0 h1:EiUYvtwu6PMrMHVjcPfnsG3v+ajPkbUeH+IL93+QYyk=
+go.opentelemetry.io/proto/slim/otlp/profiles/v1development v0.2.0/go.mod h1:mUUHKFiN2SST3AhJ8XhJxEoeVW12oqfXog0Bo8W3Ec4=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
@@ -1131,8 +1133,8 @@ go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=
-go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
-go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
+go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc=
+go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0=
go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8=
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
@@ -1491,8 +1493,8 @@ google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRR
google.golang.org/api v0.75.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA=
google.golang.org/api v0.78.0/go.mod h1:1Sg78yoMLOhlQTeF+ARBoytAcH1NNyyl390YMy6rKmw=
google.golang.org/api v0.81.0/go.mod h1:FA6Mb/bZxj706H2j+j2d6mHEEaHBmbbWnkfvmorOCko=
-google.golang.org/api v0.252.0 h1:xfKJeAJaMwb8OC9fesr369rjciQ704AjU/psjkKURSI=
-google.golang.org/api v0.252.0/go.mod h1:dnHOv81x5RAmumZ7BWLShB/u7JZNeyalImxHmtTHxqw=
+google.golang.org/api v0.257.0 h1:8Y0lzvHlZps53PEaw+G29SsQIkuKrumGWs9puiexNAA=
+google.golang.org/api v0.257.0/go.mod h1:4eJrr+vbVaZSqs7vovFd1Jb/A6ml6iw2e6FBYf3GAO4=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
@@ -1677,12 +1679,12 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
-k8s.io/api v0.34.1 h1:jC+153630BMdlFukegoEL8E/yT7aLyQkIVuwhmwDgJM=
-k8s.io/api v0.34.1/go.mod h1:SB80FxFtXn5/gwzCoN6QCtPD7Vbu5w2n1S0J5gFfTYk=
-k8s.io/apimachinery v0.34.1 h1:dTlxFls/eikpJxmAC7MVE8oOeP1zryV7iRyIjB0gky4=
-k8s.io/apimachinery v0.34.1/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw=
-k8s.io/client-go v0.34.1 h1:ZUPJKgXsnKwVwmKKdPfw4tB58+7/Ik3CrjOEhsiZ7mY=
-k8s.io/client-go v0.34.1/go.mod h1:kA8v0FP+tk6sZA0yKLRG67LWjqufAoSHA2xVGKw9Of8=
+k8s.io/api v0.34.3 h1:D12sTP257/jSH2vHV2EDYrb16bS7ULlHpdNdNhEw2S4=
+k8s.io/api v0.34.3/go.mod h1:PyVQBF886Q5RSQZOim7DybQjAbVs8g7gwJNhGtY5MBk=
+k8s.io/apimachinery v0.34.3 h1:/TB+SFEiQvN9HPldtlWOTp0hWbJ+fjU+wkxysf/aQnE=
+k8s.io/apimachinery v0.34.3/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw=
+k8s.io/client-go v0.34.3 h1:wtYtpzy/OPNYf7WyNBTj3iUA0XaBHVqhv4Iv3tbrF5A=
+k8s.io/client-go v0.34.3/go.mod h1:OxxeYagaP9Kdf78UrKLa3YZixMCfP6bgPwPwNBQBzpM=
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b h1:MloQ9/bdJyIu9lb1PzujOPolHyvO06MXG5TUIj2mNAA=
diff --git a/integration/e2e/images/images.go b/integration/e2e/images/images.go
index 0cf845f976d..c5fd5999ce8 100644
--- a/integration/e2e/images/images.go
+++ b/integration/e2e/images/images.go
@@ -11,5 +11,5 @@ var (
Minio = "minio/minio:RELEASE.2024-05-28T17-19-04Z"
Consul = "consul:1.8.4"
ETCD = "quay.io/coreos/etcd:v3.5.29"
- Prometheus = "quay.io/prometheus/prometheus:v3.8.1"
+ Prometheus = "quay.io/prometheus/prometheus:v3.9.1"
)
diff --git a/pkg/api/handlers.go b/pkg/api/handlers.go
index 5852a65cbb1..586f52b4fe7 100644
--- a/pkg/api/handlers.go
+++ b/pkg/api/handlers.go
@@ -21,6 +21,7 @@ import (
"github.com/prometheus/common/version"
"github.com/prometheus/prometheus/config"
"github.com/prometheus/prometheus/storage"
+ "github.com/prometheus/prometheus/util/features"
v1 "github.com/prometheus/prometheus/web/api/v1"
"github.com/weaveworks/common/instrument"
"github.com/weaveworks/common/middleware"
@@ -245,6 +246,7 @@ func NewQuerierHandler(
false,
false,
nil,
+ features.NewRegistry(),
)
// Let's clear all codecs to create the instrumented ones
api.ClearCodecs()
diff --git a/pkg/ingester/ingester.go b/pkg/ingester/ingester.go
index 6116a318992..d2361872851 100644
--- a/pkg/ingester/ingester.go
+++ b/pkg/ingester/ingester.go
@@ -1474,8 +1474,7 @@ func (i *Ingester) Push(ctx context.Context, req *cortexpb.WriteRequest) (*corte
var err error
if s.StartTimestampMs != 0 && s.TimestampMs != 0 {
- // TODO(SungJin1212): Change to AppendSTZeroSample after update the Prometheus v3.9.0+
- if _, err = app.AppendCTZeroSample(ref, copiedLabels, s.TimestampMs, s.StartTimestampMs); err != nil && !errors.Is(err, storage.ErrOutOfOrderCT) {
+ if _, err = app.AppendSTZeroSample(ref, copiedLabels, s.TimestampMs, s.StartTimestampMs); err != nil && !errors.Is(err, storage.ErrOutOfOrderST) {
startTimestampSampleAppendFailCount++
i.metrics.startTimestampFail.WithLabelValues(sampleMetricTypeFloat).Inc()
}
@@ -1528,8 +1527,7 @@ func (i *Ingester) Push(ctx context.Context, req *cortexpb.WriteRequest) (*corte
}
if hp.StartTimestampMs != 0 && hp.TimestampMs != 0 {
- // TODO(SungJin1212): Change to AppendHistogramSTZeroSample after update the Prometheus v3.9.0+
- if _, err = app.AppendHistogramCTZeroSample(ref, copiedLabels, hp.TimestampMs, hp.StartTimestampMs, h, fh); err != nil && !errors.Is(err, storage.ErrOutOfOrderCT) {
+ if _, err = app.AppendHistogramSTZeroSample(ref, copiedLabels, hp.TimestampMs, hp.StartTimestampMs, h, fh); err != nil && !errors.Is(err, storage.ErrOutOfOrderST) {
startTimestampHistogramAppendFailCount++
i.metrics.startTimestampFail.WithLabelValues(sampleMetricTypeHistogram).Inc()
}
@@ -2953,6 +2951,7 @@ func (i *Ingester) createTSDB(userID string) (*userTSDB, error) {
OutOfOrderCapMax: i.cfg.BlocksStorageConfig.TSDB.OutOfOrderCapMax,
EnableOverlappingCompaction: false, // Always let compactors handle overlapped blocks, e.g. OOO blocks.
BlockChunkQuerierFunc: i.blockChunkQuerierFunc(userID),
+ BlockReloadInterval: 1 * time.Minute, // use default value
}, nil)
if err != nil {
return nil, errors.Wrapf(err, "failed to open TSDB: %s", udir)
diff --git a/pkg/querier/error_translate_queryable_test.go b/pkg/querier/error_translate_queryable_test.go
index 03b11368d30..ef9f04c70c2 100644
--- a/pkg/querier/error_translate_queryable_test.go
+++ b/pkg/querier/error_translate_queryable_test.go
@@ -19,6 +19,7 @@ import (
"github.com/prometheus/prometheus/promql"
"github.com/prometheus/prometheus/storage"
"github.com/prometheus/prometheus/util/annotations"
+ "github.com/prometheus/prometheus/util/features"
v1 "github.com/prometheus/prometheus/web/api/v1"
"github.com/stretchr/testify/require"
"github.com/weaveworks/common/httpgrpc"
@@ -193,6 +194,7 @@ func createPrometheusAPI(q storage.SampleAndChunkQueryable, engine promql.QueryE
false,
false,
nil,
+ features.NewRegistry(),
)
promRouter := route.New().WithPrefix("/api/v1")
diff --git a/pkg/querier/stats_renderer_test.go b/pkg/querier/stats_renderer_test.go
index 9021faebb75..2969d832972 100644
--- a/pkg/querier/stats_renderer_test.go
+++ b/pkg/querier/stats_renderer_test.go
@@ -15,6 +15,7 @@ import (
"github.com/prometheus/prometheus/config"
"github.com/prometheus/prometheus/promql"
"github.com/prometheus/prometheus/storage"
+ "github.com/prometheus/prometheus/util/features"
v1 "github.com/prometheus/prometheus/web/api/v1"
"github.com/stretchr/testify/assert"
"github.com/weaveworks/common/user"
@@ -95,6 +96,7 @@ func Test_StatsRenderer(t *testing.T) {
false,
false,
nil,
+ features.NewRegistry(),
)
promRouter := route.New().WithPrefix("/api/v1")
diff --git a/pkg/ruler/compat.go b/pkg/ruler/compat.go
index ff9a5995b2b..97ad799b275 100644
--- a/pkg/ruler/compat.go
+++ b/pkg/ruler/compat.go
@@ -80,13 +80,13 @@ func (a *PusherAppender) SetOptions(opts *storage.AppendOptions) {
a.opts = opts
}
-func (a *PusherAppender) AppendHistogramCTZeroSample(ref storage.SeriesRef, l labels.Labels, t, ct int64, h *histogram.Histogram, fh *histogram.FloatHistogram) (storage.SeriesRef, error) {
- // AppendHistogramCTZeroSample is a no-op for PusherAppender as it happens during scrape time only.
+func (a *PusherAppender) AppendHistogramSTZeroSample(ref storage.SeriesRef, l labels.Labels, t, ct int64, h *histogram.Histogram, fh *histogram.FloatHistogram) (storage.SeriesRef, error) {
+ // AppendHistogramSTZeroSample is a no-op for PusherAppender as it happens during scrape time only.
return 0, nil
}
-func (a *PusherAppender) AppendCTZeroSample(_ storage.SeriesRef, _ labels.Labels, _, _ int64) (storage.SeriesRef, error) {
- // AppendCTZeroSample is a no-op for PusherAppender as it happens during scrape time only.
+func (a *PusherAppender) AppendSTZeroSample(_ storage.SeriesRef, _ labels.Labels, _, _ int64) (storage.SeriesRef, error) {
+ // AppendSTZeroSample is a no-op for PusherAppender as it happens during scrape time only.
return 0, nil
}
diff --git a/pkg/util/validation/validate.go b/pkg/util/validation/validate.go
index a2f7bc395df..31c4b1f34cd 100644
--- a/pkg/util/validation/validate.go
+++ b/pkg/util/validation/validate.go
@@ -432,7 +432,10 @@ func ValidateNativeHistogram(validateMetrics *ValidateMetrics, limits *Limits, u
validateMetrics.DiscardedSamples.WithLabelValues(nativeHistogramBucketCountLimitExceeded, userID).Inc()
return cortexpb.Histogram{}, newHistogramBucketLimitExceededError(ls, limits.MaxNativeHistogramBuckets)
}
- fh = fh.ReduceResolution(fh.Schema - 1)
+ if err := fh.ReduceResolution(fh.Schema - 1); err != nil {
+ validateMetrics.DiscardedSamples.WithLabelValues(nativeHistogramInvalid, userID).Inc()
+ return cortexpb.Histogram{}, newNativeHistogramInvalidError(ls, err)
+ }
}
if oBuckets != len(fh.PositiveBuckets)+len(fh.NegativeBuckets) {
validateMetrics.HistogramSamplesReducedResolution.WithLabelValues(userID).Inc()
@@ -476,7 +479,10 @@ func ValidateNativeHistogram(validateMetrics *ValidateMetrics, limits *Limits, u
validateMetrics.DiscardedSamples.WithLabelValues(nativeHistogramBucketCountLimitExceeded, userID).Inc()
return cortexpb.Histogram{}, newHistogramBucketLimitExceededError(ls, limits.MaxNativeHistogramBuckets)
}
- h = h.ReduceResolution(h.Schema - 1)
+ if err := h.ReduceResolution(h.Schema - 1); err != nil {
+ validateMetrics.DiscardedSamples.WithLabelValues(nativeHistogramInvalid, userID).Inc()
+ return cortexpb.Histogram{}, newNativeHistogramInvalidError(ls, err)
+ }
}
if oBuckets != len(h.PositiveBuckets)+len(h.NegativeBuckets) {
validateMetrics.HistogramSamplesReducedResolution.WithLabelValues(userID).Inc()
diff --git a/pkg/util/validation/validate_test.go b/pkg/util/validation/validate_test.go
index 6832ee61f0e..6a55e735a9a 100644
--- a/pkg/util/validation/validate_test.go
+++ b/pkg/util/validation/validate_test.go
@@ -509,33 +509,49 @@ func TestValidateNativeHistogram(t *testing.T) {
discardedSampleLabelValue: nativeHistogramBucketCountLimitExceeded,
},
{
- name: "exceed limit and reduce resolution for 1 level, histogram",
- bucketLimit: 6,
- histogram: cortexpb.HistogramToHistogramProto(0, h.Copy()),
- expectedHistogram: cortexpb.HistogramToHistogramProto(0, h.Copy().ReduceResolution(0)),
+ name: "exceed limit and reduce resolution for 1 level, histogram",
+ bucketLimit: 6,
+ histogram: cortexpb.HistogramToHistogramProto(0, h.Copy()),
+ expectedHistogram: func() cortexpb.Histogram {
+ hc := h.Copy()
+ require.NoError(t, hc.ReduceResolution(0))
+ return cortexpb.HistogramToHistogramProto(0, hc)
+ }(),
resolutionReduced: true,
discardedSampleLabelValue: nativeHistogramBucketCountLimitExceeded,
},
{
- name: "exceed limit and reduce resolution for 1 level, float histogram",
- bucketLimit: 6,
- histogram: cortexpb.FloatHistogramToHistogramProto(0, fh.Copy()),
- expectedHistogram: cortexpb.FloatHistogramToHistogramProto(0, fh.Copy().ReduceResolution(0)),
+ name: "exceed limit and reduce resolution for 1 level, float histogram",
+ bucketLimit: 6,
+ histogram: cortexpb.FloatHistogramToHistogramProto(0, fh.Copy()),
+ expectedHistogram: func() cortexpb.Histogram {
+ fhc := fh.Copy()
+ require.NoError(t, fhc.ReduceResolution(0))
+ return cortexpb.FloatHistogramToHistogramProto(0, fhc)
+ }(),
resolutionReduced: true,
discardedSampleLabelValue: nativeHistogramBucketCountLimitExceeded,
},
{
- name: "exceed limit and reduce resolution for 2 levels, histogram",
- bucketLimit: 4,
- histogram: cortexpb.HistogramToHistogramProto(0, h.Copy()),
- expectedHistogram: cortexpb.HistogramToHistogramProto(0, h.Copy().ReduceResolution(-1)),
+ name: "exceed limit and reduce resolution for 2 levels, histogram",
+ bucketLimit: 4,
+ histogram: cortexpb.HistogramToHistogramProto(0, h.Copy()),
+ expectedHistogram: func() cortexpb.Histogram {
+ hc := h.Copy()
+ require.NoError(t, hc.ReduceResolution(-1))
+ return cortexpb.HistogramToHistogramProto(0, hc)
+ }(),
discardedSampleLabelValue: nativeHistogramBucketCountLimitExceeded,
},
{
- name: "exceed limit and reduce resolution for 2 levels, float histogram",
- bucketLimit: 4,
- histogram: cortexpb.FloatHistogramToHistogramProto(0, fh.Copy()),
- expectedHistogram: cortexpb.FloatHistogramToHistogramProto(0, fh.Copy().ReduceResolution(-1)),
+ name: "exceed limit and reduce resolution for 2 levels, float histogram",
+ bucketLimit: 4,
+ histogram: cortexpb.FloatHistogramToHistogramProto(0, fh.Copy()),
+ expectedHistogram: func() cortexpb.Histogram {
+ fhc := fh.Copy()
+ require.NoError(t, fhc.ReduceResolution(-1))
+ return cortexpb.FloatHistogramToHistogramProto(0, fhc)
+ }(),
discardedSampleLabelValue: nativeHistogramBucketCountLimitExceeded,
},
{
diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/CHANGELOG.md b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/CHANGELOG.md
index 1799c6ef223..47d2b85fa86 100644
--- a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/CHANGELOG.md
+++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/CHANGELOG.md
@@ -1,5 +1,16 @@
# Release History
+## 1.20.0 (2025-11-06)
+
+### Features Added
+
+* Added `runtime.FetcherForNextLinkOptions.HTTPVerb` to specify the HTTP verb when fetching the next page via next link. Defaults to `http.MethodGet`.
+
+### Bugs Fixed
+
+* Fixed potential panic when decoding base64 strings.
+* Fixed an issue in resource identifier parsing which prevented it from returning an error for malformed resource IDs.
+
## 1.19.1 (2025-09-11)
### Bugs Fixed
diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/arm/internal/resource/resource_identifier.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/arm/internal/resource/resource_identifier.go
index b8348b7d82e..8a40ebe4d2f 100644
--- a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/arm/internal/resource/resource_identifier.go
+++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/arm/internal/resource/resource_identifier.go
@@ -217,6 +217,7 @@ func appendNext(parent *ResourceID, parts []string, id string) (*ResourceID, err
func splitStringAndOmitEmpty(v, sep string) []string {
r := make([]string, 0)
for _, s := range strings.Split(v, sep) {
+ s = strings.TrimSpace(s)
if len(s) == 0 {
continue
}
diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/exported/exported.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/exported/exported.go
index 460170034aa..612af11ac61 100644
--- a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/exported/exported.go
+++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/exported/exported.go
@@ -92,7 +92,7 @@ func DecodeByteArray(s string, v *[]byte, format Base64Encoding) error {
return nil
}
payload := string(s)
- if payload[0] == '"' {
+ if len(payload) >= 2 && payload[0] == '"' && payload[len(payload)-1] == '"' {
// remove surrounding quotes
payload = payload[1 : len(payload)-1]
}
diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/shared/constants.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/shared/constants.go
index 8aebe5ce53b..f152000913d 100644
--- a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/shared/constants.go
+++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/shared/constants.go
@@ -40,5 +40,5 @@ const (
Module = "azcore"
// Version is the semantic version (see http://semver.org) of this module.
- Version = "v1.19.1"
+ Version = "v1.20.0"
)
diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/pager.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/pager.go
index c66fc0a90a5..edb4a3cd44f 100644
--- a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/pager.go
+++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/pager.go
@@ -99,6 +99,11 @@ type FetcherForNextLinkOptions struct {
// StatusCodes contains additional HTTP status codes indicating success.
// The default value is http.StatusOK.
StatusCodes []int
+
+ // HTTPVerb specifies the HTTP verb to use when fetching the next page.
+ // The default value is http.MethodGet.
+ // This field is only used when NextReq is not specified.
+ HTTPVerb string
}
// FetcherForNextLink is a helper containing boilerplate code to simplify creating a PagingHandler[T].Fetcher from a next link URL.
@@ -119,7 +124,11 @@ func FetcherForNextLink(ctx context.Context, pl Pipeline, nextLink string, first
if options.NextReq != nil {
req, err = options.NextReq(ctx, nextLink)
} else {
- req, err = NewRequest(ctx, http.MethodGet, nextLink)
+ verb := http.MethodGet
+ if options.HTTPVerb != "" {
+ verb = options.HTTPVerb
+ }
+ req, err = NewRequest(ctx, verb, nextLink)
}
}
if err != nil {
diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/CHANGELOG.md b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/CHANGELOG.md
index ab63f9c031b..4a6349e1678 100644
--- a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/CHANGELOG.md
+++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/CHANGELOG.md
@@ -1,5 +1,34 @@
# Release History
+## 1.13.1 (2025-11-10)
+
+### Bugs Fixed
+
+- `AzureCLICredential` quoted arguments incorrectly on Windows
+
+## 1.13.0 (2025-10-07)
+
+### Features Added
+
+- Added `AzurePowerShellCredential`, which authenticates as the identity logged in to Azure PowerShell
+ (thanks [ArmaanMcleod](https://github.com/ArmaanMcleod))
+- When `AZURE_TOKEN_CREDENTIALS` is set to `ManagedIdentityCredential`, `DefaultAzureCredential` behaves the same as
+ does `ManagedIdentityCredential` when used directly. It doesn't apply special retry configuration or attempt to
+ determine whether IMDS is available. ([#25265](https://github.com/Azure/azure-sdk-for-go/issues/25265))
+
+### Breaking Changes
+
+* Removed the `WorkloadIdentityCredential` support for identity binding mode added in v1.13.0-beta.1.
+ It will return in v1.14.0-beta.1
+
+## 1.13.0-beta.1 (2025-09-17)
+
+### Features Added
+
+- Added `AzurePowerShellCredential`, which authenticates as the identity logged in to Azure PowerShell
+ (thanks [ArmaanMcleod](https://github.com/ArmaanMcleod))
+- `WorkloadIdentityCredential` supports identity binding mode ([#25056](https://github.com/Azure/azure-sdk-for-go/issues/25056))
+
## 1.12.0 (2025-09-16)
### Features Added
diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/README.md b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/README.md
index 069bc688d52..127c25b72cf 100644
--- a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/README.md
+++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/README.md
@@ -1,6 +1,6 @@
# Azure Identity Client Module for Go
-The Azure Identity module provides Microsoft Entra ID ([formerly Azure Active Directory](https://learn.microsoft.com/entra/fundamentals/new-name)) token authentication support across the Azure SDK. It includes a set of `TokenCredential` implementations, which can be used with Azure SDK clients supporting token authentication.
+The Azure Identity module provides [Microsoft Entra ID](https://learn.microsoft.com/entra/fundamentals/whatis) token-based authentication support across the Azure SDK. It includes a set of `TokenCredential` implementations, which can be used with Azure SDK clients supporting token authentication.
[](https://pkg.go.dev/github.com/Azure/azure-sdk-for-go/sdk/azidentity)
| [Microsoft Entra ID documentation](https://learn.microsoft.com/entra/identity/)
@@ -153,6 +153,7 @@ client := armresources.NewResourceGroupsClient("subscription ID", chain, nil)
|-|-
|[AzureCLICredential](https://pkg.go.dev/github.com/Azure/azure-sdk-for-go/sdk/azidentity#AzureCLICredential)|Authenticate as the user signed in to the Azure CLI
|[AzureDeveloperCLICredential](https://pkg.go.dev/github.com/Azure/azure-sdk-for-go/sdk/azidentity#AzureDeveloperCLICredential)|Authenticates as the user signed in to the Azure Developer CLI
+|[AzurePowerShellCredential](https://pkg.go.dev/github.com/Azure/azure-sdk-for-go/sdk/azidentity#AzurePowerShellCredential)|Authenticates as the user signed in to Azure PowerShell
## Environment Variables
diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/TOKEN_CACHING.MD b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/TOKEN_CACHING.MD
index da2094e36b1..8bdaf816515 100644
--- a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/TOKEN_CACHING.MD
+++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/TOKEN_CACHING.MD
@@ -40,6 +40,7 @@ The following table indicates the state of in-memory and persistent caching in e
| ------------------------------ | ------------------------------------------------------------------- | ------------------------ |
| `AzureCLICredential` | Not Supported | Not Supported |
| `AzureDeveloperCLICredential` | Not Supported | Not Supported |
+| `AzurePowerShellCredential` | Not Supported | Not Supported |
| `AzurePipelinesCredential` | Supported | Supported |
| `ClientAssertionCredential` | Supported | Supported |
| `ClientCertificateCredential` | Supported | Supported |
diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/TROUBLESHOOTING.md b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/TROUBLESHOOTING.md
index 838601d69c8..517006a424f 100644
--- a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/TROUBLESHOOTING.md
+++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/TROUBLESHOOTING.md
@@ -12,6 +12,7 @@ This troubleshooting guide covers failure investigation techniques, common error
- [Troubleshoot AzureCLICredential authentication issues](#troubleshoot-azureclicredential-authentication-issues)
- [Troubleshoot AzureDeveloperCLICredential authentication issues](#troubleshoot-azuredeveloperclicredential-authentication-issues)
- [Troubleshoot AzurePipelinesCredential authentication issues](#troubleshoot-azurepipelinescredential-authentication-issues)
+- [Troubleshoot AzurePowerShellCredential authentication issues](#troubleshoot-azurepowershellcredential-authentication-issues)
- [Troubleshoot ClientCertificateCredential authentication issues](#troubleshoot-clientcertificatecredential-authentication-issues)
- [Troubleshoot ClientSecretCredential authentication issues](#troubleshoot-clientsecretcredential-authentication-issues)
- [Troubleshoot DefaultAzureCredential authentication issues](#troubleshoot-defaultazurecredential-authentication-issues)
@@ -205,6 +206,34 @@ azd auth token --output json --scope https://management.core.windows.net/.defaul
```
>Note that output of this command will contain a valid access token, and SHOULD NOT BE SHARED to avoid compromising account security.
+
+## Troubleshoot `AzurePowerShellCredential` authentication issues
+
+| Error Message |Description| Mitigation |
+|---|---|---|
+|executable not found on path|No local installation of PowerShell was found.|Ensure that PowerShell is properly installed on the machine. Instructions for installing PowerShell can be found [here](https://learn.microsoft.com/powershell/scripting/install/installing-powershell).|
+|Az.Accounts module not found|The Az.Account module needed for authentication in Azure PowerShell isn't installed.|Install the latest Az.Account module. Installation instructions can be found [here](https://learn.microsoft.com/powershell/azure/install-az-ps).|
+|Please run "Connect-AzAccount" to set up account.|No account is currently logged into Azure PowerShell.|
- Log in to Azure PowerShell using the `Connect-AzAccount` command. More instructions for authenticating Azure PowerShell can be found at [Sign in with Azure PowerShell](https://learn.microsoft.com/powershell/azure/authenticate-azureps).
- Validate that Azure PowerShell can obtain tokens. For instructions, see [Verify Azure PowerShell can obtain tokens](#verify-azure-powershell-can-obtain-tokens).
|
+
+#### __Verify Azure PowerShell can obtain tokens__
+
+You can manually verify that Azure PowerShell is authenticated and can obtain tokens. First, use the `Get-AzContext` command to verify the account that is currently logged in to Azure PowerShell.
+
+```
+PS C:\> Get-AzContext
+
+Name Account SubscriptionName Environment TenantId
+---- ------- ---------------- ----------- --------
+Subscription1 (xxxxxxxx-xxxx-xxxx-xxx... test@outlook.com Subscription1 AzureCloud xxxxxxxx-x...
+```
+
+Once you've verified Azure PowerShell is using correct account, validate that it's able to obtain tokens for this account:
+
+```bash
+Get-AzAccessToken -ResourceUrl "https://management.core.windows.net"
+```
+>Note that output of this command will contain a valid access token, and SHOULD NOT BE SHARED to avoid compromising account security.
+
## Troubleshoot `WorkloadIdentityCredential` authentication issues
diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/azure_powershell_credential.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/azure_powershell_credential.go
new file mode 100644
index 00000000000..0829655545f
--- /dev/null
+++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/azure_powershell_credential.go
@@ -0,0 +1,234 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package azidentity
+
+import (
+ "context"
+ "encoding/base64"
+ "encoding/binary"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "os/exec"
+ "runtime"
+ "strings"
+ "sync"
+ "time"
+ "unicode/utf16"
+
+ "github.com/Azure/azure-sdk-for-go/sdk/azcore"
+ "github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
+ "github.com/Azure/azure-sdk-for-go/sdk/internal/log"
+)
+
+const (
+ credNameAzurePowerShell = "AzurePowerShellCredential"
+ noAzAccountModule = "Az.Accounts module not found"
+)
+
+// AzurePowerShellCredentialOptions contains optional parameters for AzurePowerShellCredential.
+type AzurePowerShellCredentialOptions struct {
+ // AdditionallyAllowedTenants specifies tenants to which the credential may authenticate, in addition to
+ // TenantID. When TenantID is empty, this option has no effect and the credential will authenticate to
+ // any requested tenant. Add the wildcard value "*" to allow the credential to authenticate to any tenant.
+ AdditionallyAllowedTenants []string
+
+ // TenantID identifies the tenant the credential should authenticate in.
+ // Defaults to Azure PowerShell's default tenant, which is typically the home tenant of the logged in user.
+ TenantID string
+
+ // inDefaultChain is true when the credential is part of DefaultAzureCredential
+ inDefaultChain bool
+
+ // exec is used by tests to fake invoking Azure PowerShell
+ exec executor
+}
+
+// AzurePowerShellCredential authenticates as the identity logged in to Azure PowerShell.
+type AzurePowerShellCredential struct {
+ mu *sync.Mutex
+ opts AzurePowerShellCredentialOptions
+}
+
+// NewAzurePowerShellCredential constructs an AzurePowerShellCredential. Pass nil to accept default options.
+func NewAzurePowerShellCredential(options *AzurePowerShellCredentialOptions) (*AzurePowerShellCredential, error) {
+ cp := AzurePowerShellCredentialOptions{}
+
+ if options != nil {
+ cp = *options
+ }
+
+ if cp.TenantID != "" && !validTenantID(cp.TenantID) {
+ return nil, errInvalidTenantID
+ }
+
+ if cp.exec == nil {
+ cp.exec = shellExec
+ }
+
+ cp.AdditionallyAllowedTenants = resolveAdditionalTenants(cp.AdditionallyAllowedTenants)
+
+ return &AzurePowerShellCredential{mu: &sync.Mutex{}, opts: cp}, nil
+}
+
+// GetToken requests a token from Azure PowerShell. This credential doesn't cache tokens, so every call invokes Azure PowerShell.
+// This method is called automatically by Azure SDK clients.
+func (c *AzurePowerShellCredential) GetToken(ctx context.Context, opts policy.TokenRequestOptions) (azcore.AccessToken, error) {
+ at := azcore.AccessToken{}
+
+ if len(opts.Scopes) != 1 {
+ return at, errors.New(credNameAzurePowerShell + ": GetToken() requires exactly one scope")
+ }
+
+ if !validScope(opts.Scopes[0]) {
+ return at, fmt.Errorf("%s.GetToken(): invalid scope %q", credNameAzurePowerShell, opts.Scopes[0])
+ }
+
+ tenant, err := resolveTenant(c.opts.TenantID, opts.TenantID, credNameAzurePowerShell, c.opts.AdditionallyAllowedTenants)
+ if err != nil {
+ return at, err
+ }
+
+ // Always pass a Microsoft Entra ID v1 resource URI (not a v2 scope) because Get-AzAccessToken only supports v1 resource URIs.
+ resource := strings.TrimSuffix(opts.Scopes[0], defaultSuffix)
+
+ tenantArg := ""
+ if tenant != "" {
+ tenantArg = fmt.Sprintf(" -TenantId '%s'", tenant)
+ }
+
+ if opts.Claims != "" {
+ encoded := base64.StdEncoding.EncodeToString([]byte(opts.Claims))
+ return at, fmt.Errorf(
+ "%s.GetToken(): Azure PowerShell requires multifactor authentication or additional claims. Run this command then retry the operation: Connect-AzAccount%s -ClaimsChallenge '%s'",
+ credNameAzurePowerShell,
+ tenantArg,
+ encoded,
+ )
+ }
+
+ // Inline script to handle Get-AzAccessToken differences between Az.Accounts versions with SecureString handling and minimum version requirement
+ script := fmt.Sprintf(`
+$ErrorActionPreference = 'Stop'
+[version]$minimumVersion = '2.2.0'
+
+$mod = Import-Module Az.Accounts -MinimumVersion $minimumVersion -PassThru -ErrorAction SilentlyContinue
+
+if (-not $mod) {
+ Write-Error '%s'
+}
+
+$params = @{
+ ResourceUrl = '%s'
+ WarningAction = 'Ignore'
+}
+
+# Only force AsSecureString for Az.Accounts versions > 2.17.0 and < 5.0.0 which return plain text token by default.
+# Newer Az.Accounts versions return SecureString token by default and no longer use AsSecureString parameter.
+if ($mod.Version -ge [version]'2.17.0' -and $mod.Version -lt [version]'5.0.0') {
+ $params['AsSecureString'] = $true
+}
+
+$tenantId = '%s'
+if ($tenantId.Length -gt 0) {
+ $params['TenantId'] = '%s'
+}
+
+$token = Get-AzAccessToken @params
+
+$customToken = New-Object -TypeName psobject
+
+# The following .NET interop pattern is supported in all PowerShell versions and safely converts SecureString to plain text.
+if ($token.Token -is [System.Security.SecureString]) {
+ $ssPtr = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($token.Token)
+ try {
+ $plainToken = [System.Runtime.InteropServices.Marshal]::PtrToStringBSTR($ssPtr)
+ } finally {
+ [System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($ssPtr)
+ }
+ $customToken | Add-Member -MemberType NoteProperty -Name Token -Value $plainToken
+} else {
+ $customToken | Add-Member -MemberType NoteProperty -Name Token -Value $token.Token
+}
+$customToken | Add-Member -MemberType NoteProperty -Name ExpiresOn -Value $token.ExpiresOn.ToUnixTimeSeconds()
+
+$jsonToken = $customToken | ConvertTo-Json
+return $jsonToken
+`, noAzAccountModule, resource, tenant, tenant)
+
+ // Windows: prefer pwsh.exe (PowerShell Core), fallback to powershell.exe (Windows PowerShell)
+ // Unix: only support pwsh (PowerShell Core)
+ exe := "pwsh"
+ if runtime.GOOS == "windows" {
+ if _, err := exec.LookPath("pwsh.exe"); err == nil {
+ exe = "pwsh.exe"
+ } else {
+ exe = "powershell.exe"
+ }
+ }
+
+ command := exe + " -NoProfile -NonInteractive -OutputFormat Text -EncodedCommand " + base64EncodeUTF16LE(script)
+
+ c.mu.Lock()
+ defer c.mu.Unlock()
+
+ b, err := c.opts.exec(ctx, credNameAzurePowerShell, command)
+ if err == nil {
+ at, err = c.createAccessToken(b)
+ }
+
+ if err != nil {
+ err = unavailableIfInDAC(err, c.opts.inDefaultChain)
+ return at, err
+ }
+
+ msg := fmt.Sprintf("%s.GetToken() acquired a token for scope %q", credNameAzurePowerShell, strings.Join(opts.Scopes, ", "))
+ log.Write(EventAuthentication, msg)
+
+ return at, nil
+}
+
+func (c *AzurePowerShellCredential) createAccessToken(tk []byte) (azcore.AccessToken, error) {
+ t := struct {
+ Token string `json:"Token"`
+ ExpiresOn int64 `json:"ExpiresOn"`
+ }{}
+
+ err := json.Unmarshal(tk, &t)
+ if err != nil {
+ return azcore.AccessToken{}, err
+ }
+
+ converted := azcore.AccessToken{
+ Token: t.Token,
+ ExpiresOn: time.Unix(t.ExpiresOn, 0).UTC(),
+ }
+
+ return converted, nil
+}
+
+// Encodes a string to Base64 using UTF-16LE encoding
+func base64EncodeUTF16LE(text string) string {
+ u16 := utf16.Encode([]rune(text))
+ buf := make([]byte, len(u16)*2)
+ for i, v := range u16 {
+ binary.LittleEndian.PutUint16(buf[i*2:], v)
+ }
+ return base64.StdEncoding.EncodeToString(buf)
+}
+
+// Decodes a Base64 UTF-16LE string back to string
+func base64DecodeUTF16LE(encoded string) (string, error) {
+ data, err := base64.StdEncoding.DecodeString(encoded)
+ if err != nil {
+ return "", err
+ }
+ u16 := make([]uint16, len(data)/2)
+ for i := range u16 {
+ u16[i] = binary.LittleEndian.Uint16(data[i*2:])
+ }
+ return string(utf16.Decode(u16)), nil
+}
+
+var _ azcore.TokenCredential = (*AzurePowerShellCredential)(nil)
diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/default_azure_credential.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/default_azure_credential.go
index c041a52dbbe..aaaabc5c2f3 100644
--- a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/default_azure_credential.go
+++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/default_azure_credential.go
@@ -26,6 +26,7 @@ const (
managedIdentity
az
azd
+ azurePowerShell
)
// DefaultAzureCredentialOptions contains optional parameters for DefaultAzureCredential.
@@ -71,6 +72,7 @@ type DefaultAzureCredentialOptions struct {
// - [ManagedIdentityCredential]
// - [AzureCLICredential]
// - [AzureDeveloperCLICredential]
+// - [AzurePowerShellCredential]
//
// Consult the documentation for these credential types for more information on how they authenticate.
// Once a credential has successfully authenticated, DefaultAzureCredential will use that credential for
@@ -83,7 +85,7 @@ type DefaultAzureCredentialOptions struct {
// Valid values for AZURE_TOKEN_CREDENTIALS are the name of any single type in the above chain, for example
// "EnvironmentCredential" or "AzureCLICredential", and these special values:
//
-// - "dev": try [AzureCLICredential] and [AzureDeveloperCLICredential], in that order
+// - "dev": try [AzureCLICredential], [AzureDeveloperCLICredential], and [AzurePowerShellCredential], in that order
// - "prod": try [EnvironmentCredential], [WorkloadIdentityCredential], and [ManagedIdentityCredential], in that order
//
// [DefaultAzureCredentialOptions].RequireAzureTokenCredentials controls whether AZURE_TOKEN_CREDENTIALS must be set.
@@ -104,13 +106,13 @@ func NewDefaultAzureCredential(options *DefaultAzureCredentialOptions) (*Default
var (
creds []azcore.TokenCredential
errorMessages []string
- selected = env | workloadIdentity | managedIdentity | az | azd
+ selected = env | workloadIdentity | managedIdentity | az | azd | azurePowerShell
)
if atc, ok := os.LookupEnv(azureTokenCredentials); ok {
switch {
case atc == "dev":
- selected = az | azd
+ selected = az | azd | azurePowerShell
case atc == "prod":
selected = env | workloadIdentity | managedIdentity
case strings.EqualFold(atc, credNameEnvironment):
@@ -123,6 +125,8 @@ func NewDefaultAzureCredential(options *DefaultAzureCredentialOptions) (*Default
selected = az
case strings.EqualFold(atc, credNameAzureDeveloperCLI):
selected = azd
+ case strings.EqualFold(atc, credNameAzurePowerShell):
+ selected = azurePowerShell
default:
return nil, fmt.Errorf(`invalid %s value %q. Valid values are "dev", "prod", or the name of any credential type in the default chain. See https://aka.ms/azsdk/go/identity/docs#DefaultAzureCredential for more information`, azureTokenCredentials, atc)
}
@@ -164,7 +168,11 @@ func NewDefaultAzureCredential(options *DefaultAzureCredentialOptions) (*Default
}
}
if selected&managedIdentity != 0 {
- o := &ManagedIdentityCredentialOptions{ClientOptions: options.ClientOptions, dac: true}
+ o := &ManagedIdentityCredentialOptions{
+ ClientOptions: options.ClientOptions,
+ // enable special DefaultAzureCredential behavior (IMDS probing) only when the chain contains another credential
+ dac: selected^managedIdentity != 0,
+ }
if ID, ok := os.LookupEnv(azureClientID); ok {
o.ID = ClientID(ID)
}
@@ -202,6 +210,19 @@ func NewDefaultAzureCredential(options *DefaultAzureCredentialOptions) (*Default
creds = append(creds, &defaultCredentialErrorReporter{credType: credNameAzureDeveloperCLI, err: err})
}
}
+ if selected&azurePowerShell != 0 {
+ azurePowerShellCred, err := NewAzurePowerShellCredential(&AzurePowerShellCredentialOptions{
+ AdditionallyAllowedTenants: additionalTenants,
+ TenantID: options.TenantID,
+ inDefaultChain: true,
+ })
+ if err == nil {
+ creds = append(creds, azurePowerShellCred)
+ } else {
+ errorMessages = append(errorMessages, credNameAzurePowerShell+": "+err.Error())
+ creds = append(creds, &defaultCredentialErrorReporter{credType: credNameAzurePowerShell, err: err})
+ }
+ }
if len(errorMessages) > 0 {
log.Writef(EventAuthentication, "NewDefaultAzureCredential failed to initialize some credentials:\n\t%s", strings.Join(errorMessages, "\n\t"))
diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/developer_credential_util.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/developer_credential_util.go
index 14f8a031265..cb7dbe2e4b8 100644
--- a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/developer_credential_util.go
+++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/developer_credential_util.go
@@ -12,7 +12,6 @@ import (
"errors"
"os"
"os/exec"
- "runtime"
"strings"
"time"
)
@@ -30,17 +29,9 @@ var shellExec = func(ctx context.Context, credName, command string) ([]byte, err
ctx, cancel = context.WithTimeout(ctx, cliTimeout)
defer cancel()
}
- var cmd *exec.Cmd
- if runtime.GOOS == "windows" {
- dir := os.Getenv("SYSTEMROOT")
- if dir == "" {
- return nil, newCredentialUnavailableError(credName, `environment variable "SYSTEMROOT" has no value`)
- }
- cmd = exec.CommandContext(ctx, "cmd.exe", "/c", command)
- cmd.Dir = dir
- } else {
- cmd = exec.CommandContext(ctx, "/bin/sh", "-c", command)
- cmd.Dir = "/bin"
+ cmd, err := buildCmd(ctx, credName, command)
+ if err != nil {
+ return nil, err
}
cmd.Env = os.Environ()
stderr := bytes.Buffer{}
@@ -57,7 +48,15 @@ var shellExec = func(ctx context.Context, credName, command string) ([]byte, err
msg := stderr.String()
var exErr *exec.ExitError
if errors.As(err, &exErr) && exErr.ExitCode() == 127 || strings.Contains(msg, "' is not recognized") {
- return nil, newCredentialUnavailableError(credName, "CLI executable not found on path")
+ return nil, newCredentialUnavailableError(credName, "executable not found on path")
+ }
+ if credName == credNameAzurePowerShell {
+ if strings.Contains(msg, "Connect-AzAccount") {
+ msg = `Please run "Connect-AzAccount" to set up an account`
+ }
+ if strings.Contains(msg, noAzAccountModule) {
+ msg = noAzAccountModule
+ }
}
if msg == "" {
msg = err.Error()
diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/developer_credential_util_nonwindows.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/developer_credential_util_nonwindows.go
new file mode 100644
index 00000000000..681fcd0cf9f
--- /dev/null
+++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/developer_credential_util_nonwindows.go
@@ -0,0 +1,17 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+//go:build !windows
+
+package azidentity
+
+import (
+ "context"
+ "os/exec"
+)
+
+func buildCmd(ctx context.Context, _, command string) (*exec.Cmd, error) {
+ cmd := exec.CommandContext(ctx, "/bin/sh", "-c", command)
+ cmd.Dir = "/bin"
+ return cmd, nil
+}
diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/developer_credential_util_windows.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/developer_credential_util_windows.go
new file mode 100644
index 00000000000..09c7a1a977c
--- /dev/null
+++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/developer_credential_util_windows.go
@@ -0,0 +1,22 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package azidentity
+
+import (
+ "context"
+ "os"
+ "os/exec"
+ "syscall"
+)
+
+func buildCmd(ctx context.Context, credName, command string) (*exec.Cmd, error) {
+ dir := os.Getenv("SYSTEMROOT")
+ if dir == "" {
+ return nil, newCredentialUnavailableError(credName, `environment variable "SYSTEMROOT" has no value`)
+ }
+ cmd := exec.CommandContext(ctx, "cmd.exe")
+ cmd.Dir = dir
+ cmd.SysProcAttr = &syscall.SysProcAttr{CmdLine: "/c " + command}
+ return cmd, nil
+}
diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/errors.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/errors.go
index a6d7c6cbc78..33cb63be09a 100644
--- a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/errors.go
+++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/errors.go
@@ -99,6 +99,8 @@ func (e *AuthenticationFailedError) Error() string {
anchor = "apc"
case credNameCert:
anchor = "client-cert"
+ case credNameAzurePowerShell:
+ anchor = "azure-pwsh"
case credNameSecret:
anchor = "client-secret"
case credNameManagedIdentity:
diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/version.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/version.go
index 4c88605366d..041f11658df 100644
--- a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/version.go
+++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/version.go
@@ -14,5 +14,5 @@ const (
module = "github.com/Azure/azure-sdk-for-go/sdk/" + component
// Version is the semantic version (see http://semver.org) of this module.
- version = "v1.12.0"
+ version = "v1.13.1"
)
diff --git a/vendor/github.com/AzureAD/microsoft-authentication-library-for-go/apps/confidential/confidential.go b/vendor/github.com/AzureAD/microsoft-authentication-library-for-go/apps/confidential/confidential.go
index 549d68ab991..29c004320d6 100644
--- a/vendor/github.com/AzureAD/microsoft-authentication-library-for-go/apps/confidential/confidential.go
+++ b/vendor/github.com/AzureAD/microsoft-authentication-library-for-go/apps/confidential/confidential.go
@@ -596,6 +596,11 @@ func (cca Client) AcquireTokenSilent(ctx context.Context, scopes []string, opts
return AuthResult{}, errors.New("call another AcquireToken method to request a new token having these claims")
}
+ // For service principal scenarios, require WithSilentAccount for public API
+ if o.account.IsZero() {
+ return AuthResult{}, errors.New("WithSilentAccount option is required")
+ }
+
silentParameters := base.AcquireTokenSilentParameters{
Scopes: scopes,
Account: o.account,
@@ -604,8 +609,15 @@ func (cca Client) AcquireTokenSilent(ctx context.Context, scopes []string, opts
IsAppCache: o.account.IsZero(),
TenantID: o.tenantID,
AuthnScheme: o.authnScheme,
+ Claims: o.claims,
}
+ return cca.acquireTokenSilentInternal(ctx, silentParameters)
+}
+
+// acquireTokenSilentInternal is the internal implementation shared by AcquireTokenSilent and AcquireTokenByCredential
+func (cca Client) acquireTokenSilentInternal(ctx context.Context, silentParameters base.AcquireTokenSilentParameters) (AuthResult, error) {
+
return cca.base.AcquireTokenSilent(ctx, silentParameters)
}
@@ -708,8 +720,10 @@ func (cca Client) AcquireTokenByAuthCode(ctx context.Context, code string, redir
// acquireTokenByCredentialOptions contains optional configuration for AcquireTokenByCredential
type acquireTokenByCredentialOptions struct {
- claims, tenantID string
- authnScheme AuthenticationScheme
+ claims, tenantID string
+ authnScheme AuthenticationScheme
+ extraBodyParameters map[string]string
+ cacheKeyComponents map[string]string
}
// AcquireByCredentialOption is implemented by options for AcquireTokenByCredential
@@ -719,7 +733,7 @@ type AcquireByCredentialOption interface {
// AcquireTokenByCredential acquires a security token from the authority, using the client credentials grant.
//
-// Options: [WithClaims], [WithTenantID]
+// Options: [WithClaims], [WithTenantID], [WithFMIPath], [WithAttribute]
func (cca Client) AcquireTokenByCredential(ctx context.Context, scopes []string, opts ...AcquireByCredentialOption) (AuthResult, error) {
o := acquireTokenByCredentialOptions{}
err := options.ApplyOptions(&o, opts)
@@ -736,6 +750,29 @@ func (cca Client) AcquireTokenByCredential(ctx context.Context, scopes []string,
if o.authnScheme != nil {
authParams.AuthnScheme = o.authnScheme
}
+ authParams.ExtraBodyParameters = o.extraBodyParameters
+ authParams.CacheKeyComponents = o.cacheKeyComponents
+ if o.claims == "" {
+ silentParameters := base.AcquireTokenSilentParameters{
+ Scopes: scopes,
+ Account: Account{}, // empty account for app token
+ RequestType: accesstokens.ATConfidential,
+ Credential: cca.cred,
+ IsAppCache: true,
+ TenantID: o.tenantID,
+ AuthnScheme: o.authnScheme,
+ Claims: o.claims,
+ ExtraBodyParameters: o.extraBodyParameters,
+ CacheKeyComponents: o.cacheKeyComponents,
+ }
+
+ // Use internal method with empty account (service principal scenario)
+ cache, err := cca.acquireTokenSilentInternal(ctx, silentParameters)
+ if err == nil {
+ return cache, nil
+ }
+ }
+
token, err := cca.base.Token.Credential(ctx, authParams, cca.cred)
if err != nil {
return AuthResult{}, err
@@ -781,3 +818,63 @@ func (cca Client) Account(ctx context.Context, accountID string) (Account, error
func (cca Client) RemoveAccount(ctx context.Context, account Account) error {
return cca.base.RemoveAccount(ctx, account)
}
+
+// WithFMIPath specifies the path to a federated managed identity.
+// The path should point to a valid FMI configuration file that contains the necessary
+// identity information for authentication.
+func WithFMIPath(path string) interface {
+ AcquireByCredentialOption
+ options.CallOption
+} {
+ return struct {
+ AcquireByCredentialOption
+ options.CallOption
+ }{
+ CallOption: options.NewCallOption(
+ func(a any) error {
+ switch t := a.(type) {
+ case *acquireTokenByCredentialOptions:
+ if t.extraBodyParameters == nil {
+ t.extraBodyParameters = make(map[string]string)
+ }
+ if t.cacheKeyComponents == nil {
+ t.cacheKeyComponents = make(map[string]string)
+ }
+ t.cacheKeyComponents["fmi_path"] = path
+ t.extraBodyParameters["fmi_path"] = path
+ default:
+ return fmt.Errorf("unexpected options type %T", a)
+ }
+ return nil
+ },
+ ),
+ }
+}
+
+// WithAttribute specifies an identity attribute to include in the token request.
+// The attribute is sent as "attributes" in the request body and returned as "xmc_attr"
+// in the access token claims. This is sometimes used withFMIPath
+func WithAttribute(attrValue string) interface {
+ AcquireByCredentialOption
+ options.CallOption
+} {
+ return struct {
+ AcquireByCredentialOption
+ options.CallOption
+ }{
+ CallOption: options.NewCallOption(
+ func(a any) error {
+ switch t := a.(type) {
+ case *acquireTokenByCredentialOptions:
+ if t.extraBodyParameters == nil {
+ t.extraBodyParameters = make(map[string]string)
+ }
+ t.extraBodyParameters["attributes"] = attrValue
+ default:
+ return fmt.Errorf("unexpected options type %T", a)
+ }
+ return nil
+ },
+ ),
+ }
+}
diff --git a/vendor/github.com/AzureAD/microsoft-authentication-library-for-go/apps/internal/base/base.go b/vendor/github.com/AzureAD/microsoft-authentication-library-for-go/apps/internal/base/base.go
index 61c1c4cec1e..abf54f7e509 100644
--- a/vendor/github.com/AzureAD/microsoft-authentication-library-for-go/apps/internal/base/base.go
+++ b/vendor/github.com/AzureAD/microsoft-authentication-library-for-go/apps/internal/base/base.go
@@ -46,16 +46,18 @@ type accountManager interface {
// AcquireTokenSilentParameters contains the parameters to acquire a token silently (from cache).
type AcquireTokenSilentParameters struct {
- Scopes []string
- Account shared.Account
- RequestType accesstokens.AppType
- Credential *accesstokens.Credential
- IsAppCache bool
- TenantID string
- UserAssertion string
- AuthorizationType authority.AuthorizeType
- Claims string
- AuthnScheme authority.AuthenticationScheme
+ Scopes []string
+ Account shared.Account
+ RequestType accesstokens.AppType
+ Credential *accesstokens.Credential
+ IsAppCache bool
+ TenantID string
+ UserAssertion string
+ AuthorizationType authority.AuthorizeType
+ Claims string
+ AuthnScheme authority.AuthenticationScheme
+ ExtraBodyParameters map[string]string
+ CacheKeyComponents map[string]string
}
// AcquireTokenAuthCodeParameters contains the parameters required to acquire an access token using the auth code flow.
@@ -327,7 +329,12 @@ func (b Client) AcquireTokenSilent(ctx context.Context, silent AcquireTokenSilen
if silent.AuthnScheme != nil {
authParams.AuthnScheme = silent.AuthnScheme
}
-
+ if silent.CacheKeyComponents != nil {
+ authParams.CacheKeyComponents = silent.CacheKeyComponents
+ }
+ if silent.ExtraBodyParameters != nil {
+ authParams.ExtraBodyParameters = silent.ExtraBodyParameters
+ }
m := b.pmanager
if authParams.AuthorizationType != authority.ATOnBehalfOf {
authParams.AuthorizationType = authority.ATRefreshToken
@@ -367,8 +374,19 @@ func (b Client) AcquireTokenSilent(ctx context.Context, silent AcquireTokenSilen
// If the token is not same, we don't need to refresh it.
// Which means it refreshed.
if str, err := m.Read(ctx, authParams); err == nil && str.AccessToken.Secret == ar.AccessToken {
- if tr, er := b.Token.Credential(ctx, authParams, silent.Credential); er == nil {
- return b.AuthResultFromToken(ctx, authParams, tr)
+ switch silent.RequestType {
+ case accesstokens.ATConfidential:
+ if tr, er := b.Token.Credential(ctx, authParams, silent.Credential); er == nil {
+ return b.AuthResultFromToken(ctx, authParams, tr)
+ }
+ case accesstokens.ATPublic:
+ token, err := b.Token.Refresh(ctx, silent.RequestType, authParams, silent.Credential, storageTokenResponse.RefreshToken)
+ if err != nil {
+ return ar, err
+ }
+ return b.AuthResultFromToken(ctx, authParams, token)
+ case accesstokens.ATUnknown:
+ return ar, errors.New("silent request type cannot be ATUnknown")
}
}
}
@@ -446,6 +464,9 @@ func (b Client) AcquireTokenOnBehalfOf(ctx context.Context, onBehalfOfParams Acq
authParams.Claims = onBehalfOfParams.Claims
authParams.Scopes = onBehalfOfParams.Scopes
authParams.UserAssertion = onBehalfOfParams.UserAssertion
+ if authParams.ExtraBodyParameters != nil {
+ authParams.ExtraBodyParameters = silentParameters.ExtraBodyParameters
+ }
token, err := b.Token.OnBehalfOf(ctx, authParams, onBehalfOfParams.Credential)
if err == nil {
ar, err = b.AuthResultFromToken(ctx, authParams, token)
diff --git a/vendor/github.com/AzureAD/microsoft-authentication-library-for-go/apps/internal/base/storage/items.go b/vendor/github.com/AzureAD/microsoft-authentication-library-for-go/apps/internal/base/storage/items.go
index 7379e2233c8..b7d1a670b1e 100644
--- a/vendor/github.com/AzureAD/microsoft-authentication-library-for-go/apps/internal/base/storage/items.go
+++ b/vendor/github.com/AzureAD/microsoft-authentication-library-for-go/apps/internal/base/storage/items.go
@@ -79,6 +79,7 @@ type AccessToken struct {
UserAssertionHash string `json:"user_assertion_hash,omitempty"`
TokenType string `json:"token_type,omitempty"`
AuthnSchemeKeyID string `json:"keyid,omitempty"`
+ ExtCacheKey string `json:"ext_cache_key,omitempty"`
AdditionalFields map[string]interface{}
}
@@ -105,15 +106,21 @@ func NewAccessToken(homeID, env, realm, clientID string, cachedAt, refreshOn, ex
// Key outputs the key that can be used to uniquely look up this entry in a map.
func (a AccessToken) Key() string {
ks := []string{a.HomeAccountID, a.Environment, a.CredentialType, a.ClientID, a.Realm, a.Scopes}
- key := strings.Join(
- ks,
- shared.CacheKeySeparator,
- )
+
// add token type to key for new access tokens types. skip for bearer token type to
// preserve fwd and back compat between a common cache and msal clients
if !strings.EqualFold(a.TokenType, authority.AccessTokenTypeBearer) {
- key = strings.Join([]string{key, a.TokenType}, shared.CacheKeySeparator)
+ ks = append(ks, a.TokenType)
}
+ // add extra body param hash to key if present
+ if a.ExtCacheKey != "" {
+ ks[2] = "atext" // if the there is extra cache we add "atext" to the key replacing accesstoken
+ ks = append(ks, a.ExtCacheKey)
+ }
+ key := strings.Join(
+ ks,
+ shared.CacheKeySeparator,
+ )
return strings.ToLower(key)
}
diff --git a/vendor/github.com/AzureAD/microsoft-authentication-library-for-go/apps/internal/base/storage/storage.go b/vendor/github.com/AzureAD/microsoft-authentication-library-for-go/apps/internal/base/storage/storage.go
index 84a234967ff..825d8a0f660 100644
--- a/vendor/github.com/AzureAD/microsoft-authentication-library-for-go/apps/internal/base/storage/storage.go
+++ b/vendor/github.com/AzureAD/microsoft-authentication-library-for-go/apps/internal/base/storage/storage.go
@@ -135,7 +135,8 @@ func (m *Manager) Read(ctx context.Context, authParameters authority.AuthParams)
aliases = metadata.Aliases
}
- accessToken := m.readAccessToken(homeAccountID, aliases, realm, clientID, scopes, tokenType, authnSchemeKeyID)
+ accessToken := m.readAccessToken(homeAccountID, aliases, realm, clientID, scopes, tokenType, authnSchemeKeyID, authParameters.CacheExtKeyGenerator())
+
tr.AccessToken = accessToken
if homeAccountID == "" {
@@ -203,6 +204,7 @@ func (m *Manager) Write(authParameters authority.AuthParams, tokenResponse acces
authnSchemeKeyID,
)
+ accessToken.ExtCacheKey = authParameters.CacheExtKeyGenerator()
// Since we have a valid access token, cache it before moving on.
if err := accessToken.Validate(); err == nil {
if err := m.writeAccessToken(accessToken); err != nil {
@@ -291,26 +293,49 @@ func (m *Manager) aadMetadata(ctx context.Context, authorityInfo authority.Info)
return m.aadCache[authorityInfo.Host], nil
}
-func (m *Manager) readAccessToken(homeID string, envAliases []string, realm, clientID string, scopes []string, tokenType, authnSchemeKeyID string) AccessToken {
+func (m *Manager) readAccessToken(homeID string, envAliases []string, realm, clientID string, scopes []string, tokenType, authnSchemeKeyID, extCacheKey string) AccessToken {
m.contractMu.RLock()
- // TODO: linear search (over a map no less) is slow for a large number (thousands) of tokens.
- // this shows up as the dominating node in a profile. for real-world scenarios this likely isn't
- // an issue, however if it does become a problem then we know where to look.
- for k, at := range m.contract.AccessTokens {
+
+ tokensToSearch := m.contract.AccessTokens
+
+ for k, at := range tokensToSearch {
+ // TODO: linear search (over a map no less) is slow for a large number (thousands) of tokens.
+ // this shows up as the dominating node in a profile. for real-world scenarios this likely isn't
+ // an issue, however if it does become a problem then we know where to look.
if at.HomeAccountID == homeID && at.Realm == realm && at.ClientID == clientID {
- if (strings.EqualFold(at.TokenType, tokenType) && at.AuthnSchemeKeyID == authnSchemeKeyID) || (at.TokenType == "" && (tokenType == "" || tokenType == "Bearer")) {
- if checkAlias(at.Environment, envAliases) && isMatchingScopes(scopes, at.Scopes) {
- m.contractMu.RUnlock()
- if needsUpgrade(k) {
- m.contractMu.Lock()
- defer m.contractMu.Unlock()
- at = upgrade(m.contract.AccessTokens, k)
+ // Match token type and authentication scheme
+ tokenTypeMatch := (strings.EqualFold(at.TokenType, tokenType) && at.AuthnSchemeKeyID == authnSchemeKeyID) ||
+ (at.TokenType == "" && (tokenType == "" || tokenType == "Bearer"))
+ environmentAndScopesMatch := checkAlias(at.Environment, envAliases) && isMatchingScopes(scopes, at.Scopes)
+
+ if tokenTypeMatch && environmentAndScopesMatch {
+ // For hashed tokens, check that the key contains the hash
+ if extCacheKey != "" {
+ if !strings.Contains(k, extCacheKey) {
+ continue // Skip this token if the key doesn't contain the hash
+ }
+ } else {
+ // If no extCacheKey is provided, only match tokens that also have no extCacheKey
+ if at.ExtCacheKey != "" {
+ continue // Skip tokens that require a hash when no hash is provided
}
+ }
+ // Handle token upgrade if needed
+ if needsUpgrade(k) {
+ m.contractMu.RUnlock()
+ m.contractMu.Lock()
+ at = upgrade(tokensToSearch, k)
+ m.contractMu.Unlock()
return at
}
+
+ m.contractMu.RUnlock()
+ return at
}
}
}
+
+ // No token found, unlock and return empty token
m.contractMu.RUnlock()
return AccessToken{}
}
diff --git a/vendor/github.com/AzureAD/microsoft-authentication-library-for-go/apps/internal/oauth/ops/accesstokens/accesstokens.go b/vendor/github.com/AzureAD/microsoft-authentication-library-for-go/apps/internal/oauth/ops/accesstokens/accesstokens.go
index d738c7591ee..481f9e43411 100644
--- a/vendor/github.com/AzureAD/microsoft-authentication-library-for-go/apps/internal/oauth/ops/accesstokens/accesstokens.go
+++ b/vendor/github.com/AzureAD/microsoft-authentication-library-for-go/apps/internal/oauth/ops/accesstokens/accesstokens.go
@@ -281,6 +281,9 @@ func (c Client) FromClientSecret(ctx context.Context, authParameters authority.A
qv.Set(clientID, authParameters.ClientID)
addScopeQueryParam(qv, authParameters)
+ // Add extra body parameters if provided
+ addExtraBodyParameters(ctx, qv, authParameters)
+
return c.doTokenResp(ctx, authParameters, qv)
}
@@ -296,6 +299,9 @@ func (c Client) FromAssertion(ctx context.Context, authParameters authority.Auth
qv.Set(clientInfo, clientInfoVal)
addScopeQueryParam(qv, authParameters)
+ // Add extra body parameters if provided
+ addExtraBodyParameters(ctx, qv, authParameters)
+
return c.doTokenResp(ctx, authParameters, qv)
}
@@ -329,6 +335,8 @@ func (c Client) FromUserAssertionClientCertificate(ctx context.Context, authPara
qv.Set("requested_token_use", "on_behalf_of")
addScopeQueryParam(qv, authParameters)
+ // Add extra body parameters if provided
+ addExtraBodyParameters(ctx, qv, authParameters)
return c.doTokenResp(ctx, authParameters, qv)
}
@@ -466,3 +474,12 @@ func addScopeQueryParam(queryParams url.Values, authParameters authority.AuthPar
scopes := AppendDefaultScopes(authParameters)
queryParams.Set("scope", strings.Join(scopes, " "))
}
+
+// addExtraBodyParameters evaluates and adds extra body parameters to the request
+func addExtraBodyParameters(ctx context.Context, v url.Values, ap authority.AuthParams) {
+ for key, value := range ap.ExtraBodyParameters {
+ if value != "" {
+ v.Set(key, value)
+ }
+ }
+}
diff --git a/vendor/github.com/AzureAD/microsoft-authentication-library-for-go/apps/internal/oauth/ops/authority/authority.go b/vendor/github.com/AzureAD/microsoft-authentication-library-for-go/apps/internal/oauth/ops/authority/authority.go
index 3f403746404..debd465dbad 100644
--- a/vendor/github.com/AzureAD/microsoft-authentication-library-for-go/apps/internal/oauth/ops/authority/authority.go
+++ b/vendor/github.com/AzureAD/microsoft-authentication-library-for-go/apps/internal/oauth/ops/authority/authority.go
@@ -15,6 +15,7 @@ import (
"net/url"
"os"
"path"
+ "sort"
"strings"
"time"
@@ -47,6 +48,8 @@ type jsonCaller interface {
}
// For backward compatibility, accept both old and new China endpoints for a transition period.
+// This list is derived from the AAD instance discovery metadata and represents all known trusted hosts
+// across different Azure clouds (Public, China, Germany, US Government, etc.)
var aadTrustedHostList = map[string]bool{
"login.windows.net": true, // Microsoft Azure Worldwide - Used in validation scenarios where host is not this list
"login.partner.microsoftonline.cn": true, // Microsoft Azure China (new)
@@ -55,6 +58,9 @@ var aadTrustedHostList = map[string]bool{
"login-us.microsoftonline.com": true, // Microsoft Azure US Government - Legacy
"login.microsoftonline.us": true, // Microsoft Azure US Government
"login.microsoftonline.com": true, // Microsoft Azure Worldwide
+ "login.microsoft.com": true,
+ "sts.windows.net": true,
+ "login.usgovcloudapi.net": true,
}
// TrustedHost checks if an AAD host is trusted/valid.
@@ -103,36 +109,46 @@ func (r *TenantDiscoveryResponse) Validate() error {
// ValidateIssuerMatchesAuthority validates that the issuer in the TenantDiscoveryResponse matches the authority.
// This is used to identity security or configuration issues in authorities and the OIDC endpoint
func (r *TenantDiscoveryResponse) ValidateIssuerMatchesAuthority(authorityURI string, aliases map[string]bool) error {
-
if authorityURI == "" {
return errors.New("TenantDiscoveryResponse: empty authorityURI provided for validation")
}
+ if r.Issuer == "" {
+ return errors.New("TenantDiscoveryResponse: empty issuer in response")
+ }
- // Parse the issuer URL
issuerURL, err := url.Parse(r.Issuer)
if err != nil {
return fmt.Errorf("TenantDiscoveryResponse: failed to parse issuer URL: %w", err)
}
+ authorityURL, err := url.Parse(authorityURI)
+ if err != nil {
+ return fmt.Errorf("TenantDiscoveryResponse: failed to parse authority URL: %w", err)
+ }
+
+ // Fast path: exact scheme + host match
+ if issuerURL.Scheme == authorityURL.Scheme && issuerURL.Host == authorityURL.Host {
+ return nil
+ }
- // Even if it doesn't match the authority, issuers from known and trusted hosts are valid
+ // Alias-based acceptance
if aliases != nil && aliases[issuerURL.Host] {
return nil
}
- // Parse the authority URL for comparison
- authorityURL, err := url.Parse(authorityURI)
- if err != nil {
- return fmt.Errorf("TenantDiscoveryResponse: failed to parse authority URL: %w", err)
+ issuerHost := issuerURL.Host
+ authorityHost := authorityURL.Host
+
+ // Accept if issuer host is trusted
+ if TrustedHost(issuerHost) {
+ return nil
}
- // Check if the scheme and host match (paths can be ignored when validating the issuer)
- if issuerURL.Scheme == authorityURL.Scheme && issuerURL.Host == authorityURL.Host {
+ // Accept if authority is a regional variant ending with "."
+ if strings.HasSuffix(authorityHost, "."+issuerHost) {
return nil
}
- // If we get here, validation failed
- return fmt.Errorf("TenantDiscoveryResponse: issuer from OIDC discovery '%s' does not match authority '%s' or a known pattern",
- r.Issuer, authorityURI)
+ return fmt.Errorf("TenantDiscoveryResponse: issuer '%s' does not match authority '%s' or any trusted/alias rule", r.Issuer, authorityURI)
}
type InstanceDiscoveryMetadata struct {
@@ -256,6 +272,12 @@ type AuthParams struct {
DomainHint string
// AuthnScheme is an optional scheme for formatting access tokens
AuthnScheme AuthenticationScheme
+ // ExtraBodyParameters are additional parameters to include in token requests.
+ // The functions are evaluated at request time to get the parameter values.
+ // These parameters are also included in the cache key.
+ ExtraBodyParameters map[string]string
+ // CacheKeyComponents are additional components to include in the cache key.
+ CacheKeyComponents map[string]string
}
// NewAuthParams creates an authorization parameters object.
@@ -642,8 +664,42 @@ func (a *AuthParams) AssertionHash() string {
}
func (a *AuthParams) AppKey() string {
+ baseKey := a.ClientID + "_"
if a.AuthorityInfo.Tenant != "" {
- return fmt.Sprintf("%s_%s_AppTokenCache", a.ClientID, a.AuthorityInfo.Tenant)
+ baseKey += a.AuthorityInfo.Tenant
+ }
+
+ // Include extra body parameters in the cache key
+ paramHash := a.CacheExtKeyGenerator()
+ if paramHash != "" {
+ baseKey = fmt.Sprintf("%s_%s", baseKey, paramHash)
+ }
+
+ return baseKey + "_AppTokenCache"
+}
+
+// CacheExtKeyGenerator computes a hash of the Cache key components key and values
+// to include in the cache key. This ensures tokens acquired with different
+// parameters are cached separately.
+func (a *AuthParams) CacheExtKeyGenerator() string {
+ if len(a.CacheKeyComponents) == 0 {
+ return ""
+ }
+
+ // Sort keys to ensure consistent hashing
+ keys := make([]string, 0, len(a.CacheKeyComponents))
+ for k := range a.CacheKeyComponents {
+ keys = append(keys, k)
}
- return fmt.Sprintf("%s__AppTokenCache", a.ClientID)
+ sort.Strings(keys)
+
+ // Create a string by concatenating key+value pairs
+ keyStr := ""
+ for _, key := range keys {
+ // Append key followed by its value with no separator
+ keyStr += key + a.CacheKeyComponents[key]
+ }
+
+ hash := sha256.Sum256([]byte(keyStr))
+ return strings.ToLower(base64.RawURLEncoding.EncodeToString(hash[:]))
}
diff --git a/vendor/github.com/AzureAD/microsoft-authentication-library-for-go/apps/public/public.go b/vendor/github.com/AzureAD/microsoft-authentication-library-for-go/apps/public/public.go
index 7beed26174e..797c086cb87 100644
--- a/vendor/github.com/AzureAD/microsoft-authentication-library-for-go/apps/public/public.go
+++ b/vendor/github.com/AzureAD/microsoft-authentication-library-for-go/apps/public/public.go
@@ -368,9 +368,9 @@ type AcquireByUsernamePasswordOption interface {
acquireByUsernamePasswordOption()
}
-// AcquireTokenByUsernamePassword acquires a security token from the authority, via Username/Password Authentication.
-// NOTE: this flow is NOT recommended.
+// Deprecated: This API will be removed in a future release. Use a more secure flow instead. Follow this migration guide: https://aka.ms/msal-ropc-migration
//
+// AcquireTokenByUsernamePassword acquires a security token from the authority, via Username/Password Authentication.
// Options: [WithClaims], [WithTenantID]
func (pca Client) AcquireTokenByUsernamePassword(ctx context.Context, scopes []string, username, password string, opts ...AcquireByUsernamePasswordOption) (AuthResult, error) {
o := acquireTokenByUsernamePasswordOptions{}
diff --git a/vendor/github.com/hashicorp/consul/api/txn.go b/vendor/github.com/hashicorp/consul/api/txn.go
index 59adafdac3d..65b6e8830f6 100644
--- a/vendor/github.com/hashicorp/consul/api/txn.go
+++ b/vendor/github.com/hashicorp/consul/api/txn.go
@@ -36,7 +36,7 @@ type TxnOps []*TxnOp
type TxnResult struct {
KV *KVPair
Node *Node
- Service *CatalogService
+ Service *AgentService
Check *HealthCheck
}
diff --git a/vendor/github.com/hashicorp/go-version/LICENSE b/vendor/github.com/hashicorp/go-version/LICENSE
index 1409d6ab92f..bb1e9a486af 100644
--- a/vendor/github.com/hashicorp/go-version/LICENSE
+++ b/vendor/github.com/hashicorp/go-version/LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2014 HashiCorp, Inc.
+Copyright IBM Corp. 2014, 2025
Mozilla Public License, version 2.0
diff --git a/vendor/github.com/hashicorp/go-version/README.md b/vendor/github.com/hashicorp/go-version/README.md
index 4b7806cd964..83a8249f72c 100644
--- a/vendor/github.com/hashicorp/go-version/README.md
+++ b/vendor/github.com/hashicorp/go-version/README.md
@@ -1,6 +1,7 @@
# Versioning Library for Go
+

-[](https://godoc.org/github.com/hashicorp/go-version)
+[](https://pkg.go.dev/github.com/hashicorp/go-version)
go-version is a library for parsing versions and version constraints,
and verifying versions against a set of constraints. go-version
@@ -12,7 +13,7 @@ Versions used with go-version must follow [SemVer](http://semver.org/).
## Installation and Usage
Package documentation can be found on
-[GoDoc](http://godoc.org/github.com/hashicorp/go-version).
+[Go Reference](https://pkg.go.dev/github.com/hashicorp/go-version).
Installation can be done with a normal `go get`:
diff --git a/vendor/github.com/hashicorp/go-version/constraint.go b/vendor/github.com/hashicorp/go-version/constraint.go
index 29bdc4d2b5d..3964da070d6 100644
--- a/vendor/github.com/hashicorp/go-version/constraint.go
+++ b/vendor/github.com/hashicorp/go-version/constraint.go
@@ -1,4 +1,4 @@
-// Copyright (c) HashiCorp, Inc.
+// Copyright IBM Corp. 2014, 2025
// SPDX-License-Identifier: MPL-2.0
package version
@@ -8,8 +8,26 @@ import (
"regexp"
"sort"
"strings"
+ "sync"
)
+var (
+ constraintRegexp *regexp.Regexp
+ constraintRegexpOnce sync.Once
+)
+
+func getConstraintRegexp() *regexp.Regexp {
+ constraintRegexpOnce.Do(func() {
+ // This heavy lifting only happens the first time this function is called
+ constraintRegexp = regexp.MustCompile(fmt.Sprintf(
+ `^\s*(%s)\s*(%s)\s*$`,
+ `<=|>=|!=|~>|<|>|=|`,
+ VersionRegexpRaw,
+ ))
+ })
+ return constraintRegexp
+}
+
// Constraint represents a single constraint for a version, such as
// ">= 1.0".
type Constraint struct {
@@ -29,38 +47,11 @@ type Constraints []*Constraint
type constraintFunc func(v, c *Version) bool
-var constraintOperators map[string]constraintOperation
-
type constraintOperation struct {
op operator
f constraintFunc
}
-var constraintRegexp *regexp.Regexp
-
-func init() {
- constraintOperators = map[string]constraintOperation{
- "": {op: equal, f: constraintEqual},
- "=": {op: equal, f: constraintEqual},
- "!=": {op: notEqual, f: constraintNotEqual},
- ">": {op: greaterThan, f: constraintGreaterThan},
- "<": {op: lessThan, f: constraintLessThan},
- ">=": {op: greaterThanEqual, f: constraintGreaterThanEqual},
- "<=": {op: lessThanEqual, f: constraintLessThanEqual},
- "~>": {op: pessimistic, f: constraintPessimistic},
- }
-
- ops := make([]string, 0, len(constraintOperators))
- for k := range constraintOperators {
- ops = append(ops, regexp.QuoteMeta(k))
- }
-
- constraintRegexp = regexp.MustCompile(fmt.Sprintf(
- `^\s*(%s)\s*(%s)\s*$`,
- strings.Join(ops, "|"),
- VersionRegexpRaw))
-}
-
// NewConstraint will parse one or more constraints from the given
// constraint string. The string must be a comma-separated list of
// constraints.
@@ -107,7 +98,7 @@ func (cs Constraints) Check(v *Version) bool {
// to '>0.2' it is *NOT* treated as equal.
//
// Missing operator is treated as equal to '=', whitespaces
-// are ignored and constraints are sorted before comaparison.
+// are ignored and constraints are sorted before comparison.
func (cs Constraints) Equals(c Constraints) bool {
if len(cs) != len(c) {
return false
@@ -176,9 +167,9 @@ func (c *Constraint) String() string {
}
func parseSingle(v string) (*Constraint, error) {
- matches := constraintRegexp.FindStringSubmatch(v)
+ matches := getConstraintRegexp().FindStringSubmatch(v)
if matches == nil {
- return nil, fmt.Errorf("Malformed constraint: %s", v)
+ return nil, fmt.Errorf("malformed constraint: %s", v)
}
check, err := NewVersion(matches[2])
@@ -186,7 +177,25 @@ func parseSingle(v string) (*Constraint, error) {
return nil, err
}
- cop := constraintOperators[matches[1]]
+ var cop constraintOperation
+ switch matches[1] {
+ case "=":
+ cop = constraintOperation{op: equal, f: constraintEqual}
+ case "!=":
+ cop = constraintOperation{op: notEqual, f: constraintNotEqual}
+ case ">":
+ cop = constraintOperation{op: greaterThan, f: constraintGreaterThan}
+ case "<":
+ cop = constraintOperation{op: lessThan, f: constraintLessThan}
+ case ">=":
+ cop = constraintOperation{op: greaterThanEqual, f: constraintGreaterThanEqual}
+ case "<=":
+ cop = constraintOperation{op: lessThanEqual, f: constraintLessThanEqual}
+ case "~>":
+ cop = constraintOperation{op: pessimistic, f: constraintPessimistic}
+ default:
+ cop = constraintOperation{op: equal, f: constraintEqual}
+ }
return &Constraint{
f: cop.f,
diff --git a/vendor/github.com/hashicorp/go-version/version.go b/vendor/github.com/hashicorp/go-version/version.go
index 7c683c2813a..17b29732ee1 100644
--- a/vendor/github.com/hashicorp/go-version/version.go
+++ b/vendor/github.com/hashicorp/go-version/version.go
@@ -1,23 +1,39 @@
-// Copyright (c) HashiCorp, Inc.
+// Copyright IBM Corp. 2014, 2025
// SPDX-License-Identifier: MPL-2.0
package version
import (
- "bytes"
"database/sql/driver"
"fmt"
"regexp"
"strconv"
"strings"
+ "sync"
)
// The compiled regular expression used to test the validity of a version.
var (
- versionRegexp *regexp.Regexp
- semverRegexp *regexp.Regexp
+ versionRegexp *regexp.Regexp
+ versionRegexpOnce sync.Once
+ semverRegexp *regexp.Regexp
+ semverRegexpOnce sync.Once
)
+func getVersionRegexp() *regexp.Regexp {
+ versionRegexpOnce.Do(func() {
+ versionRegexp = regexp.MustCompile("^" + VersionRegexpRaw + "$")
+ })
+ return versionRegexp
+}
+
+func getSemverRegexp() *regexp.Regexp {
+ semverRegexpOnce.Do(func() {
+ semverRegexp = regexp.MustCompile("^" + SemverRegexpRaw + "$")
+ })
+ return semverRegexp
+}
+
// The raw regular expression string used for testing the validity
// of a version.
const (
@@ -42,28 +58,23 @@ type Version struct {
original string
}
-func init() {
- versionRegexp = regexp.MustCompile("^" + VersionRegexpRaw + "$")
- semverRegexp = regexp.MustCompile("^" + SemverRegexpRaw + "$")
-}
-
// NewVersion parses the given version and returns a new
// Version.
func NewVersion(v string) (*Version, error) {
- return newVersion(v, versionRegexp)
+ return newVersion(v, getVersionRegexp())
}
// NewSemver parses the given version and returns a new
// Version that adheres strictly to SemVer specs
// https://semver.org/
func NewSemver(v string) (*Version, error) {
- return newVersion(v, semverRegexp)
+ return newVersion(v, getSemverRegexp())
}
func newVersion(v string, pattern *regexp.Regexp) (*Version, error) {
matches := pattern.FindStringSubmatch(v)
if matches == nil {
- return nil, fmt.Errorf("Malformed version: %s", v)
+ return nil, fmt.Errorf("malformed version: %s", v)
}
segmentsStr := strings.Split(matches[1], ".")
segments := make([]int64, len(segmentsStr))
@@ -71,7 +82,7 @@ func newVersion(v string, pattern *regexp.Regexp) (*Version, error) {
val, err := strconv.ParseInt(str, 10, 64)
if err != nil {
return nil, fmt.Errorf(
- "Error parsing version: %s", err)
+ "error parsing version: %s", err)
}
segments[i] = val
@@ -174,7 +185,7 @@ func (v *Version) Compare(other *Version) int {
} else if lhs < rhs {
return -1
}
- // Otherwis, rhs was > lhs, they're not equal
+ // Otherwise, rhs was > lhs, they're not equal
return 1
}
@@ -382,22 +393,29 @@ func (v *Version) Segments64() []int64 {
// missing parts (1.0 => 1.0.0) will be made into a canonicalized form
// as shown in the parenthesized examples.
func (v *Version) String() string {
- var buf bytes.Buffer
- fmtParts := make([]string, len(v.segments))
+ return string(v.bytes())
+}
+
+func (v *Version) bytes() []byte {
+ var buf []byte
for i, s := range v.segments {
- // We can ignore err here since we've pre-parsed the values in segments
- str := strconv.FormatInt(s, 10)
- fmtParts[i] = str
+ if i > 0 {
+ buf = append(buf, '.')
+ }
+ buf = strconv.AppendInt(buf, s, 10)
}
- fmt.Fprintf(&buf, strings.Join(fmtParts, "."))
+
if v.pre != "" {
- fmt.Fprintf(&buf, "-%s", v.pre)
+ buf = append(buf, '-')
+ buf = append(buf, v.pre...)
}
+
if v.metadata != "" {
- fmt.Fprintf(&buf, "+%s", v.metadata)
+ buf = append(buf, '+')
+ buf = append(buf, v.metadata...)
}
- return buf.String()
+ return buf
}
// Original returns the original parsed version as-is, including any
diff --git a/vendor/github.com/hashicorp/go-version/version_collection.go b/vendor/github.com/hashicorp/go-version/version_collection.go
index 83547fe13d6..11bc8b1c56c 100644
--- a/vendor/github.com/hashicorp/go-version/version_collection.go
+++ b/vendor/github.com/hashicorp/go-version/version_collection.go
@@ -1,4 +1,4 @@
-// Copyright (c) HashiCorp, Inc.
+// Copyright IBM Corp. 2014, 2025
// SPDX-License-Identifier: MPL-2.0
package version
diff --git a/vendor/github.com/miekg/dns/README.md b/vendor/github.com/miekg/dns/README.md
index 54471f5c225..2a7d8c265bf 100644
--- a/vendor/github.com/miekg/dns/README.md
+++ b/vendor/github.com/miekg/dns/README.md
@@ -3,6 +3,11 @@
[](https://goreportcard.com/report/miekg/dns)
[](https://godoc.org/github.com/miekg/dns)
+DNS version 2 is now available at , check it out if you want to
+help shape the next 15 years of the Go DNS package.
+
+The version here will see no new features and less and less development.
+
# Alternative (more granular) approach to a DNS library
> Less is more.
@@ -17,96 +22,97 @@ avoiding breaking changes wherever reasonable. We support the last two versions
# Goals
-* KISS;
-* Fast;
-* Small API. If it's easy to code in Go, don't make a function for it.
+- KISS;
+- Fast;
+- Small API. If it's easy to code in Go, don't make a function for it.
# Users
A not-so-up-to-date-list-that-may-be-actually-current:
-* https://github.com/coredns/coredns
-* https://github.com/abh/geodns
-* https://github.com/baidu/bfe
-* http://www.statdns.com/
-* http://www.dnsinspect.com/
-* https://github.com/chuangbo/jianbing-dictionary-dns
-* http://www.dns-lg.com/
-* https://github.com/fcambus/rrda
-* https://github.com/kenshinx/godns
-* https://github.com/skynetservices/skydns
-* https://github.com/hashicorp/consul
-* https://github.com/DevelopersPL/godnsagent
-* https://github.com/duedil-ltd/discodns
-* https://github.com/StalkR/dns-reverse-proxy
-* https://github.com/tianon/rawdns
-* https://mesosphere.github.io/mesos-dns/
-* https://github.com/fcambus/statzone
-* https://github.com/benschw/dns-clb-go
-* https://github.com/corny/dnscheck for
-* https://github.com/miekg/unbound
-* https://github.com/miekg/exdns
-* https://dnslookup.org
-* https://github.com/looterz/grimd
-* https://github.com/phamhongviet/serf-dns
-* https://github.com/mehrdadrad/mylg
-* https://github.com/bamarni/dockness
-* https://github.com/fffaraz/microdns
-* https://github.com/ipdcode/hades
-* https://github.com/StackExchange/dnscontrol/
-* https://www.dnsperf.com/
-* https://dnssectest.net/
-* https://github.com/oif/apex
-* https://github.com/jedisct1/dnscrypt-proxy
-* https://github.com/jedisct1/rpdns
-* https://github.com/xor-gate/sshfp
-* https://github.com/rs/dnstrace
-* https://blitiri.com.ar/p/dnss ([github mirror](https://github.com/albertito/dnss))
-* https://render.com
-* https://github.com/peterzen/goresolver
-* https://github.com/folbricht/routedns
-* https://domainr.com/
-* https://zonedb.org/
-* https://router7.org/
-* https://github.com/fortio/dnsping
-* https://github.com/Luzilla/dnsbl_exporter
-* https://github.com/bodgit/tsig
-* https://github.com/v2fly/v2ray-core (test only)
-* https://kuma.io/
-* https://www.misaka.io/services/dns
-* https://ping.sx/dig
-* https://fleetdeck.io/
-* https://github.com/markdingo/autoreverse
-* https://github.com/slackhq/nebula
-* https://addr.tools/
-* https://dnscheck.tools/
-* https://github.com/egbakou/domainverifier
-* https://github.com/semihalev/sdns
-* https://github.com/wintbiit/NineDNS
-* https://linuxcontainers.org/incus/
-* https://ifconfig.es
-* https://github.com/zmap/zdns
-* https://framagit.org/bortzmeyer/check-soa
+- https://github.com/coredns/coredns
+- https://github.com/abh/geodns
+- https://github.com/baidu/bfe
+- http://www.statdns.com/
+- http://www.dnsinspect.com/
+- https://github.com/chuangbo/jianbing-dictionary-dns
+- http://www.dns-lg.com/
+- https://github.com/fcambus/rrda
+- https://github.com/kenshinx/godns
+- https://github.com/skynetservices/skydns
+- https://github.com/hashicorp/consul
+- https://github.com/DevelopersPL/godnsagent
+- https://github.com/duedil-ltd/discodns
+- https://github.com/StalkR/dns-reverse-proxy
+- https://github.com/tianon/rawdns
+- https://mesosphere.github.io/mesos-dns/
+- https://github.com/fcambus/statzone
+- https://github.com/benschw/dns-clb-go
+- https://github.com/corny/dnscheck for
+- https://github.com/miekg/unbound
+- https://github.com/miekg/exdns
+- https://dnslookup.org
+- https://github.com/looterz/grimd
+- https://github.com/phamhongviet/serf-dns
+- https://github.com/mehrdadrad/mylg
+- https://github.com/bamarni/dockness
+- https://github.com/fffaraz/microdns
+- https://github.com/ipdcode/hades
+- https://github.com/StackExchange/dnscontrol/
+- https://www.dnsperf.com/
+- https://dnssectest.net/
+- https://github.com/oif/apex
+- https://github.com/jedisct1/dnscrypt-proxy
+- https://github.com/jedisct1/rpdns
+- https://github.com/xor-gate/sshfp
+- https://github.com/rs/dnstrace
+- https://blitiri.com.ar/p/dnss ([github mirror](https://github.com/albertito/dnss))
+- https://render.com
+- https://github.com/peterzen/goresolver
+- https://github.com/folbricht/routedns
+- https://domainr.com/
+- https://zonedb.org/
+- https://router7.org/
+- https://github.com/fortio/dnsping
+- https://github.com/Luzilla/dnsbl_exporter
+- https://github.com/bodgit/tsig
+- https://github.com/v2fly/v2ray-core (test only)
+- https://kuma.io/
+- https://www.misaka.io/services/dns
+- https://ping.sx/dig
+- https://fleetdeck.io/
+- https://github.com/markdingo/autoreverse
+- https://github.com/slackhq/nebula
+- https://addr.tools/
+- https://dnscheck.tools/
+- https://github.com/egbakou/domainverifier
+- https://github.com/semihalev/sdns
+- https://github.com/wintbiit/NineDNS
+- https://linuxcontainers.org/incus/
+- https://ifconfig.es
+- https://github.com/zmap/zdns
+- https://framagit.org/bortzmeyer/check-soa
+- https://github.com/jkerdreux-imt/owns
Send pull request if you want to be listed here.
# Features
-* UDP/TCP queries, IPv4 and IPv6
-* RFC 1035 zone file parsing ($INCLUDE, $ORIGIN, $TTL and $GENERATE (for all record types) are supported
-* Fast
-* Server side programming (mimicking the net/http package)
-* Client side programming
-* DNSSEC: signing, validating and key generation for DSA, RSA, ECDSA and Ed25519
-* EDNS0, NSID, Cookies
-* AXFR/IXFR
-* TSIG, SIG(0)
-* DNS over TLS (DoT): encrypted connection between client and server over TCP
-* DNS name compression
+- UDP/TCP queries, IPv4 and IPv6
+- RFC 1035 zone file parsing ($INCLUDE, $ORIGIN, $TTL and $GENERATE (for all record types) are supported
+- Fast
+- Server side programming (mimicking the net/http package)
+- Client side programming
+- DNSSEC: signing, validating and key generation for DSA, RSA, ECDSA and Ed25519
+- EDNS0, NSID, Cookies
+- AXFR/IXFR
+- TSIG, SIG(0)
+- DNS over TLS (DoT): encrypted connection between client and server over TCP
+- DNS name compression
Have fun!
-Miek Gieben - 2010-2012 -
+Miek Gieben - 2010-2012 -
DNS Authors 2012-
# Building
@@ -126,81 +132,83 @@ Example programs can be found in the `github.com/miekg/exdns` repository.
## Supported RFCs
-*all of them*
-
-* 103{4,5} - DNS standard
-* 1183 - ISDN, X25 and other deprecated records
-* 1348 - NSAP record (removed the record)
-* 1982 - Serial Arithmetic
-* 1876 - LOC record
-* 1995 - IXFR
-* 1996 - DNS notify
-* 2136 - DNS Update (dynamic updates)
-* 2181 - RRset definition - there is no RRset type though, just []RR
-* 2537 - RSAMD5 DNS keys
-* 2065 - DNSSEC (updated in later RFCs)
-* 2671 - EDNS record
-* 2782 - SRV record
-* 2845 - TSIG record
-* 2915 - NAPTR record
-* 2929 - DNS IANA Considerations
-* 3110 - RSASHA1 DNS keys
-* 3123 - APL record
-* 3225 - DO bit (DNSSEC OK)
-* 340{1,2,3} - NAPTR record
-* 3445 - Limiting the scope of (DNS)KEY
-* 3596 - AAAA record
-* 3597 - Unknown RRs
-* 4025 - A Method for Storing IPsec Keying Material in DNS
-* 403{3,4,5} - DNSSEC + validation functions
-* 4255 - SSHFP record
-* 4343 - Case insensitivity
-* 4408 - SPF record
-* 4509 - SHA256 Hash in DS
-* 4592 - Wildcards in the DNS
-* 4635 - HMAC SHA TSIG
-* 4701 - DHCID
-* 4892 - id.server
-* 5001 - NSID
-* 5155 - NSEC3 record
-* 5205 - HIP record
-* 5702 - SHA2 in the DNS
-* 5936 - AXFR
-* 5966 - TCP implementation recommendations
-* 6605 - ECDSA
-* 6725 - IANA Registry Update
-* 6742 - ILNP DNS
-* 6840 - Clarifications and Implementation Notes for DNS Security
-* 6844 - CAA record
-* 6891 - EDNS0 update
-* 6895 - DNS IANA considerations
-* 6944 - DNSSEC DNSKEY Algorithm Status
-* 6975 - Algorithm Understanding in DNSSEC
-* 7043 - EUI48/EUI64 records
-* 7314 - DNS (EDNS) EXPIRE Option
-* 7477 - CSYNC RR
-* 7828 - edns-tcp-keepalive EDNS0 Option
-* 7553 - URI record
-* 7858 - DNS over TLS: Initiation and Performance Considerations
-* 7871 - EDNS0 Client Subnet
-* 7873 - Domain Name System (DNS) Cookies
-* 8080 - EdDSA for DNSSEC
-* 8490 - DNS Stateful Operations
-* 8499 - DNS Terminology
-* 8659 - DNS Certification Authority Authorization (CAA) Resource Record
-* 8777 - DNS Reverse IP Automatic Multicast Tunneling (AMT) Discovery
-* 8914 - Extended DNS Errors
-* 8976 - Message Digest for DNS Zones (ZONEMD RR)
-* 9460 - Service Binding and Parameter Specification via the DNS
-* 9461 - Service Binding Mapping for DNS Servers
-* 9462 - Discovery of Designated Resolvers
-* 9460 - SVCB and HTTPS Records
-* 9606 - DNS Resolver Information
-* Draft - Compact Denial of Existence in DNSSEC
+_all of them_
+
+- 103{4,5} - DNS standard
+- 1183 - ISDN, X25 and other deprecated records
+- 1348 - NSAP record (removed the record)
+- 1982 - Serial Arithmetic
+- 1876 - LOC record
+- 1995 - IXFR
+- 1996 - DNS notify
+- 2136 - DNS Update (dynamic updates)
+- 2181 - RRset definition - there is no RRset type though, just []RR
+- 2537 - RSAMD5 DNS keys
+- 2065 - DNSSEC (updated in later RFCs)
+- 2671 - EDNS record
+- 2782 - SRV record
+- 2845 - TSIG record
+- 2915 - NAPTR record
+- 2929 - DNS IANA Considerations
+- 3110 - RSASHA1 DNS keys
+- 3123 - APL record
+- 3225 - DO bit (DNSSEC OK)
+- 340{1,2,3} - NAPTR record
+- 3445 - Limiting the scope of (DNS)KEY
+- 3596 - AAAA record
+- 3597 - Unknown RRs
+- 4025 - A Method for Storing IPsec Keying Material in DNS
+- 403{3,4,5} - DNSSEC + validation functions
+- 4255 - SSHFP record
+- 4343 - Case insensitivity
+- 4408 - SPF record
+- 4509 - SHA256 Hash in DS
+- 4592 - Wildcards in the DNS
+- 4635 - HMAC SHA TSIG
+- 4701 - DHCID
+- 4892 - id.server
+- 5001 - NSID
+- 5155 - NSEC3 record
+- 5205 - HIP record
+- 5702 - SHA2 in the DNS
+- 5936 - AXFR
+- 5966 - TCP implementation recommendations
+- 6605 - ECDSA
+- 6725 - IANA Registry Update
+- 6742 - ILNP DNS
+- 6840 - Clarifications and Implementation Notes for DNS Security
+- 6844 - CAA record
+- 6891 - EDNS0 update
+- 6895 - DNS IANA considerations
+- 6944 - DNSSEC DNSKEY Algorithm Status
+- 6975 - Algorithm Understanding in DNSSEC
+- 7043 - EUI48/EUI64 records
+- 7314 - DNS (EDNS) EXPIRE Option
+- 7477 - CSYNC RR
+- 7828 - edns-tcp-keepalive EDNS0 Option
+- 7553 - URI record
+- 7858 - DNS over TLS: Initiation and Performance Considerations
+- 7871 - EDNS0 Client Subnet
+- 7873 - Domain Name System (DNS) Cookies
+- 8080 - EdDSA for DNSSEC
+- 8490 - DNS Stateful Operations
+- 8499 - DNS Terminology
+- 8659 - DNS Certification Authority Authorization (CAA) Resource Record
+- 8777 - DNS Reverse IP Automatic Multicast Tunneling (AMT) Discovery
+- 8914 - Extended DNS Errors
+- 8976 - Message Digest for DNS Zones (ZONEMD RR)
+- 9460 - Service Binding and Parameter Specification via the DNS
+- 9461 - Service Binding Mapping for DNS Servers
+- 9462 - Discovery of Designated Resolvers
+- 9460 - SVCB and HTTPS Records
+- 9567 - DNS Error Reporting
+- 9606 - DNS Resolver Information
+- 9660 - DNS Zone Version (ZONEVERSION) Option
+- Draft - Compact Denial of Existence in DNSSEC
## Loosely Based Upon
-* ldns -
-* NSD -
-* Net::DNS -
-* GRONG -
+- ldns -
+- NSD -
+- Net::DNS -
+- GRONG -
diff --git a/vendor/github.com/miekg/dns/edns.go b/vendor/github.com/miekg/dns/edns.go
index 5c970ca7ed8..89318b75004 100644
--- a/vendor/github.com/miekg/dns/edns.go
+++ b/vendor/github.com/miekg/dns/edns.go
@@ -24,6 +24,8 @@ const (
EDNS0TCPKEEPALIVE = 0xb // EDNS0 tcp keep alive (See RFC 7828)
EDNS0PADDING = 0xc // EDNS0 padding (See RFC 7830)
EDNS0EDE = 0xf // EDNS0 extended DNS errors (See RFC 8914)
+ EDNS0REPORTING = 0x12 // EDNS0 reporting (See RFC 9567)
+ EDNS0ZONEVERSION = 0x13 // EDNS0 Zone Version (See RFC 9660)
EDNS0LOCALSTART = 0xFDE9 // Beginning of range reserved for local/experimental use (See RFC 6891)
EDNS0LOCALEND = 0xFFFE // End of range reserved for local/experimental use (See RFC 6891)
_DO = 1 << 15 // DNSSEC OK
@@ -60,6 +62,10 @@ func makeDataOpt(code uint16) EDNS0 {
return new(EDNS0_EDE)
case EDNS0ESU:
return new(EDNS0_ESU)
+ case EDNS0REPORTING:
+ return new(EDNS0_REPORTING)
+ case EDNS0ZONEVERSION:
+ return new(EDNS0_ZONEVERSION)
default:
e := new(EDNS0_LOCAL)
e.Code = code
@@ -75,17 +81,16 @@ type OPT struct {
func (rr *OPT) String() string {
s := "\n;; OPT PSEUDOSECTION:\n; EDNS: version " + strconv.Itoa(int(rr.Version())) + "; "
+ s += "flags:"
if rr.Do() {
- if rr.Co() {
- s += "flags: do, co; "
- } else {
- s += "flags: do; "
- }
- } else {
- s += "flags:; "
+ s += " do"
}
- if rr.Hdr.Ttl&0x7FFF != 0 {
- s += fmt.Sprintf("MBZ: 0x%04x, ", rr.Hdr.Ttl&0x7FFF)
+ if rr.Co() {
+ s += " co"
+ }
+ s += "; "
+ if z := rr.Z(); z != 0 {
+ s += fmt.Sprintf("MBZ: 0x%04x, ", z)
}
s += "udp: " + strconv.Itoa(int(rr.UDPSize()))
@@ -127,6 +132,10 @@ func (rr *OPT) String() string {
s += "\n; EDE: " + o.String()
case *EDNS0_ESU:
s += "\n; ESU: " + o.String()
+ case *EDNS0_REPORTING:
+ s += "\n; REPORT-CHANNEL: " + o.String()
+ case *EDNS0_ZONEVERSION:
+ s += "\n; ZONEVERSION: " + o.String()
}
}
return s
@@ -308,10 +317,6 @@ type EDNS0_SUBNET struct {
func (e *EDNS0_SUBNET) Option() uint16 { return EDNS0SUBNET }
func (e *EDNS0_SUBNET) pack() ([]byte, error) {
- b := make([]byte, 4)
- binary.BigEndian.PutUint16(b[0:], e.Family)
- b[2] = e.SourceNetmask
- b[3] = e.SourceScope
switch e.Family {
case 0:
// "dig" sets AddressFamily to 0 if SourceNetmask is also 0
@@ -319,16 +324,27 @@ func (e *EDNS0_SUBNET) pack() ([]byte, error) {
if e.SourceNetmask != 0 {
return nil, errors.New("bad address family")
}
+ b := make([]byte, 4)
+ b[3] = e.SourceScope
+ return b, nil
case 1:
if e.SourceNetmask > net.IPv4len*8 {
return nil, errors.New("bad netmask")
}
- if len(e.Address.To4()) != net.IPv4len {
+ ip4 := e.Address.To4()
+ if len(ip4) != net.IPv4len {
return nil, errors.New("bad address")
}
- ip := e.Address.To4().Mask(net.CIDRMask(int(e.SourceNetmask), net.IPv4len*8))
needLength := (e.SourceNetmask + 8 - 1) / 8 // division rounding up
- b = append(b, ip[:needLength]...)
+ b := make([]byte, 4+needLength)
+ binary.BigEndian.PutUint16(b[0:], e.Family)
+ b[2] = e.SourceNetmask
+ b[3] = e.SourceScope
+ if needLength > 0 {
+ ip := ip4.Mask(net.CIDRMask(int(e.SourceNetmask), net.IPv4len*8))
+ copy(b[4:], ip[:needLength])
+ }
+ return b, nil
case 2:
if e.SourceNetmask > net.IPv6len*8 {
return nil, errors.New("bad netmask")
@@ -336,13 +352,19 @@ func (e *EDNS0_SUBNET) pack() ([]byte, error) {
if len(e.Address) != net.IPv6len {
return nil, errors.New("bad address")
}
- ip := e.Address.Mask(net.CIDRMask(int(e.SourceNetmask), net.IPv6len*8))
needLength := (e.SourceNetmask + 8 - 1) / 8 // division rounding up
- b = append(b, ip[:needLength]...)
+ b := make([]byte, 4+needLength)
+ binary.BigEndian.PutUint16(b[0:], e.Family)
+ b[2] = e.SourceNetmask
+ b[3] = e.SourceScope
+ if needLength > 0 {
+ ip := e.Address.Mask(net.CIDRMask(int(e.SourceNetmask), net.IPv6len*8))
+ copy(b[4:], ip[:needLength])
+ }
+ return b, nil
default:
return nil, errors.New("bad address family")
}
- return b, nil
}
func (e *EDNS0_SUBNET) unpack(b []byte) error {
@@ -875,3 +897,74 @@ func (e *EDNS0_ESU) unpack(b []byte) error {
e.Uri = string(b)
return nil
}
+
+// EDNS0_REPORTING implements the EDNS0 Reporting Channel option (RFC 9567).
+type EDNS0_REPORTING struct {
+ Code uint16 // always EDNS0REPORTING
+ AgentDomain string
+}
+
+func (e *EDNS0_REPORTING) Option() uint16 { return EDNS0REPORTING }
+func (e *EDNS0_REPORTING) String() string { return e.AgentDomain }
+func (e *EDNS0_REPORTING) copy() EDNS0 { return &EDNS0_REPORTING{e.Code, e.AgentDomain} }
+func (e *EDNS0_REPORTING) pack() ([]byte, error) {
+ b := make([]byte, 255)
+ off1, err := PackDomainName(Fqdn(e.AgentDomain), b, 0, nil, false)
+ if err != nil {
+ return nil, fmt.Errorf("bad agent domain: %w", err)
+ }
+ return b[:off1], nil
+}
+func (e *EDNS0_REPORTING) unpack(b []byte) error {
+ domain, _, err := UnpackDomainName(b, 0)
+ if err != nil {
+ return fmt.Errorf("bad agent domain: %w", err)
+ }
+ e.AgentDomain = domain
+ return nil
+}
+
+// EDNS0_ZONEVERSION implements the EDNS0 Zone Version option (RFC 9660).
+type EDNS0_ZONEVERSION struct {
+ // always EDNS0ZONEVERSION (19)
+ Code uint16
+ // An unsigned 1-octet Label Count indicating
+ // the number of labels for the name of the zone that VERSION value refers to.
+ LabelCount uint8
+ // An unsigned 1-octet type number distinguishing the format and meaning of version.
+ // 0 SOA-SERIAL, 1-245 Unassigned, 246-255 Reserved for private use, see RFC 9660.
+ Type uint8
+ // An opaque octet string conveying the zone version data (VERSION).
+ Version string
+}
+
+func (e *EDNS0_ZONEVERSION) Option() uint16 { return EDNS0ZONEVERSION }
+func (e *EDNS0_ZONEVERSION) String() string { return e.Version }
+func (e *EDNS0_ZONEVERSION) copy() EDNS0 {
+ return &EDNS0_ZONEVERSION{e.Code, e.LabelCount, e.Type, e.Version}
+}
+func (e *EDNS0_ZONEVERSION) pack() ([]byte, error) {
+ b := []byte{
+ // first octet label count
+ e.LabelCount,
+ // second octet is type
+ e.Type,
+ }
+ if len(e.Version) > 0 {
+ b = append(b, []byte(e.Version)...)
+ }
+ return b, nil
+}
+func (e *EDNS0_ZONEVERSION) unpack(b []byte) error {
+ if len(b) < 2 {
+ return ErrBuf
+ }
+ e.LabelCount = b[0]
+ e.Type = b[1]
+ if len(b) > 2 {
+ e.Version = string(b[2:])
+ } else {
+ e.Version = ""
+ }
+ return nil
+}
diff --git a/vendor/github.com/miekg/dns/server.go b/vendor/github.com/miekg/dns/server.go
index 364149cfcea..50478b32409 100644
--- a/vendor/github.com/miekg/dns/server.go
+++ b/vendor/github.com/miekg/dns/server.go
@@ -194,7 +194,9 @@ type DecorateWriter func(Writer) Writer
// rejected (or ignored) by the MsgAcceptFunc, or passed to this function.
type MsgInvalidFunc func(m []byte, err error)
-func DefaultMsgInvalidFunc(m []byte, err error) {}
+var DefaultMsgInvalidFunc MsgInvalidFunc = defaultMsgInvalidFunc
+
+func defaultMsgInvalidFunc(m []byte, err error) {}
// A Server defines parameters for running an DNS server.
type Server struct {
diff --git a/vendor/github.com/miekg/dns/version.go b/vendor/github.com/miekg/dns/version.go
index 4af7d286c7f..c53a63d7c8b 100644
--- a/vendor/github.com/miekg/dns/version.go
+++ b/vendor/github.com/miekg/dns/version.go
@@ -3,7 +3,7 @@ package dns
import "fmt"
// Version is current version of this library.
-var Version = v{1, 1, 68}
+var Version = v{1, 1, 69}
// v holds the version of this library.
type v struct {
diff --git a/vendor/github.com/prometheus/prometheus/discovery/discovery.go b/vendor/github.com/prometheus/prometheus/discovery/discovery.go
index 2157b820b97..e643cb10af1 100644
--- a/vendor/github.com/prometheus/prometheus/discovery/discovery.go
+++ b/vendor/github.com/prometheus/prometheus/discovery/discovery.go
@@ -54,19 +54,23 @@ type DiscovererOptions struct {
// Extra HTTP client options to expose to Discoverers. This field may be
// ignored; Discoverer implementations must opt-in to reading it.
HTTPClientOptions []config.HTTPClientOption
+
+ // SetName identifies this discoverer set.
+ SetName string
}
// RefreshMetrics are used by the "refresh" package.
// We define them here in the "discovery" package in order to avoid a cyclic dependency between
// "discovery" and "refresh".
type RefreshMetrics struct {
- Failures prometheus.Counter
- Duration prometheus.Observer
+ Failures prometheus.Counter
+ Duration prometheus.Observer
+ DurationHistogram prometheus.Observer
}
// RefreshMetricsInstantiator instantiates the metrics used by the "refresh" package.
type RefreshMetricsInstantiator interface {
- Instantiate(mech string) *RefreshMetrics
+ Instantiate(mech, setName string) *RefreshMetrics
}
// RefreshMetricsManager is an interface for registering, unregistering, and
diff --git a/vendor/github.com/prometheus/prometheus/discovery/dns/dns.go b/vendor/github.com/prometheus/prometheus/discovery/dns/dns.go
index 24af8f65d96..1e0a78698ba 100644
--- a/vendor/github.com/prometheus/prometheus/discovery/dns/dns.go
+++ b/vendor/github.com/prometheus/prometheus/discovery/dns/dns.go
@@ -78,7 +78,7 @@ func (*SDConfig) Name() string { return "dns" }
// NewDiscoverer returns a Discoverer for the Config.
func (c *SDConfig) NewDiscoverer(opts discovery.DiscovererOptions) (discovery.Discoverer, error) {
- return NewDiscovery(*c, opts.Logger, opts.Metrics)
+ return NewDiscovery(*c, opts)
}
// UnmarshalYAML implements the yaml.Unmarshaler interface.
@@ -118,14 +118,14 @@ type Discovery struct {
}
// NewDiscovery returns a new Discovery which periodically refreshes its targets.
-func NewDiscovery(conf SDConfig, logger *slog.Logger, metrics discovery.DiscovererMetrics) (*Discovery, error) {
- m, ok := metrics.(*dnsMetrics)
+func NewDiscovery(conf SDConfig, opts discovery.DiscovererOptions) (*Discovery, error) {
+ m, ok := opts.Metrics.(*dnsMetrics)
if !ok {
return nil, errors.New("invalid discovery metrics type")
}
- if logger == nil {
- logger = promslog.NewNopLogger()
+ if opts.Logger == nil {
+ opts.Logger = promslog.NewNopLogger()
}
qtype := dns.TypeSRV
@@ -145,15 +145,16 @@ func NewDiscovery(conf SDConfig, logger *slog.Logger, metrics discovery.Discover
names: conf.Names,
qtype: qtype,
port: conf.Port,
- logger: logger,
+ logger: opts.Logger,
lookupFn: lookupWithSearchPath,
metrics: m,
}
d.Discovery = refresh.NewDiscovery(
refresh.Options{
- Logger: logger,
+ Logger: opts.Logger,
Mech: "dns",
+ SetName: opts.SetName,
Interval: time.Duration(conf.RefreshInterval),
RefreshF: d.refresh,
MetricsInstantiator: m.refreshMetrics,
diff --git a/vendor/github.com/prometheus/prometheus/discovery/manager.go b/vendor/github.com/prometheus/prometheus/discovery/manager.go
index 6688152da98..431050aa0b7 100644
--- a/vendor/github.com/prometheus/prometheus/discovery/manager.go
+++ b/vendor/github.com/prometheus/prometheus/discovery/manager.go
@@ -27,6 +27,7 @@ import (
"github.com/prometheus/common/promslog"
"github.com/prometheus/prometheus/discovery/targetgroup"
+ "github.com/prometheus/prometheus/util/features"
)
type poolKey struct {
@@ -111,6 +112,13 @@ func NewManager(ctx context.Context, logger *slog.Logger, registerer prometheus.
}
mgr.metrics = metrics
+ // Register all available service discovery providers with the feature registry.
+ if mgr.featureRegistry != nil {
+ for _, sdName := range RegisteredConfigNames() {
+ mgr.featureRegistry.Enable(features.ServiceDiscoveryProviders, sdName)
+ }
+ }
+
return mgr
}
@@ -141,6 +149,15 @@ func HTTPClientOptions(opts ...config.HTTPClientOption) func(*Manager) {
}
}
+// FeatureRegistry sets the feature registry for the manager.
+func FeatureRegistry(fr features.Collector) func(*Manager) {
+ return func(m *Manager) {
+ m.mtx.Lock()
+ defer m.mtx.Unlock()
+ m.featureRegistry = fr
+ }
+}
+
// Manager maintains a set of discovery providers and sends each update to a map channel.
// Targets are grouped by the target set name.
type Manager struct {
@@ -175,6 +192,9 @@ type Manager struct {
metrics *Metrics
sdMetrics map[string]DiscovererMetrics
+
+ // featureRegistry is used to track which service discovery providers are configured.
+ featureRegistry features.Collector
}
// Providers returns the currently configured SD providers.
@@ -479,6 +499,7 @@ func (m *Manager) registerProviders(cfgs Configs, setName string) int {
Logger: m.logger.With("discovery", typ, "config", setName),
HTTPClientOptions: m.httpOpts,
Metrics: m.sdMetrics[typ],
+ SetName: setName,
})
if err != nil {
m.logger.Error("Cannot create service discovery", "err", err, "type", typ, "config", setName)
diff --git a/vendor/github.com/prometheus/prometheus/discovery/metrics_refresh.go b/vendor/github.com/prometheus/prometheus/discovery/metrics_refresh.go
index ef49e591a35..9f3eb27b49b 100644
--- a/vendor/github.com/prometheus/prometheus/discovery/metrics_refresh.go
+++ b/vendor/github.com/prometheus/prometheus/discovery/metrics_refresh.go
@@ -14,6 +14,8 @@
package discovery
import (
+ "time"
+
"github.com/prometheus/client_golang/prometheus"
)
@@ -21,8 +23,9 @@ import (
// We define them here in the "discovery" package in order to avoid a cyclic dependency between
// "discovery" and "refresh".
type RefreshMetricsVecs struct {
- failuresVec *prometheus.CounterVec
- durationVec *prometheus.SummaryVec
+ failuresVec *prometheus.CounterVec
+ durationVec *prometheus.SummaryVec
+ durationHistVec *prometheus.HistogramVec
metricRegisterer MetricRegisterer
}
@@ -36,13 +39,23 @@ func NewRefreshMetrics(reg prometheus.Registerer) RefreshMetricsManager {
Name: "prometheus_sd_refresh_failures_total",
Help: "Number of refresh failures for the given SD mechanism.",
},
- []string{"mechanism"}),
+ []string{"mechanism", "config"}),
durationVec: prometheus.NewSummaryVec(
prometheus.SummaryOpts{
Name: "prometheus_sd_refresh_duration_seconds",
Help: "The duration of a refresh in seconds for the given SD mechanism.",
Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001},
},
+ []string{"mechanism", "config"}),
+ durationHistVec: prometheus.NewHistogramVec(
+ prometheus.HistogramOpts{
+ Name: "prometheus_sd_refresh_duration_histogram_seconds",
+ Help: "The duration of a refresh for the given SD mechanism.",
+ Buckets: []float64{.01, .1, 1, 10},
+ NativeHistogramBucketFactor: 1.1,
+ NativeHistogramMaxBucketNumber: 100,
+ NativeHistogramMinResetDuration: 1 * time.Hour,
+ },
[]string{"mechanism"}),
}
@@ -51,16 +64,18 @@ func NewRefreshMetrics(reg prometheus.Registerer) RefreshMetricsManager {
m.metricRegisterer = NewMetricRegisterer(reg, []prometheus.Collector{
m.failuresVec,
m.durationVec,
+ m.durationHistVec,
})
return m
}
-// Instantiate returns metrics out of metric vectors.
-func (m *RefreshMetricsVecs) Instantiate(mech string) *RefreshMetrics {
+// Instantiate returns metrics out of metric vectors for a given mechanism and config.
+func (m *RefreshMetricsVecs) Instantiate(mech, config string) *RefreshMetrics {
return &RefreshMetrics{
- Failures: m.failuresVec.WithLabelValues(mech),
- Duration: m.durationVec.WithLabelValues(mech),
+ Failures: m.failuresVec.WithLabelValues(mech, config),
+ Duration: m.durationVec.WithLabelValues(mech, config),
+ DurationHistogram: m.durationHistVec.WithLabelValues(mech),
}
}
diff --git a/vendor/github.com/prometheus/prometheus/discovery/refresh/refresh.go b/vendor/github.com/prometheus/prometheus/discovery/refresh/refresh.go
index 31646c0e4c1..0613fd6c6d6 100644
--- a/vendor/github.com/prometheus/prometheus/discovery/refresh/refresh.go
+++ b/vendor/github.com/prometheus/prometheus/discovery/refresh/refresh.go
@@ -28,6 +28,7 @@ import (
type Options struct {
Logger *slog.Logger
Mech string
+ SetName string
Interval time.Duration
RefreshF func(ctx context.Context) ([]*targetgroup.Group, error)
MetricsInstantiator discovery.RefreshMetricsInstantiator
@@ -43,7 +44,7 @@ type Discovery struct {
// NewDiscovery returns a Discoverer function that calls a refresh() function at every interval.
func NewDiscovery(opts Options) *Discovery {
- m := opts.MetricsInstantiator.Instantiate(opts.Mech)
+ m := opts.MetricsInstantiator.Instantiate(opts.Mech, opts.SetName)
var logger *slog.Logger
if opts.Logger == nil {
@@ -107,6 +108,7 @@ func (d *Discovery) refresh(ctx context.Context) ([]*targetgroup.Group, error) {
now := time.Now()
defer func() {
d.metrics.Duration.Observe(time.Since(now).Seconds())
+ d.metrics.DurationHistogram.Observe(time.Since(now).Seconds())
}()
tgs, err := d.refreshf(ctx)
diff --git a/vendor/github.com/prometheus/prometheus/discovery/registry.go b/vendor/github.com/prometheus/prometheus/discovery/registry.go
index 33938cef3e7..b3b82cdeecc 100644
--- a/vendor/github.com/prometheus/prometheus/discovery/registry.go
+++ b/vendor/github.com/prometheus/prometheus/discovery/registry.go
@@ -280,3 +280,13 @@ func RegisterSDMetrics(registerer prometheus.Registerer, rmm RefreshMetricsManag
}
return metrics, nil
}
+
+// RegisteredConfigNames returns the names of all registered service discovery providers.
+func RegisteredConfigNames() []string {
+ names := make([]string, 0, len(configNames))
+ for name := range configNames {
+ names = append(names, name)
+ }
+ sort.Strings(names)
+ return names
+}
diff --git a/vendor/github.com/prometheus/prometheus/model/histogram/float_histogram.go b/vendor/github.com/prometheus/prometheus/model/histogram/float_histogram.go
index c607448f38e..91fcac1cfbf 100644
--- a/vendor/github.com/prometheus/prometheus/model/histogram/float_histogram.go
+++ b/vendor/github.com/prometheus/prometheus/model/histogram/float_histogram.go
@@ -164,8 +164,8 @@ func (h *FloatHistogram) CopyToSchema(targetSchema int32) *FloatHistogram {
Sum: h.Sum,
}
- c.PositiveSpans, c.PositiveBuckets = reduceResolution(h.PositiveSpans, h.PositiveBuckets, h.Schema, targetSchema, false, false)
- c.NegativeSpans, c.NegativeBuckets = reduceResolution(h.NegativeSpans, h.NegativeBuckets, h.Schema, targetSchema, false, false)
+ c.PositiveSpans, c.PositiveBuckets = mustReduceResolution(h.PositiveSpans, h.PositiveBuckets, h.Schema, targetSchema, false, false)
+ c.NegativeSpans, c.NegativeBuckets = mustReduceResolution(h.NegativeSpans, h.NegativeBuckets, h.Schema, targetSchema, false, false)
return &c
}
@@ -393,13 +393,13 @@ func (h *FloatHistogram) Add(other *FloatHistogram) (res *FloatHistogram, counte
switch {
case other.Schema < h.Schema:
- hPositiveSpans, hPositiveBuckets = reduceResolution(hPositiveSpans, hPositiveBuckets, h.Schema, other.Schema, false, true)
- hNegativeSpans, hNegativeBuckets = reduceResolution(hNegativeSpans, hNegativeBuckets, h.Schema, other.Schema, false, true)
+ hPositiveSpans, hPositiveBuckets = mustReduceResolution(hPositiveSpans, hPositiveBuckets, h.Schema, other.Schema, false, true)
+ hNegativeSpans, hNegativeBuckets = mustReduceResolution(hNegativeSpans, hNegativeBuckets, h.Schema, other.Schema, false, true)
h.Schema = other.Schema
case other.Schema > h.Schema:
- otherPositiveSpans, otherPositiveBuckets = reduceResolution(otherPositiveSpans, otherPositiveBuckets, other.Schema, h.Schema, false, false)
- otherNegativeSpans, otherNegativeBuckets = reduceResolution(otherNegativeSpans, otherNegativeBuckets, other.Schema, h.Schema, false, false)
+ otherPositiveSpans, otherPositiveBuckets = mustReduceResolution(otherPositiveSpans, otherPositiveBuckets, other.Schema, h.Schema, false, false)
+ otherNegativeSpans, otherNegativeBuckets = mustReduceResolution(otherNegativeSpans, otherNegativeBuckets, other.Schema, h.Schema, false, false)
}
h.PositiveSpans, h.PositiveBuckets = addBuckets(h.Schema, h.ZeroThreshold, false, hPositiveSpans, hPositiveBuckets, otherPositiveSpans, otherPositiveBuckets)
@@ -459,12 +459,12 @@ func (h *FloatHistogram) Sub(other *FloatHistogram) (res *FloatHistogram, counte
switch {
case other.Schema < h.Schema:
- hPositiveSpans, hPositiveBuckets = reduceResolution(hPositiveSpans, hPositiveBuckets, h.Schema, other.Schema, false, true)
- hNegativeSpans, hNegativeBuckets = reduceResolution(hNegativeSpans, hNegativeBuckets, h.Schema, other.Schema, false, true)
+ hPositiveSpans, hPositiveBuckets = mustReduceResolution(hPositiveSpans, hPositiveBuckets, h.Schema, other.Schema, false, true)
+ hNegativeSpans, hNegativeBuckets = mustReduceResolution(hNegativeSpans, hNegativeBuckets, h.Schema, other.Schema, false, true)
h.Schema = other.Schema
case other.Schema > h.Schema:
- otherPositiveSpans, otherPositiveBuckets = reduceResolution(otherPositiveSpans, otherPositiveBuckets, other.Schema, h.Schema, false, false)
- otherNegativeSpans, otherNegativeBuckets = reduceResolution(otherNegativeSpans, otherNegativeBuckets, other.Schema, h.Schema, false, false)
+ otherPositiveSpans, otherPositiveBuckets = mustReduceResolution(otherPositiveSpans, otherPositiveBuckets, other.Schema, h.Schema, false, false)
+ otherNegativeSpans, otherNegativeBuckets = mustReduceResolution(otherNegativeSpans, otherNegativeBuckets, other.Schema, h.Schema, false, false)
}
h.PositiveSpans, h.PositiveBuckets = addBuckets(h.Schema, h.ZeroThreshold, true, hPositiveSpans, hPositiveBuckets, otherPositiveSpans, otherPositiveBuckets)
@@ -1050,16 +1050,21 @@ func (i *floatBucketIterator) Next() bool {
if i.spansIdx >= len(i.spans) {
return false
}
+ span := i.spans[i.spansIdx]
if i.schema == i.targetSchema {
// Fast path for the common case.
- span := i.spans[i.spansIdx]
if i.bucketsIdx == 0 {
// Seed origIdx for the first bucket.
i.currIdx = span.Offset
} else {
i.currIdx++
}
+ if i.bucketsIdx >= len(i.buckets) {
+ // This protects against index out of range panic, which
+ // can only happen with an invalid histogram.
+ return false
+ }
for i.idxInSpan >= span.Length {
// We have exhausted the current span and have to find a new
@@ -1080,7 +1085,6 @@ func (i *floatBucketIterator) Next() bool {
// Copy all of these into local variables so that we can forward to the
// next bucket and then roll back if needed.
origIdx, spansIdx, idxInSpan := i.origIdx, i.spansIdx, i.idxInSpan
- span := i.spans[spansIdx]
firstPass := true
i.currCount = 0
@@ -1092,6 +1096,14 @@ func (i *floatBucketIterator) Next() bool {
} else {
origIdx++
}
+ if i.bucketsIdx >= len(i.buckets) {
+ // This protects against index out of range panic, which
+ // can only happen with an invalid histogram.
+ if firstPass {
+ return false
+ }
+ break mergeLoop
+ }
for idxInSpan >= span.Length {
// We have exhausted the current span and have to find a new
// one. We even handle pathologic spans of length 0 here.
@@ -1152,6 +1164,11 @@ func (i *reverseFloatBucketIterator) Next() bool {
// We have exhausted the current span and have to find a new
// one. We'll even handle pathologic spans of length 0.
i.spansIdx--
+ if i.spansIdx < 0 {
+ // This protects against index out of range panic, which
+ // can only happen with an invalid histogram.
+ return false
+ }
i.idxInSpan = int32(i.spans[i.spansIdx].Length) - 1
i.currIdx -= i.spans[i.spansIdx+1].Offset
}
@@ -1565,25 +1582,40 @@ func addCustomBucketsWithMismatches(
}
// ReduceResolution reduces the float histogram's spans, buckets into target schema.
-// The target schema must be smaller than the current float histogram's schema.
-// This will panic if the histogram has custom buckets or if the target schema is
-// a custom buckets schema.
-func (h *FloatHistogram) ReduceResolution(targetSchema int32) *FloatHistogram {
+// An error is returned in the following cases:
+// - The target schema is not smaller than the current histogram's schema.
+// - The histogram has custom buckets.
+// - The target schema is a custom buckets schema.
+// - Any spans have an invalid offset.
+// - The spans are inconsistent with the number of buckets.
+func (h *FloatHistogram) ReduceResolution(targetSchema int32) error {
+ // Note that the follow three returns are not returning a
+ // histogram.Error because they are programming errors.
if h.UsesCustomBuckets() {
- panic("cannot reduce resolution when there are custom buckets")
+ return errors.New("cannot reduce resolution when there are custom buckets")
}
if IsCustomBucketsSchema(targetSchema) {
- panic("cannot reduce resolution to custom buckets schema")
+ return errors.New("cannot reduce resolution to custom buckets schema")
}
if targetSchema >= h.Schema {
- panic(fmt.Errorf("cannot reduce resolution from schema %d to %d", h.Schema, targetSchema))
+ return fmt.Errorf("cannot reduce resolution from schema %d to %d", h.Schema, targetSchema)
}
- h.PositiveSpans, h.PositiveBuckets = reduceResolution(h.PositiveSpans, h.PositiveBuckets, h.Schema, targetSchema, false, true)
- h.NegativeSpans, h.NegativeBuckets = reduceResolution(h.NegativeSpans, h.NegativeBuckets, h.Schema, targetSchema, false, true)
+ var err error
+
+ if h.PositiveSpans, h.PositiveBuckets, err = reduceResolution(
+ h.PositiveSpans, h.PositiveBuckets, h.Schema, targetSchema, false, true,
+ ); err != nil {
+ return err
+ }
+ if h.NegativeSpans, h.NegativeBuckets, err = reduceResolution(
+ h.NegativeSpans, h.NegativeBuckets, h.Schema, targetSchema, false, true,
+ ); err != nil {
+ return err
+ }
h.Schema = targetSchema
- return h
+ return nil
}
// checkSchemaAndBounds checks if two histograms are compatible because they
diff --git a/vendor/github.com/prometheus/prometheus/model/histogram/generic.go b/vendor/github.com/prometheus/prometheus/model/histogram/generic.go
index cd385407d53..649db769c7e 100644
--- a/vendor/github.com/prometheus/prometheus/model/histogram/generic.go
+++ b/vendor/github.com/prometheus/prometheus/model/histogram/generic.go
@@ -738,6 +738,8 @@ var exponentialBounds = [][]float64{
// deltas. Set it to false if the buckets contain absolute counts.
// Set inplace to true to reuse input slices and avoid allocations (otherwise
// new slices will be allocated for result).
+// The functions returns an error if there are too many or too few buckets for the spans
+// or if any span except the first has a negative offset.
func reduceResolution[IBC InternalBucketCount](
originSpans []Span,
originBuckets []IBC,
@@ -745,7 +747,7 @@ func reduceResolution[IBC InternalBucketCount](
targetSchema int32,
deltaBuckets bool,
inplace bool,
-) ([]Span, []IBC) {
+) ([]Span, []IBC, error) {
var (
targetSpans []Span // The spans in the target schema.
targetBuckets []IBC // The bucket counts in the target schema.
@@ -764,10 +766,18 @@ func reduceResolution[IBC InternalBucketCount](
targetBuckets = originBuckets[:0]
}
- for _, span := range originSpans {
+ for n, span := range originSpans {
+ if n > 0 && span.Offset < 0 {
+ return nil, nil, fmt.Errorf("span number %d with offset %d: %w", n+1, span.Offset, ErrHistogramSpanNegativeOffset)
+ }
// Determine the index of the first bucket in this span.
bucketIdx += span.Offset
for j := 0; j < int(span.Length); j++ {
+ // Protect against too few buckets in the origin.
+ if bucketCountIdx >= len(originBuckets) {
+ return nil, nil, fmt.Errorf("have %d buckets but spans need more: %w", len(originBuckets), ErrHistogramSpansBucketsMismatch)
+ }
+
// Determine the index of the bucket in the target schema from the index in the original schema.
targetBucketIdx = targetIdx(bucketIdx, originSchema, targetSchema)
@@ -826,12 +836,33 @@ func reduceResolution[IBC InternalBucketCount](
targetBuckets = append(targetBuckets, originBuckets[bucketCountIdx])
}
}
-
bucketIdx++
bucketCountIdx++
}
}
+ if bucketCountIdx != len(originBuckets) {
+ return nil, nil, fmt.Errorf("spans need %d buckets, have %d buckets: %w", bucketCountIdx, len(originBuckets), ErrHistogramSpansBucketsMismatch)
+ }
+ return targetSpans, targetBuckets, nil
+}
+// mustReduceResolution works like reduceResolution, but panics instead of
+// returning an error. Use mustReduceResolution if you are sure that the spans
+// and buckets are valid.
+func mustReduceResolution[IBC InternalBucketCount](
+ originSpans []Span,
+ originBuckets []IBC,
+ originSchema,
+ targetSchema int32,
+ deltaBuckets bool,
+ inplace bool,
+) ([]Span, []IBC) {
+ targetSpans, targetBuckets, err := reduceResolution(
+ originSpans, originBuckets, originSchema, targetSchema, deltaBuckets, inplace,
+ )
+ if err != nil {
+ panic(err)
+ }
return targetSpans, targetBuckets
}
diff --git a/vendor/github.com/prometheus/prometheus/model/histogram/histogram.go b/vendor/github.com/prometheus/prometheus/model/histogram/histogram.go
index a7d9ce80f01..5fc68ef9d02 100644
--- a/vendor/github.com/prometheus/prometheus/model/histogram/histogram.go
+++ b/vendor/github.com/prometheus/prometheus/model/histogram/histogram.go
@@ -14,6 +14,7 @@
package histogram
import (
+ "errors"
"fmt"
"math"
"slices"
@@ -515,6 +516,11 @@ func (r *regularBucketIterator) Next() bool {
r.currIdx += span.Offset
}
+ // This protects against index out of range panic, which
+ // can only happen with an invalid histogram.
+ if r.bucketsIdx >= len(r.buckets) {
+ return false
+ }
r.currCount += r.buckets[r.bucketsIdx]
r.idxInSpan++
r.bucketsIdx++
@@ -576,6 +582,11 @@ func (c *cumulativeBucketIterator) Next() bool {
c.initialized = true
}
+ // This protects against index out of range panic, which
+ // can only happen with an invalid histogram.
+ if c.posBucketsIdx >= len(c.h.PositiveBuckets) {
+ return false
+ }
c.currCount += c.h.PositiveBuckets[c.posBucketsIdx]
c.currCumulativeCount += uint64(c.currCount)
c.currUpper = getBound(c.currIdx, c.h.Schema, c.h.CustomValues)
@@ -607,26 +618,37 @@ func (c *cumulativeBucketIterator) At() Bucket[uint64] {
}
// ReduceResolution reduces the histogram's spans, buckets into target schema.
-// The target schema must be smaller than the current histogram's schema.
-// This will panic if the histogram has custom buckets or if the target schema is
-// a custom buckets schema.
-func (h *Histogram) ReduceResolution(targetSchema int32) *Histogram {
+// An error is returned in the following cases:
+// - The target schema is not smaller than the current histogram's schema.
+// - The histogram has custom buckets.
+// - The target schema is a custom buckets schema.
+// - Any spans have an invalid offset.
+// - The spans are inconsistent with the number of buckets.
+func (h *Histogram) ReduceResolution(targetSchema int32) error {
+ // Note that the follow three returns are not returning a
+ // histogram.Error because they are programming errors.
if h.UsesCustomBuckets() {
- panic("cannot reduce resolution when there are custom buckets")
+ return errors.New("cannot reduce resolution when there are custom buckets")
}
if IsCustomBucketsSchema(targetSchema) {
- panic("cannot reduce resolution to custom buckets schema")
+ return errors.New("cannot reduce resolution to custom buckets schema")
}
if targetSchema >= h.Schema {
- panic(fmt.Errorf("cannot reduce resolution from schema %d to %d", h.Schema, targetSchema))
+ return fmt.Errorf("cannot reduce resolution from schema %d to %d", h.Schema, targetSchema)
}
- h.PositiveSpans, h.PositiveBuckets = reduceResolution(
+ var err error
+
+ if h.PositiveSpans, h.PositiveBuckets, err = reduceResolution(
h.PositiveSpans, h.PositiveBuckets, h.Schema, targetSchema, true, true,
- )
- h.NegativeSpans, h.NegativeBuckets = reduceResolution(
+ ); err != nil {
+ return err
+ }
+ if h.NegativeSpans, h.NegativeBuckets, err = reduceResolution(
h.NegativeSpans, h.NegativeBuckets, h.Schema, targetSchema, true, true,
- )
+ ); err != nil {
+ return err
+ }
h.Schema = targetSchema
- return h
+ return nil
}
diff --git a/vendor/github.com/prometheus/prometheus/model/labels/labels_common.go b/vendor/github.com/prometheus/prometheus/model/labels/labels_common.go
index 5a3979784cb..ab82ae6a8fc 100644
--- a/vendor/github.com/prometheus/prometheus/model/labels/labels_common.go
+++ b/vendor/github.com/prometheus/prometheus/model/labels/labels_common.go
@@ -26,7 +26,9 @@ import (
const (
// MetricName is a special label name that represent a metric name.
//
- // Deprecated: Use schema.Metadata structure and its methods.
+ // Deprecated: Instead, consider using schema.Metadata structure and its methods for consistent metadata behaviour with the newly added __type__ and __unit__ labels. Alternatively use github.com/prometheus/common/model.MetricNameLabel for the direct replacement.
+ //
+ // labels package is providing label transport, agnostic to semantic meaning of each label.
MetricName = "__name__"
AlertName = "alertname"
diff --git a/vendor/github.com/prometheus/prometheus/model/labels/labels_dedupelabels.go b/vendor/github.com/prometheus/prometheus/model/labels/labels_dedupelabels.go
index 1e736c832ea..4518482c968 100644
--- a/vendor/github.com/prometheus/prometheus/model/labels/labels_dedupelabels.go
+++ b/vendor/github.com/prometheus/prometheus/model/labels/labels_dedupelabels.go
@@ -24,6 +24,9 @@ import (
"github.com/cespare/xxhash/v2"
)
+// ImplementationName is the name of the labels implementation.
+const ImplementationName = "dedupelabels"
+
// Labels is implemented by a SymbolTable and string holding name/value
// pairs encoded as indexes into the table in varint encoding.
// Names are in alphabetical order.
diff --git a/vendor/github.com/prometheus/prometheus/model/labels/labels_slicelabels.go b/vendor/github.com/prometheus/prometheus/model/labels/labels_slicelabels.go
index 21ad145c1cc..71dbcd00449 100644
--- a/vendor/github.com/prometheus/prometheus/model/labels/labels_slicelabels.go
+++ b/vendor/github.com/prometheus/prometheus/model/labels/labels_slicelabels.go
@@ -25,6 +25,9 @@ import (
"github.com/cespare/xxhash/v2"
)
+// ImplementationName is the name of the labels implementation.
+const ImplementationName = "slicelabels"
+
// Labels is a sorted set of labels. Order has to be guaranteed upon
// instantiation.
type Labels []Label
@@ -297,12 +300,9 @@ func FromStrings(ss ...string) Labels {
// Compare compares the two label sets.
// The result will be 0 if a==b, <0 if a < b, and >0 if a > b.
func Compare(a, b Labels) int {
- l := len(a)
- if len(b) < l {
- l = len(b)
- }
+ l := min(len(b), len(a))
- for i := 0; i < l; i++ {
+ for i := range l {
if a[i].Name != b[i].Name {
if a[i].Name < b[i].Name {
return -1
@@ -419,10 +419,7 @@ func (b *Builder) Labels() Labels {
return b.base
}
- expectedSize := len(b.base) + len(b.add) - len(b.del)
- if expectedSize < 1 {
- expectedSize = 1
- }
+ expectedSize := max(len(b.base)+len(b.add)-len(b.del), 1)
res := make(Labels, 0, expectedSize)
for _, l := range b.base {
if slices.Contains(b.del, l.Name) || contains(b.add, l.Name) {
diff --git a/vendor/github.com/prometheus/prometheus/model/labels/labels_stringlabels.go b/vendor/github.com/prometheus/prometheus/model/labels/labels_stringlabels.go
index f0872238029..1460e7db932 100644
--- a/vendor/github.com/prometheus/prometheus/model/labels/labels_stringlabels.go
+++ b/vendor/github.com/prometheus/prometheus/model/labels/labels_stringlabels.go
@@ -23,6 +23,9 @@ import (
"github.com/cespare/xxhash/v2"
)
+// ImplementationName is the name of the labels implementation.
+const ImplementationName = "stringlabels"
+
// Labels is implemented by a single flat string holding name/value pairs.
// Each name and value is preceded by its length, encoded as a single byte
// for size 0-254, or the following 3 bytes little-endian, if the first byte is 255.
diff --git a/vendor/github.com/prometheus/prometheus/model/metadata/metadata.go b/vendor/github.com/prometheus/prometheus/model/metadata/metadata.go
index 1b7e63e0f35..d2a91bb5603 100644
--- a/vendor/github.com/prometheus/prometheus/model/metadata/metadata.go
+++ b/vendor/github.com/prometheus/prometheus/model/metadata/metadata.go
@@ -1,4 +1,4 @@
-// Copyright 2022 The Prometheus Authors
+// 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
@@ -13,7 +13,11 @@
package metadata
-import "github.com/prometheus/common/model"
+import (
+ "strings"
+
+ "github.com/prometheus/common/model"
+)
// Metadata stores a series' metadata information.
type Metadata struct {
@@ -21,3 +25,21 @@ type Metadata struct {
Unit string `json:"unit"`
Help string `json:"help"`
}
+
+// IsEmpty returns true if metadata structure is empty, including unknown type case.
+func (m Metadata) IsEmpty() bool {
+ return (m.Type == "" || m.Type == model.MetricTypeUnknown) && m.Unit == "" && m.Help == ""
+}
+
+// Equals returns true if m is semantically the same as other metadata.
+func (m Metadata) Equals(other Metadata) bool {
+ if strings.Compare(m.Unit, other.Unit) != 0 || strings.Compare(m.Help, other.Help) != 0 {
+ return false
+ }
+
+ // Unknown means the same as empty string.
+ if m.Type == "" || m.Type == model.MetricTypeUnknown {
+ return other.Type == "" || other.Type == model.MetricTypeUnknown
+ }
+ return m.Type == other.Type
+}
diff --git a/vendor/github.com/prometheus/prometheus/model/textparse/interface.go b/vendor/github.com/prometheus/prometheus/model/textparse/interface.go
index 37b1b761a03..bbc52290ade 100644
--- a/vendor/github.com/prometheus/prometheus/model/textparse/interface.go
+++ b/vendor/github.com/prometheus/prometheus/model/textparse/interface.go
@@ -29,7 +29,7 @@ import (
type Parser interface {
// Series returns the bytes of a series with a simple float64 as a
// value, the timestamp if set, and the value of the current sample.
- // TODO(bwplotka): Similar to CreatedTimestamp, have ts == 0 meaning no timestamp provided.
+ // TODO(bwplotka): Similar to StartTimestamp, have ts == 0 meaning no timestamp provided.
// We already accepted in many places (PRW, proto parsing histograms) that 0 timestamp is not a
// a valid timestamp. If needed it can be represented as 0+1ms.
Series() ([]byte, *int64, float64)
@@ -38,7 +38,7 @@ type Parser interface {
// value, the timestamp if set, and the histogram in the current sample.
// Depending on the parsed input, the function returns an (integer) Histogram
// or a FloatHistogram, with the respective other return value being nil.
- // TODO(bwplotka): Similar to CreatedTimestamp, have ts == 0 meaning no timestamp provided.
+ // TODO(bwplotka): Similar to StartTimestamp, have ts == 0 meaning no timestamp provided.
// We already accepted in many places (PRW, proto parsing histograms) that 0 timestamp is not a
// a valid timestamp. If needed it can be represented as 0+1ms.
Histogram() ([]byte, *int64, *histogram.Histogram, *histogram.FloatHistogram)
@@ -76,10 +76,10 @@ type Parser interface {
// retrieved (including the case where no exemplars exist at all).
Exemplar(l *exemplar.Exemplar) bool
- // CreatedTimestamp returns the created timestamp (in milliseconds) for the
+ // StartTimestamp returns the created timestamp (in milliseconds) for the
// current sample. It returns 0 if it is unknown e.g. if it wasn't set or
// if the scrape protocol or metric type does not support created timestamps.
- CreatedTimestamp() int64
+ StartTimestamp() int64
// Next advances the parser to the next sample.
// It returns (EntryInvalid, io.EOF) if no samples were read.
@@ -146,9 +146,9 @@ type ParserOptions struct {
// that is also present as a native histogram. (Proto parsing only).
KeepClassicOnClassicAndNativeHistograms bool
- // OpenMetricsSkipCTSeries determines whether to skip `_created` timestamp series
+ // OpenMetricsSkipSTSeries determines whether to skip `_created` timestamp series
// during (OpenMetrics parsing only).
- OpenMetricsSkipCTSeries bool
+ OpenMetricsSkipSTSeries bool
// FallbackContentType specifies the fallback content type to use when the provided
// Content-Type header cannot be parsed or is not supported.
@@ -175,7 +175,7 @@ func New(b []byte, contentType string, st *labels.SymbolTable, opts ParserOption
switch mediaType {
case "application/openmetrics-text":
baseParser = NewOpenMetricsParser(b, st, func(o *openMetricsParserOptions) {
- o.skipCTSeries = opts.OpenMetricsSkipCTSeries
+ o.skipSTSeries = opts.OpenMetricsSkipSTSeries
o.enableTypeAndUnitLabels = opts.EnableTypeAndUnitLabels
})
case "application/vnd.google.protobuf":
diff --git a/vendor/github.com/prometheus/prometheus/model/textparse/nhcbparse.go b/vendor/github.com/prometheus/prometheus/model/textparse/nhcbparse.go
index 8ec541de8ab..79441e1f754 100644
--- a/vendor/github.com/prometheus/prometheus/model/textparse/nhcbparse.go
+++ b/vendor/github.com/prometheus/prometheus/model/textparse/nhcbparse.go
@@ -83,7 +83,7 @@ type NHCBParser struct {
fhNHCB *histogram.FloatHistogram
lsetNHCB labels.Labels
exemplars []exemplar.Exemplar
- ctNHCB int64
+ stNHCB int64
metricStringNHCB string
// Collates values from the classic histogram series to build
@@ -92,7 +92,7 @@ type NHCBParser struct {
tempNHCB convertnhcb.TempHistogram
tempExemplars []exemplar.Exemplar
tempExemplarCount int
- tempCT int64
+ tempST int64
// Remembers the last base histogram metric name (assuming it's
// a classic histogram) so we can tell if the next float series
@@ -159,16 +159,16 @@ func (p *NHCBParser) Exemplar(ex *exemplar.Exemplar) bool {
return p.parser.Exemplar(ex)
}
-func (p *NHCBParser) CreatedTimestamp() int64 {
+func (p *NHCBParser) StartTimestamp() int64 {
switch p.state {
case stateStart, stateInhibiting:
if p.entry == EntrySeries || p.entry == EntryHistogram {
- return p.parser.CreatedTimestamp()
+ return p.parser.StartTimestamp()
}
case stateCollecting:
- return p.tempCT
+ return p.tempST
case stateEmitting:
- return p.ctNHCB
+ return p.stNHCB
}
return 0
}
@@ -318,7 +318,7 @@ func (p *NHCBParser) handleClassicHistogramSeries(lset labels.Labels) bool {
func (p *NHCBParser) processClassicHistogramSeries(lset labels.Labels, name string, updateHist func(*convertnhcb.TempHistogram)) {
if p.state != stateCollecting {
p.storeClassicLabels(name)
- p.tempCT = p.parser.CreatedTimestamp()
+ p.tempST = p.parser.StartTimestamp()
p.state = stateCollecting
p.tempLsetNHCB = convertnhcb.GetHistogramMetricBase(lset, name)
}
@@ -352,7 +352,7 @@ func (p *NHCBParser) swapExemplars() {
}
// processNHCB converts the collated classic histogram series to NHCB and caches the info
-// to be returned to callers. Retruns true if the conversion was successful.
+// to be returned to callers. Returns true if the conversion was successful.
func (p *NHCBParser) processNHCB() bool {
if p.state != stateCollecting {
return false
@@ -385,13 +385,13 @@ func (p *NHCBParser) processNHCB() bool {
p.bytesNHCB = []byte(p.metricStringNHCB)
p.lsetNHCB = p.tempLsetNHCB
p.swapExemplars()
- p.ctNHCB = p.tempCT
+ p.stNHCB = p.tempST
p.state = stateEmitting
} else {
p.state = stateStart
}
p.tempNHCB.Reset()
p.tempExemplarCount = 0
- p.tempCT = 0
+ p.tempST = 0
return err == nil
}
diff --git a/vendor/github.com/prometheus/prometheus/model/textparse/openmetricsparse.go b/vendor/github.com/prometheus/prometheus/model/textparse/openmetricsparse.go
index 505e45fc40e..207ceb45737 100644
--- a/vendor/github.com/prometheus/prometheus/model/textparse/openmetricsparse.go
+++ b/vendor/github.com/prometheus/prometheus/model/textparse/openmetricsparse.go
@@ -103,34 +103,34 @@ type OpenMetricsParser struct {
hasExemplarTs bool
// Created timestamp parsing state.
- ct int64
- ctHashSet uint64
+ st int64
+ stHashSet uint64
// ignoreExemplar instructs the parser to not overwrite exemplars (to keep them while peeking ahead).
ignoreExemplar bool
// visitedMFName is the metric family name of the last visited metric when peeking ahead
- // for _created series during the execution of the CreatedTimestamp method.
+ // for _created series during the execution of the StartTimestamp method.
visitedMFName []byte
- skipCTSeries bool
+ skipSTSeries bool
enableTypeAndUnitLabels bool
}
type openMetricsParserOptions struct {
- skipCTSeries bool
+ skipSTSeries bool
enableTypeAndUnitLabels bool
}
type OpenMetricsOption func(*openMetricsParserOptions)
-// WithOMParserCTSeriesSkipped turns off exposing _created lines
+// WithOMParserSTSeriesSkipped turns off exposing _created lines
// as series, which makes those only used for parsing created timestamp
-// for `CreatedTimestamp` method purposes.
+// for `StartTimestamp` method purposes.
//
// It's recommended to use this option to avoid using _created lines for other
// purposes than created timestamp, but leave false by default for the
// best-effort compatibility.
-func WithOMParserCTSeriesSkipped() OpenMetricsOption {
+func WithOMParserSTSeriesSkipped() OpenMetricsOption {
return func(o *openMetricsParserOptions) {
- o.skipCTSeries = true
+ o.skipSTSeries = true
}
}
@@ -142,7 +142,7 @@ func WithOMParserTypeAndUnitLabels() OpenMetricsOption {
}
}
-// NewOpenMetricsParser returns a new parser for the byte slice with option to skip CT series parsing.
+// NewOpenMetricsParser returns a new parser for the byte slice with option to skip ST series parsing.
func NewOpenMetricsParser(b []byte, st *labels.SymbolTable, opts ...OpenMetricsOption) Parser {
options := &openMetricsParserOptions{}
@@ -153,7 +153,7 @@ func NewOpenMetricsParser(b []byte, st *labels.SymbolTable, opts ...OpenMetricsO
parser := &OpenMetricsParser{
l: &openMetricsLexer{b: b},
builder: labels.NewScratchBuilderWithSymbolTable(st, 16),
- skipCTSeries: options.skipCTSeries,
+ skipSTSeries: options.skipSTSeries,
enableTypeAndUnitLabels: options.enableTypeAndUnitLabels,
}
@@ -285,12 +285,12 @@ func (p *OpenMetricsParser) Exemplar(e *exemplar.Exemplar) bool {
return true
}
-// CreatedTimestamp returns the created timestamp for a current Metric if exists or nil.
+// StartTimestamp returns the created timestamp for a current Metric if exists or nil.
// NOTE(Maniktherana): Might use additional CPU/mem resources due to deep copy of parser required for peeking given 1.0 OM specification on _created series.
-func (p *OpenMetricsParser) CreatedTimestamp() int64 {
- if !typeRequiresCT(p.mtype) {
- // Not a CT supported metric type, fast path.
- p.ctHashSet = 0 // Use ctHashSet as a single way of telling "empty cache"
+func (p *OpenMetricsParser) StartTimestamp() int64 {
+ if !typeRequiresST(p.mtype) {
+ // Not a ST supported metric type, fast path.
+ p.stHashSet = 0 // Use stHashSet as a single way of telling "empty cache"
return 0
}
@@ -307,8 +307,8 @@ func (p *OpenMetricsParser) CreatedTimestamp() int64 {
currHash := p.seriesHash(&buf, currName)
// Check cache, perhaps we fetched something already.
- if currHash == p.ctHashSet && p.ct > 0 {
- return p.ct
+ if currHash == p.stHashSet && p.st > 0 {
+ return p.st
}
// Create a new lexer and other core state details to reset the parser once this function is done executing.
@@ -322,7 +322,7 @@ func (p *OpenMetricsParser) CreatedTimestamp() int64 {
resetStart := p.start
resetMType := p.mtype
- p.skipCTSeries = false
+ p.skipSTSeries = false
p.ignoreExemplar = true
defer func() {
p.l = resetLexer
@@ -334,38 +334,38 @@ func (p *OpenMetricsParser) CreatedTimestamp() int64 {
for {
eType, err := p.Next()
if err != nil {
- // This means p.Next() will give error too later on, so def no CT line found.
- // This might result in partial scrape with wrong/missing CT, but only
+ // This means p.Next() will give error too later on, so def no ST line found.
+ // This might result in partial scrape with wrong/missing ST, but only
// spec improvement would help.
- // TODO: Make sure OM 1.1/2.0 pass CT via metadata or exemplar-like to avoid this.
- p.resetCTParseValues()
+ // TODO: Make sure OM 1.1/2.0 pass ST via metadata or exemplar-like to avoid this.
+ p.resetSTParseValues()
return 0
}
if eType != EntrySeries {
- // Assume we hit different family, no CT line found.
- p.resetCTParseValues()
+ // Assume we hit different family, no ST line found.
+ p.resetSTParseValues()
return 0
}
peekedName := p.series[p.offsets[0]-p.start : p.offsets[1]-p.start]
if len(peekedName) < 8 || string(peekedName[len(peekedName)-8:]) != "_created" {
- // Not a CT line, search more.
+ // Not a ST line, search more.
continue
}
// Remove _created suffix.
peekedHash := p.seriesHash(&buf, peekedName[:len(peekedName)-8])
if peekedHash != currHash {
- // Found CT line for a different series, for our series no CT.
- p.resetCTParseValues()
+ // Found ST line for a different series, for our series no ST.
+ p.resetSTParseValues()
return 0
}
// All timestamps in OpenMetrics are Unix Epoch in seconds. Convert to milliseconds.
// https://github.com/prometheus/OpenMetrics/blob/v1.0.0/specification/OpenMetrics.md#timestamps
- ct := int64(p.val * 1000.0)
- p.setCTParseValues(ct, currHash, currName, true)
- return ct
+ st := int64(p.val * 1000.0)
+ p.setSTParseValues(st, currHash, currName, true)
+ return st
}
}
@@ -404,23 +404,23 @@ func (p *OpenMetricsParser) seriesHash(offsetsArr *[]byte, metricFamilyName []by
return hashedOffsets
}
-// setCTParseValues sets the parser to the state after CreatedTimestamp method was called and CT was found.
-// This is useful to prevent re-parsing the same series again and early return the CT value.
-func (p *OpenMetricsParser) setCTParseValues(ct int64, ctHashSet uint64, mfName []byte, skipCTSeries bool) {
- p.ct = ct
- p.ctHashSet = ctHashSet
+// setSTParseValues sets the parser to the state after StartTimestamp method was called and ST was found.
+// This is useful to prevent re-parsing the same series again and early return the ST value.
+func (p *OpenMetricsParser) setSTParseValues(st int64, stHashSet uint64, mfName []byte, skipSTSeries bool) {
+ p.st = st
+ p.stHashSet = stHashSet
p.visitedMFName = mfName
- p.skipCTSeries = skipCTSeries // Do we need to set it?
+ p.skipSTSeries = skipSTSeries // Do we need to set it?
}
-// resetCTParseValues resets the parser to the state before CreatedTimestamp method was called.
-func (p *OpenMetricsParser) resetCTParseValues() {
- p.ctHashSet = 0
- p.skipCTSeries = true
+// resetSTParseValues resets the parser to the state before StartTimestamp method was called.
+func (p *OpenMetricsParser) resetSTParseValues() {
+ p.stHashSet = 0
+ p.skipSTSeries = true
}
-// typeRequiresCT returns true if the metric type requires a _created timestamp.
-func typeRequiresCT(t model.MetricType) bool {
+// typeRequiresST returns true if the metric type requires a _created timestamp.
+func typeRequiresST(t model.MetricType) bool {
switch t {
case model.MetricTypeCounter, model.MetricTypeSummary, model.MetricTypeHistogram:
return true
@@ -544,7 +544,7 @@ func (p *OpenMetricsParser) Next() (Entry, error) {
if err := p.parseSeriesEndOfLine(p.nextToken()); err != nil {
return EntryInvalid, err
}
- if p.skipCTSeries && p.isCreatedSeries() {
+ if p.skipSTSeries && p.isCreatedSeries() {
return p.Next()
}
return EntrySeries, nil
@@ -565,7 +565,7 @@ func (p *OpenMetricsParser) Next() (Entry, error) {
if err := p.parseSeriesEndOfLine(t2); err != nil {
return EntryInvalid, err
}
- if p.skipCTSeries && p.isCreatedSeries() {
+ if p.skipSTSeries && p.isCreatedSeries() {
return p.Next()
}
return EntrySeries, nil
@@ -697,7 +697,7 @@ func (p *OpenMetricsParser) parseLVals(offsets []int, isExemplar bool) ([]int, e
func (p *OpenMetricsParser) isCreatedSeries() bool {
metricName := p.series[p.offsets[0]-p.start : p.offsets[1]-p.start]
// check length so the metric is longer than len("_created")
- if typeRequiresCT(p.mtype) && len(metricName) >= 8 && string(metricName[len(metricName)-8:]) == "_created" {
+ if typeRequiresST(p.mtype) && len(metricName) >= 8 && string(metricName[len(metricName)-8:]) == "_created" {
return true
}
return false
diff --git a/vendor/github.com/prometheus/prometheus/model/textparse/promparse.go b/vendor/github.com/prometheus/prometheus/model/textparse/promparse.go
index 2b4b750b4d7..4a75bcd8d8b 100644
--- a/vendor/github.com/prometheus/prometheus/model/textparse/promparse.go
+++ b/vendor/github.com/prometheus/prometheus/model/textparse/promparse.go
@@ -274,9 +274,9 @@ func (*PromParser) Exemplar(*exemplar.Exemplar) bool {
return false
}
-// CreatedTimestamp returns 0 as it's not implemented yet.
+// StartTimestamp returns 0 as it's not implemented yet.
// TODO(bwplotka): https://github.com/prometheus/prometheus/issues/12980
-func (*PromParser) CreatedTimestamp() int64 {
+func (*PromParser) StartTimestamp() int64 {
return 0
}
diff --git a/vendor/github.com/prometheus/prometheus/model/textparse/protobufparse.go b/vendor/github.com/prometheus/prometheus/model/textparse/protobufparse.go
index 800f02085e2..a48aa4af69a 100644
--- a/vendor/github.com/prometheus/prometheus/model/textparse/protobufparse.go
+++ b/vendor/github.com/prometheus/prometheus/model/textparse/protobufparse.go
@@ -19,7 +19,6 @@ import (
"fmt"
"io"
"math"
- "strings"
"unicode/utf8"
"github.com/gogo/protobuf/types"
@@ -400,24 +399,24 @@ func (p *ProtobufParser) Exemplar(ex *exemplar.Exemplar) bool {
return true
}
-// CreatedTimestamp returns CT or 0 if CT is not present on counters, summaries or histograms.
-func (p *ProtobufParser) CreatedTimestamp() int64 {
- var ct *types.Timestamp
+// StartTimestamp returns ST or 0 if ST is not present on counters, summaries or histograms.
+func (p *ProtobufParser) StartTimestamp() int64 {
+ var st *types.Timestamp
switch p.dec.GetType() {
case dto.MetricType_COUNTER:
- ct = p.dec.GetCounter().GetCreatedTimestamp()
+ st = p.dec.GetCounter().GetCreatedTimestamp()
case dto.MetricType_SUMMARY:
- ct = p.dec.GetSummary().GetCreatedTimestamp()
+ st = p.dec.GetSummary().GetCreatedTimestamp()
case dto.MetricType_HISTOGRAM, dto.MetricType_GAUGE_HISTOGRAM:
- ct = p.dec.GetHistogram().GetCreatedTimestamp()
+ st = p.dec.GetHistogram().GetCreatedTimestamp()
default:
}
- if ct == nil {
+ if st == nil {
return 0
}
// Same as the gogo proto types.TimestampFromProto but straight to integer.
// and without validation.
- return ct.GetSeconds()*1e3 + int64(ct.GetNanos())/1e6
+ return st.GetSeconds()*1e3 + int64(st.GetNanos())/1e6
}
// Next advances the parser to the next "sample" (emulating the behavior of a
@@ -466,16 +465,6 @@ func (p *ProtobufParser) Next() (Entry, error) {
default:
return EntryInvalid, fmt.Errorf("unknown metric type for metric %q: %s", name, p.dec.GetType())
}
- unit := p.dec.GetUnit()
- if len(unit) > 0 {
- if p.dec.GetType() == dto.MetricType_COUNTER && strings.HasSuffix(name, "_total") {
- if !strings.HasSuffix(name[:len(name)-6], unit) || len(name)-6 < len(unit)+1 || name[len(name)-6-len(unit)-1] != '_' {
- return EntryInvalid, fmt.Errorf("unit %q not a suffix of counter %q", unit, name)
- }
- } else if !strings.HasSuffix(name, unit) || len(name) < len(unit)+1 || name[len(name)-len(unit)-1] != '_' {
- return EntryInvalid, fmt.Errorf("unit %q not a suffix of metric %q", unit, name)
- }
- }
p.entryBytes.Reset()
p.entryBytes.WriteString(name)
p.state = EntryHelp
diff --git a/vendor/github.com/prometheus/prometheus/notifier/alertmanagerset.go b/vendor/github.com/prometheus/prometheus/notifier/alertmanagerset.go
index c47c9ea23ad..b6d1b8c4aa1 100644
--- a/vendor/github.com/prometheus/prometheus/notifier/alertmanagerset.go
+++ b/vendor/github.com/prometheus/prometheus/notifier/alertmanagerset.go
@@ -111,7 +111,8 @@ func (s *alertmanagerSet) sync(tgs []*targetgroup.Group) {
if _, ok := seen[us]; ok {
continue
}
- s.metrics.latency.DeleteLabelValues(us)
+ s.metrics.latencySummary.DeleteLabelValues(us)
+ s.metrics.latencyHistogram.DeleteLabelValues(us)
s.metrics.sent.DeleteLabelValues(us)
s.metrics.errors.DeleteLabelValues(us)
seen[us] = struct{}{}
diff --git a/vendor/github.com/prometheus/prometheus/notifier/manager.go b/vendor/github.com/prometheus/prometheus/notifier/manager.go
index 65adfd5c3ea..e37f59a2506 100644
--- a/vendor/github.com/prometheus/prometheus/notifier/manager.go
+++ b/vendor/github.com/prometheus/prometheus/notifier/manager.go
@@ -498,7 +498,9 @@ func (n *Manager) sendAll(alerts ...*Alert) bool {
amSetCovered.CompareAndSwap(k, false, true)
}
- n.metrics.latency.WithLabelValues(url).Observe(time.Since(begin).Seconds())
+ durationSeconds := time.Since(begin).Seconds()
+ n.metrics.latencySummary.WithLabelValues(url).Observe(durationSeconds)
+ n.metrics.latencyHistogram.WithLabelValues(url).Observe(durationSeconds)
n.metrics.sent.WithLabelValues(url).Add(float64(count))
wg.Done()
diff --git a/vendor/github.com/prometheus/prometheus/notifier/metric.go b/vendor/github.com/prometheus/prometheus/notifier/metric.go
index b9a55b3ec74..3f4abdda93c 100644
--- a/vendor/github.com/prometheus/prometheus/notifier/metric.go
+++ b/vendor/github.com/prometheus/prometheus/notifier/metric.go
@@ -13,10 +13,15 @@
package notifier
-import "github.com/prometheus/client_golang/prometheus"
+import (
+ "time"
+
+ "github.com/prometheus/client_golang/prometheus"
+)
type alertMetrics struct {
- latency *prometheus.SummaryVec
+ latencySummary *prometheus.SummaryVec
+ latencyHistogram *prometheus.HistogramVec
errors *prometheus.CounterVec
sent *prometheus.CounterVec
dropped prometheus.Counter
@@ -25,9 +30,13 @@ type alertMetrics struct {
alertmanagersDiscovered prometheus.GaugeFunc
}
-func newAlertMetrics(r prometheus.Registerer, queueCap int, queueLen, alertmanagersDiscovered func() float64) *alertMetrics {
+func newAlertMetrics(
+ r prometheus.Registerer,
+ queueCap int,
+ queueLen, alertmanagersDiscovered func() float64,
+) *alertMetrics {
m := &alertMetrics{
- latency: prometheus.NewSummaryVec(prometheus.SummaryOpts{
+ latencySummary: prometheus.NewSummaryVec(prometheus.SummaryOpts{
Namespace: namespace,
Subsystem: subsystem,
Name: "latency_seconds",
@@ -36,6 +45,19 @@ func newAlertMetrics(r prometheus.Registerer, queueCap int, queueLen, alertmanag
},
[]string{alertmanagerLabel},
),
+ latencyHistogram: prometheus.NewHistogramVec(prometheus.HistogramOpts{
+ Namespace: namespace,
+ Subsystem: subsystem,
+ Name: "latency_histogram_seconds",
+ Help: "Latency histogram for sending alert notifications.",
+
+ Buckets: []float64{.01, .1, 1, 10},
+ NativeHistogramBucketFactor: 1.1,
+ NativeHistogramMaxBucketNumber: 100,
+ NativeHistogramMinResetDuration: 1 * time.Hour,
+ },
+ []string{alertmanagerLabel},
+ ),
errors: prometheus.NewCounterVec(prometheus.CounterOpts{
Namespace: namespace,
Subsystem: subsystem,
@@ -80,7 +102,8 @@ func newAlertMetrics(r prometheus.Registerer, queueCap int, queueLen, alertmanag
if r != nil {
r.MustRegister(
- m.latency,
+ m.latencySummary,
+ m.latencyHistogram,
m.errors,
m.sent,
m.dropped,
diff --git a/vendor/github.com/prometheus/prometheus/promql/engine.go b/vendor/github.com/prometheus/prometheus/promql/engine.go
index 75fc9b05d34..5a08da121cd 100644
--- a/vendor/github.com/prometheus/prometheus/promql/engine.go
+++ b/vendor/github.com/prometheus/prometheus/promql/engine.go
@@ -49,6 +49,7 @@ import (
"github.com/prometheus/prometheus/storage"
"github.com/prometheus/prometheus/tsdb/chunkenc"
"github.com/prometheus/prometheus/util/annotations"
+ "github.com/prometheus/prometheus/util/features"
"github.com/prometheus/prometheus/util/logging"
"github.com/prometheus/prometheus/util/stats"
"github.com/prometheus/prometheus/util/zeropool"
@@ -76,15 +77,19 @@ const (
)
type engineMetrics struct {
- currentQueries prometheus.Gauge
- maxConcurrentQueries prometheus.Gauge
- queryLogEnabled prometheus.Gauge
- queryLogFailures prometheus.Counter
- queryQueueTime prometheus.Observer
- queryPrepareTime prometheus.Observer
- queryInnerEval prometheus.Observer
- queryResultSort prometheus.Observer
- querySamples prometheus.Counter
+ currentQueries prometheus.Gauge
+ maxConcurrentQueries prometheus.Gauge
+ queryLogEnabled prometheus.Gauge
+ queryLogFailures prometheus.Counter
+ queryQueueTime prometheus.Observer
+ queryQueueTimeHistogram prometheus.Observer
+ queryPrepareTime prometheus.Observer
+ queryPrepareTimeHistogram prometheus.Observer
+ queryInnerEval prometheus.Observer
+ queryInnerEvalHistogram prometheus.Observer
+ queryResultSort prometheus.Observer
+ queryResultSortHistogram prometheus.Observer
+ querySamples prometheus.Counter
}
type (
@@ -326,6 +331,9 @@ type EngineOpts struct {
EnableDelayedNameRemoval bool
// EnableTypeAndUnitLabels will allow PromQL Engine to make decisions based on the type and unit labels.
EnableTypeAndUnitLabels bool
+
+ // FeatureRegistry is the registry for tracking enabled/disabled features.
+ FeatureRegistry features.Collector
}
// Engine handles the lifetime of queries from beginning to end.
@@ -363,6 +371,19 @@ func NewEngine(opts EngineOpts) *Engine {
[]string{"slice"},
)
+ queryResultHistogram := prometheus.NewHistogramVec(prometheus.HistogramOpts{
+ Namespace: namespace,
+ Subsystem: subsystem,
+ Name: "query_duration_histogram_seconds",
+ Help: "The duration of various parts of PromQL query execution.",
+ Buckets: []float64{.01, .1, 1, 10},
+ NativeHistogramBucketFactor: 1.1,
+ NativeHistogramMaxBucketNumber: 100,
+ NativeHistogramMinResetDuration: 1 * time.Hour,
+ },
+ []string{"slice"},
+ )
+
metrics := &engineMetrics{
currentQueries: prometheus.NewGauge(prometheus.GaugeOpts{
Namespace: namespace,
@@ -394,10 +415,14 @@ func NewEngine(opts EngineOpts) *Engine {
Name: "query_samples_total",
Help: "The total number of samples loaded by all queries.",
}),
- queryQueueTime: queryResultSummary.WithLabelValues("queue_time"),
- queryPrepareTime: queryResultSummary.WithLabelValues("prepare_time"),
- queryInnerEval: queryResultSummary.WithLabelValues("inner_eval"),
- queryResultSort: queryResultSummary.WithLabelValues("result_sort"),
+ queryQueueTime: queryResultSummary.WithLabelValues("queue_time"),
+ queryQueueTimeHistogram: queryResultHistogram.WithLabelValues("queue_time"),
+ queryPrepareTime: queryResultSummary.WithLabelValues("prepare_time"),
+ queryPrepareTimeHistogram: queryResultHistogram.WithLabelValues("prepare_time"),
+ queryInnerEval: queryResultSummary.WithLabelValues("inner_eval"),
+ queryInnerEvalHistogram: queryResultHistogram.WithLabelValues("inner_eval"),
+ queryResultSort: queryResultSummary.WithLabelValues("result_sort"),
+ queryResultSortHistogram: queryResultHistogram.WithLabelValues("result_sort"),
}
if t := opts.ActiveQueryTracker; t != nil {
@@ -421,9 +446,22 @@ func NewEngine(opts EngineOpts) *Engine {
metrics.queryLogFailures,
metrics.querySamples,
queryResultSummary,
+ queryResultHistogram,
)
}
+ if r := opts.FeatureRegistry; r != nil {
+ r.Set(features.PromQL, "at_modifier", opts.EnableAtModifier)
+ r.Set(features.PromQL, "negative_offset", opts.EnableNegativeOffset)
+ r.Set(features.PromQL, "per_step_stats", opts.EnablePerStepStats)
+ r.Set(features.PromQL, "delayed_name_removal", opts.EnableDelayedNameRemoval)
+ r.Set(features.PromQL, "type_and_unit_labels", opts.EnableTypeAndUnitLabels)
+ r.Enable(features.PromQL, "per_query_lookback_delta")
+ r.Enable(features.PromQL, "subqueries")
+
+ parser.RegisterFeatures(r)
+ }
+
return &Engine{
timeout: opts.Timeout,
logger: opts.Logger,
@@ -701,7 +739,7 @@ func (ng *Engine) queueActive(ctx context.Context, q *query) (func(), error) {
if ng.activeQueryTracker == nil {
return func() {}, nil
}
- queueSpanTimer, _ := q.stats.GetSpanTimer(ctx, stats.ExecQueueTime, ng.metrics.queryQueueTime)
+ queueSpanTimer, _ := q.stats.GetSpanTimer(ctx, stats.ExecQueueTime, ng.metrics.queryQueueTime, ng.metrics.queryQueueTimeHistogram)
queryIndex, err := ng.activeQueryTracker.Insert(ctx, q.q)
queueSpanTimer.Finish()
return func() { ng.activeQueryTracker.Delete(queryIndex) }, err
@@ -717,7 +755,7 @@ func durationMilliseconds(d time.Duration) int64 {
// execEvalStmt evaluates the expression of an evaluation statement for the given time range.
func (ng *Engine) execEvalStmt(ctx context.Context, query *query, s *parser.EvalStmt) (parser.Value, annotations.Annotations, error) {
- prepareSpanTimer, ctxPrepare := query.stats.GetSpanTimer(ctx, stats.QueryPreparationTime, ng.metrics.queryPrepareTime)
+ prepareSpanTimer, ctxPrepare := query.stats.GetSpanTimer(ctx, stats.QueryPreparationTime, ng.metrics.queryPrepareTime, ng.metrics.queryPrepareTimeHistogram)
mint, maxt := FindMinMaxTime(s)
querier, err := query.queryable.Querier(mint, maxt)
if err != nil {
@@ -732,7 +770,7 @@ func (ng *Engine) execEvalStmt(ctx context.Context, query *query, s *parser.Eval
// Modify the offset of vector and matrix selectors for the @ modifier
// w.r.t. the start time since only 1 evaluation will be done on them.
setOffsetForAtModifier(timeMilliseconds(s.Start), s.Expr)
- evalSpanTimer, ctxInnerEval := query.stats.GetSpanTimer(ctx, stats.InnerEvalTime, ng.metrics.queryInnerEval)
+ evalSpanTimer, ctxInnerEval := query.stats.GetSpanTimer(ctx, stats.InnerEvalTime, ng.metrics.queryInnerEval, ng.metrics.queryInnerEvalHistogram)
// Instant evaluation. This is executed as a range evaluation with one step.
if s.Start.Equal(s.End) && s.Interval == 0 {
start := timeMilliseconds(s.Start)
@@ -835,7 +873,7 @@ func (ng *Engine) execEvalStmt(ctx context.Context, query *query, s *parser.Eval
}
func (ng *Engine) sortMatrixResult(ctx context.Context, query *query, mat Matrix) {
- sortSpanTimer, _ := query.stats.GetSpanTimer(ctx, stats.ResultSortTime, ng.metrics.queryResultSort)
+ sortSpanTimer, _ := query.stats.GetSpanTimer(ctx, stats.ResultSortTime, ng.metrics.queryResultSort, ng.metrics.queryResultSortHistogram)
sort.Sort(mat)
sortSpanTimer.Finish()
}
@@ -1137,15 +1175,20 @@ func (ev *evaluator) Eval(ctx context.Context, expr parser.Expr) (v parser.Value
v, ws = ev.eval(ctx, expr)
if ev.enableDelayedNameRemoval {
- ev.cleanupMetricLabels(v)
+ v = ev.cleanupMetricLabels(v)
}
return v, ws, nil
}
// EvalSeriesHelper stores extra information about a series.
type EvalSeriesHelper struct {
- // Used to map left-hand to right-hand in binary operations.
- signature string
+ // Ordinal number of join signature, used to map left-hand to right-hand in
+ // binary operations. For example given the following series, if
+ // the join signature is job, instance then:
+ // metric{job="a", instance="1", other="x"} -> sigOrdinal 0
+ // metric{job="a", instance="1", other="y"} -> sigOrdinal 0
+ // metric{job="a", instance="2", other="x"} -> sigOrdinal 1
+ sigOrdinal int
}
// EvalNodeHelper stores extra information and caches for evaluating a single node across steps.
@@ -1165,9 +1208,13 @@ type EvalNodeHelper struct {
lblResultBuf []byte
// For binary vector matching.
- rightSigs map[string]Sample
- matchedSigs map[string]map[uint64]struct{}
+ rightSigs map[int]Sample
+ matchedSigs map[int]map[uint64]struct{}
resultMetric map[string]labels.Labels
+ numSigs int
+
+ // For info series matching.
+ rightStrSigs map[string]Sample
// Additional options for the evaluation.
enableDelayedNameRemoval bool
@@ -1250,13 +1297,14 @@ func (enh *EvalNodeHelper) resetHistograms(inVec Vector, arg parser.Expr) annota
// the given funcCall with the values computed for each expression at that
// step. The return value is the combination into time series of all the
// function call results.
-// The prepSeries function (if provided) can be used to prepare the helper
+// The matching (if provided) can be used to prepare the helper
// for each series, then passed to each call funcCall.
-func (ev *evaluator) rangeEval(ctx context.Context, prepSeries func(labels.Labels, *EvalSeriesHelper), funcCall func([]Vector, Matrix, [][]EvalSeriesHelper, *EvalNodeHelper) (Vector, annotations.Annotations), exprs ...parser.Expr) (Matrix, annotations.Annotations) {
+func (ev *evaluator) rangeEval(ctx context.Context, matching *parser.VectorMatching, funcCall func([]Vector, Matrix, [][]EvalSeriesHelper, *EvalNodeHelper) (Vector, annotations.Annotations), exprs ...parser.Expr) (Matrix, annotations.Annotations) {
numSteps := int((ev.endTimestamp-ev.startTimestamp)/ev.interval) + 1
matrixes := make([]Matrix, len(exprs))
origMatrixes := make([]Matrix, len(exprs))
originalNumSamples := ev.currentSamples
+ useSignatures := matching != nil
var warnings annotations.Annotations
for i, e := range exprs {
@@ -1297,18 +1345,46 @@ func (ev *evaluator) rangeEval(ctx context.Context, prepSeries func(labels.Label
bufHelpers [][]EvalSeriesHelper // Buffer updated on each step
)
- // If the series preparation function is provided, we should run it for
- // every single series in the matrix.
- if prepSeries != nil {
+ if useSignatures {
+ var (
+ // Function to compute the join signature for each series.
+ sigf func(labels.Labels) string
+ buf = make([]byte, 0, 1024)
+ names = slices.Clone(matching.MatchingLabels)
+ )
+ if matching.On {
+ slices.Sort(names)
+ sigf = func(lset labels.Labels) string {
+ return string(lset.BytesWithLabels(buf, names...))
+ }
+ } else { // "without"
+ names = append([]string{labels.MetricName}, names...)
+ slices.Sort(names)
+ sigf = func(lset labels.Labels) string {
+ return string(lset.BytesWithoutLabels(buf, names...))
+ }
+ }
+
seriesHelpers = make([][]EvalSeriesHelper, len(exprs))
bufHelpers = make([][]EvalSeriesHelper, len(exprs))
+ signatureToOrdinal := make(map[string]int)
+
for i := range exprs {
seriesHelpers[i] = make([]EvalSeriesHelper, len(matrixes[i]))
bufHelpers[i] = make([]EvalSeriesHelper, len(matrixes[i]))
for si, series := range matrixes[i] {
- prepSeries(series.Metric, &seriesHelpers[i][si])
+ strSig := sigf(series.Metric)
+
+ if sigOrd, ok := signatureToOrdinal[strSig]; ok {
+ seriesHelpers[i][si] = EvalSeriesHelper{sigOrdinal: sigOrd}
+ continue
+ }
+
+ signatureToOrdinal[strSig] = enh.numSigs
+ seriesHelpers[i][si] = EvalSeriesHelper{sigOrdinal: enh.numSigs}
+ enh.numSigs++
}
}
}
@@ -1323,12 +1399,12 @@ func (ev *evaluator) rangeEval(ctx context.Context, prepSeries func(labels.Label
for i := range exprs {
var bh []EvalSeriesHelper
var sh []EvalSeriesHelper
- if prepSeries != nil {
+ if useSignatures {
bh = bufHelpers[i][:0]
sh = seriesHelpers[i]
}
vectors[i], bh = ev.gatherVector(ts, matrixes[i], vectors[i], bh, sh)
- if prepSeries != nil {
+ if useSignatures {
bufHelpers[i] = bh
}
}
@@ -2115,8 +2191,8 @@ func (ev *evaluator) eval(ctx context.Context, expr parser.Expr) (parser.Value,
mat[i].Histograms[j].H = mat[i].Histograms[j].H.Copy().Mul(-1)
}
}
- if !ev.enableDelayedNameRemoval && mat.ContainsSameLabelset() {
- ev.errorf("vector cannot contain metrics with the same labelset")
+ if !ev.enableDelayedNameRemoval {
+ mat = ev.mergeSeriesWithSameLabelset(mat)
}
}
return mat, ws
@@ -2129,27 +2205,21 @@ func (ev *evaluator) eval(ctx context.Context, expr parser.Expr) (parser.Value,
return append(enh.Out, Sample{F: val}), nil
}, e.LHS, e.RHS)
case lt == parser.ValueTypeVector && rt == parser.ValueTypeVector:
- // Function to compute the join signature for each series.
- buf := make([]byte, 0, 1024)
- sigf := signatureFunc(e.VectorMatching.On, buf, e.VectorMatching.MatchingLabels...)
- initSignatures := func(series labels.Labels, h *EvalSeriesHelper) {
- h.signature = sigf(series)
- }
switch e.Op {
case parser.LAND:
- return ev.rangeEval(ctx, initSignatures, func(v []Vector, _ Matrix, sh [][]EvalSeriesHelper, enh *EvalNodeHelper) (Vector, annotations.Annotations) {
+ return ev.rangeEval(ctx, e.VectorMatching, func(v []Vector, _ Matrix, sh [][]EvalSeriesHelper, enh *EvalNodeHelper) (Vector, annotations.Annotations) {
return ev.VectorAnd(v[0], v[1], e.VectorMatching, sh[0], sh[1], enh), nil
}, e.LHS, e.RHS)
case parser.LOR:
- return ev.rangeEval(ctx, initSignatures, func(v []Vector, _ Matrix, sh [][]EvalSeriesHelper, enh *EvalNodeHelper) (Vector, annotations.Annotations) {
+ return ev.rangeEval(ctx, e.VectorMatching, func(v []Vector, _ Matrix, sh [][]EvalSeriesHelper, enh *EvalNodeHelper) (Vector, annotations.Annotations) {
return ev.VectorOr(v[0], v[1], e.VectorMatching, sh[0], sh[1], enh), nil
}, e.LHS, e.RHS)
case parser.LUNLESS:
- return ev.rangeEval(ctx, initSignatures, func(v []Vector, _ Matrix, sh [][]EvalSeriesHelper, enh *EvalNodeHelper) (Vector, annotations.Annotations) {
+ return ev.rangeEval(ctx, e.VectorMatching, func(v []Vector, _ Matrix, sh [][]EvalSeriesHelper, enh *EvalNodeHelper) (Vector, annotations.Annotations) {
return ev.VectorUnless(v[0], v[1], e.VectorMatching, sh[0], sh[1], enh), nil
}, e.LHS, e.RHS)
default:
- return ev.rangeEval(ctx, initSignatures, func(v []Vector, _ Matrix, sh [][]EvalSeriesHelper, enh *EvalNodeHelper) (Vector, annotations.Annotations) {
+ return ev.rangeEval(ctx, e.VectorMatching, func(v []Vector, _ Matrix, sh [][]EvalSeriesHelper, enh *EvalNodeHelper) (Vector, annotations.Annotations) {
vec, err := ev.VectorBinop(e.Op, v[0], v[1], e.VectorMatching, e.ReturnBool, sh[0], sh[1], enh, e.PositionRange())
return vec, handleVectorBinopError(err, e)
}, e.LHS, e.RHS)
@@ -2720,16 +2790,15 @@ func (*evaluator) VectorAnd(lhs, rhs Vector, matching *parser.VectorMatching, lh
return nil // Short-circuit: AND with nothing is nothing.
}
- // The set of signatures for the right-hand side Vector.
- rightSigs := map[string]struct{}{}
- // Add all rhs samples to a map so we can easily find matches later.
+ // Ordinals of signatures present on the right-hand side.
+ rightSigOrdinalsPresent := make([]bool, enh.numSigs)
for _, sh := range rhsh {
- rightSigs[sh.signature] = struct{}{}
+ rightSigOrdinalsPresent[sh.sigOrdinal] = true
}
for i, ls := range lhs {
// If there's a matching entry in the right-hand side Vector, add the sample.
- if _, ok := rightSigs[lhsh[i].signature]; ok {
+ if rightSigOrdinalsPresent[lhsh[i].sigOrdinal] {
enh.Out = append(enh.Out, ls)
}
}
@@ -2748,15 +2817,15 @@ func (*evaluator) VectorOr(lhs, rhs Vector, matching *parser.VectorMatching, lhs
return enh.Out
}
- leftSigs := map[string]struct{}{}
+ leftSigOrdinalsPresent := make([]bool, enh.numSigs)
// Add everything from the left-hand-side Vector.
for i, ls := range lhs {
- leftSigs[lhsh[i].signature] = struct{}{}
+ leftSigOrdinalsPresent[lhsh[i].sigOrdinal] = true
enh.Out = append(enh.Out, ls)
}
// Add all right-hand side elements which have not been added from the left-hand side.
for j, rs := range rhs {
- if _, ok := leftSigs[rhsh[j].signature]; !ok {
+ if !leftSigOrdinalsPresent[rhsh[j].sigOrdinal] {
enh.Out = append(enh.Out, rs)
}
}
@@ -2774,13 +2843,14 @@ func (*evaluator) VectorUnless(lhs, rhs Vector, matching *parser.VectorMatching,
return enh.Out
}
- rightSigs := map[string]struct{}{}
+ // Ordinals of signatures present on the right-hand side.
+ rightSigOrdinalsPresent := make([]bool, enh.numSigs)
for _, sh := range rhsh {
- rightSigs[sh.signature] = struct{}{}
+ rightSigOrdinalsPresent[sh.sigOrdinal] = true
}
for i, ls := range lhs {
- if _, ok := rightSigs[lhsh[i].signature]; !ok {
+ if !rightSigOrdinalsPresent[lhsh[i].sigOrdinal] {
enh.Out = append(enh.Out, ls)
}
}
@@ -2804,22 +2874,20 @@ func (ev *evaluator) VectorBinop(op parser.ItemType, lhs, rhs Vector, matching *
lhsh, rhsh = rhsh, lhsh
}
- // All samples from the rhs hashed by the matching label/values.
+ // All samples from the rhs by their join signature ordinal.
if enh.rightSigs == nil {
- enh.rightSigs = make(map[string]Sample, len(enh.Out))
+ enh.rightSigs = make(map[int]Sample, len(enh.Out))
} else {
- for k := range enh.rightSigs {
- delete(enh.rightSigs, k)
- }
+ clear(enh.rightSigs)
}
rightSigs := enh.rightSigs
// Add all rhs samples to a map so we can easily find matches later.
for i, rs := range rhs {
- sig := rhsh[i].signature
+ sigOrd := rhsh[i].sigOrdinal
// The rhs is guaranteed to be the 'one' side. Having multiple samples
// with the same signature means that the matching is many-to-many.
- if duplSample, found := rightSigs[sig]; found {
+ if duplSample, found := rightSigs[sigOrd]; found {
// oneSide represents which side of the vector represents the 'one' in the many-to-one relationship.
oneSide := "right"
if matching.Card == parser.CardOneToMany {
@@ -2830,17 +2898,15 @@ func (ev *evaluator) VectorBinop(op parser.ItemType, lhs, rhs Vector, matching *
ev.errorf("found duplicate series for the match group %s on the %s hand-side of the operation: [%s, %s]"+
";many-to-many matching not allowed: matching labels must be unique on one side", matchedLabels.String(), oneSide, rs.Metric.String(), duplSample.Metric.String())
}
- rightSigs[sig] = rs
+ rightSigs[sigOrd] = rs
}
- // Tracks the match-signature. For one-to-one operations the value is nil. For many-to-one
- // the value is a set of signatures to detect duplicated result elements.
+ // Tracks the matching by signature ordinals. For one-to-one operations the value is nil.
+ // For many-to-one the value is a set of hashes to detect duplicated result elements.
if enh.matchedSigs == nil {
- enh.matchedSigs = make(map[string]map[uint64]struct{}, len(rightSigs))
+ enh.matchedSigs = make(map[int]map[uint64]struct{}, len(rightSigs))
} else {
- for k := range enh.matchedSigs {
- delete(enh.matchedSigs, k)
- }
+ clear(enh.matchedSigs)
}
matchedSigs := enh.matchedSigs
@@ -2848,9 +2914,9 @@ func (ev *evaluator) VectorBinop(op parser.ItemType, lhs, rhs Vector, matching *
// the binary operation.
var lastErr error
for i, ls := range lhs {
- sig := lhsh[i].signature
+ sigOrd := lhsh[i].sigOrdinal
- rs, found := rightSigs[sig] // Look for a match in the rhs Vector.
+ rs, found := rightSigs[sigOrd] // Look for a match in the rhs Vector.
if !found {
continue
}
@@ -2870,27 +2936,25 @@ func (ev *evaluator) VectorBinop(op parser.ItemType, lhs, rhs Vector, matching *
if info != nil {
lastErr = info
}
- switch {
- case returnBool:
+ if returnBool {
histogramValue = nil
if keep {
floatValue = 1.0
} else {
floatValue = 0.0
}
- case !keep:
- continue
}
+
metric := resultMetric(ls.Metric, rs.Metric, op, matching, enh)
if !ev.enableDelayedNameRemoval && returnBool {
metric = metric.DropReserved(schema.IsMetadataLabel)
}
- insertedSigs, exists := matchedSigs[sig]
+ insertedSigs, exists := matchedSigs[sigOrd]
if matching.Card == parser.CardOneToOne {
if exists {
ev.errorf("multiple matches for labels: many-to-one matching must be explicit (group_left/group_right)")
}
- matchedSigs[sig] = nil // Set existence to true.
+ matchedSigs[sigOrd] = nil // Set existence to true.
} else {
// In many-to-one matching the grouping labels have to ensure a unique metric
// for the result Vector. Check whether those labels have already been added for
@@ -2899,13 +2963,17 @@ func (ev *evaluator) VectorBinop(op parser.ItemType, lhs, rhs Vector, matching *
if !exists {
insertedSigs = map[uint64]struct{}{}
- matchedSigs[sig] = insertedSigs
+ matchedSigs[sigOrd] = insertedSigs
} else if _, duplicate := insertedSigs[insertSig]; duplicate {
ev.errorf("multiple matches for labels: grouping labels must ensure unique matches")
}
insertedSigs[insertSig] = struct{}{}
}
+ if !keep && !returnBool {
+ continue
+ }
+
enh.Out = append(enh.Out, Sample{
Metric: metric,
F: floatValue,
@@ -2916,20 +2984,6 @@ func (ev *evaluator) VectorBinop(op parser.ItemType, lhs, rhs Vector, matching *
return enh.Out, lastErr
}
-func signatureFunc(on bool, b []byte, names ...string) func(labels.Labels) string {
- if on {
- slices.Sort(names)
- return func(lset labels.Labels) string {
- return string(lset.BytesWithLabels(b, names...))
- }
- }
- names = append([]string{labels.MetricName}, names...)
- slices.Sort(names)
- return func(lset labels.Labels) string {
- return string(lset.BytesWithoutLabels(b, names...))
- }
-}
-
// resultMetric returns the metric for the given sample(s) based on the Vector
// binary operation and the matching options.
func resultMetric(lhs, rhs labels.Labels, op parser.ItemType, matching *parser.VectorMatching, enh *EvalNodeHelper) labels.Labels {
@@ -2937,7 +2991,6 @@ func resultMetric(lhs, rhs labels.Labels, op parser.ItemType, matching *parser.V
enh.resultMetric = make(map[string]labels.Labels, len(enh.Out))
}
- enh.resetBuilder(lhs)
buf := bytes.NewBuffer(enh.lblResultBuf[:0])
enh.lblBuf = lhs.Bytes(enh.lblBuf)
buf.Write(enh.lblBuf)
@@ -2950,6 +3003,7 @@ func resultMetric(lhs, rhs labels.Labels, op parser.ItemType, matching *parser.V
}
str := string(enh.lblResultBuf)
+ enh.resetBuilder(lhs)
if changesMetricSchema(op) {
// Setting empty Metadata causes the deletion of those if they exists.
schema.Metadata{}.SetToLabels(enh.lb)
@@ -3058,7 +3112,6 @@ func scalarBinop(op parser.ItemType, lhs, rhs float64) float64 {
// vectorElemBinop evaluates a binary operation between two Vector elements.
func vectorElemBinop(op parser.ItemType, lhs, rhs float64, hlhs, hrhs *histogram.FloatHistogram, pos posrange.PositionRange) (res float64, resH *histogram.FloatHistogram, keep bool, info, err error) {
- opName := parser.ItemTypeStr[op]
switch {
case hlhs == nil && hrhs == nil:
{
@@ -3097,7 +3150,7 @@ func vectorElemBinop(op parser.ItemType, lhs, rhs float64, hlhs, hrhs *histogram
case parser.MUL:
return 0, hrhs.Copy().Mul(lhs).Compact(0), true, nil, nil
case parser.ADD, parser.SUB, parser.DIV, parser.POW, parser.MOD, parser.EQLC, parser.NEQ, parser.GTR, parser.LSS, parser.GTE, parser.LTE, parser.ATAN2:
- return 0, nil, false, nil, annotations.NewIncompatibleTypesInBinOpInfo("float", opName, "histogram", pos)
+ return 0, nil, false, nil, annotations.NewIncompatibleTypesInBinOpInfo("float", parser.ItemTypeStr[op], "histogram", pos)
}
}
case hlhs != nil && hrhs == nil:
@@ -3108,7 +3161,7 @@ func vectorElemBinop(op parser.ItemType, lhs, rhs float64, hlhs, hrhs *histogram
case parser.DIV:
return 0, hlhs.Copy().Div(rhs).Compact(0), true, nil, nil
case parser.ADD, parser.SUB, parser.POW, parser.MOD, parser.EQLC, parser.NEQ, parser.GTR, parser.LSS, parser.GTE, parser.LTE, parser.ATAN2:
- return 0, nil, false, nil, annotations.NewIncompatibleTypesInBinOpInfo("histogram", opName, "float", pos)
+ return 0, nil, false, nil, annotations.NewIncompatibleTypesInBinOpInfo("histogram", parser.ItemTypeStr[op], "float", pos)
}
}
case hlhs != nil && hrhs != nil:
@@ -3148,7 +3201,7 @@ func vectorElemBinop(op parser.ItemType, lhs, rhs float64, hlhs, hrhs *histogram
// This operation expects that both histograms are compacted.
return 0, hlhs, !hlhs.Equals(hrhs), nil, nil
case parser.MUL, parser.DIV, parser.POW, parser.MOD, parser.GTR, parser.LSS, parser.GTE, parser.LTE, parser.ATAN2:
- return 0, nil, false, nil, annotations.NewIncompatibleTypesInBinOpInfo("histogram", opName, "histogram", pos)
+ return 0, nil, false, nil, annotations.NewIncompatibleTypesInBinOpInfo("histogram", parser.ItemTypeStr[op], "histogram", pos)
}
}
}
@@ -3172,6 +3225,7 @@ type groupedAggregation struct {
incrementalMean bool // True after reverting to incremental calculation of the mean value.
counterResetSeen bool // Counter reset hint CounterReset seen. Currently only used for histogram samples.
notCounterResetSeen bool // Counter reset hint NotCounterReset seen. Currently only used for histogram samples.
+ dropName bool // True if any sample in this group has DropName set.
}
// aggregation evaluates sum, avg, count, stdvar, stddev or quantile at one timestep on inputMatrix.
@@ -3200,6 +3254,7 @@ func (ev *evaluator) aggregation(e *parser.AggregateExpr, q float64, inputMatrix
floatMean: f,
incompatibleHistograms: false,
groupCount: 1,
+ dropName: inputMatrix[si].DropName,
}
switch op {
case parser.AVG, parser.SUM:
@@ -3256,6 +3311,10 @@ func (ev *evaluator) aggregation(e *parser.AggregateExpr, q float64, inputMatrix
continue
}
+ if inputMatrix[si].DropName {
+ group.dropName = true
+ }
+
switch op {
case parser.SUM:
if h != nil {
@@ -3494,7 +3553,7 @@ func (ev *evaluator) aggregation(e *parser.AggregateExpr, q float64, inputMatrix
ss := &outputMatrix[ri]
addToSeries(ss, enh.Ts, aggr.floatValue, aggr.histogramValue, numSteps)
- ss.DropName = inputMatrix[ri].DropName
+ ss.DropName = aggr.dropName
}
return annos
@@ -3773,7 +3832,7 @@ func (*evaluator) aggregationCountValues(e *parser.AggregateExpr, grouping []str
return enh.Out, nil
}
-func (ev *evaluator) cleanupMetricLabels(v parser.Value) {
+func (ev *evaluator) cleanupMetricLabels(v parser.Value) parser.Value {
if v.Type() == parser.ValueTypeMatrix {
mat := v.(Matrix)
for i := range mat {
@@ -3781,9 +3840,7 @@ func (ev *evaluator) cleanupMetricLabels(v parser.Value) {
mat[i].Metric = mat[i].Metric.DropReserved(schema.IsMetadataLabel)
}
}
- if mat.ContainsSameLabelset() {
- ev.errorf("vector cannot contain metrics with the same labelset")
- }
+ return ev.mergeSeriesWithSameLabelset(mat)
} else if v.Type() == parser.ValueTypeVector {
vec := v.(Vector)
for i := range vec {
@@ -3794,7 +3851,75 @@ func (ev *evaluator) cleanupMetricLabels(v parser.Value) {
if vec.ContainsSameLabelset() {
ev.errorf("vector cannot contain metrics with the same labelset")
}
+ return vec
}
+ return v
+}
+
+// mergeSeriesWithSameLabelset merges series in a matrix that have the same labelset
+// after __name__ label removal. This happens when delayed name removal is enabled and
+// operations like OR combine series that originally had different names but end up
+// with the same labelset after dropping the name. If series with the same labelset
+// have overlapping timestamps, an error is returned.
+func (ev *evaluator) mergeSeriesWithSameLabelset(mat Matrix) Matrix {
+ if len(mat) <= 1 {
+ return mat
+ }
+
+ // Fast path: check if there are any duplicate labelsets without allocating.
+ // This is the common case and we want to avoid allocations.
+ if !mat.ContainsSameLabelset() {
+ return mat
+ }
+
+ // Slow path: there are duplicates, so we need to merge series with non-overlapping timestamps.
+ // Group series by their labelset hash.
+ seriesByHash := make(map[uint64][]int)
+ for i := range mat {
+ hash := mat[i].Metric.Hash()
+ seriesByHash[hash] = append(seriesByHash[hash], i)
+ }
+
+ // Merge series with the same labelset.
+ merged := make(Matrix, 0, len(seriesByHash))
+ for _, indices := range seriesByHash {
+ if len(indices) == 1 {
+ // No collision, add as-is.
+ merged = append(merged, mat[indices[0]])
+ continue
+ }
+
+ // Multiple series with the same labelset - merge all samples.
+ base := mat[indices[0]]
+ for _, idx := range indices[1:] {
+ base.Floats = append(base.Floats, mat[idx].Floats...)
+ base.Histograms = append(base.Histograms, mat[idx].Histograms...)
+ }
+
+ // Sort merged samples by timestamp.
+ sort.Slice(base.Floats, func(i, j int) bool {
+ return base.Floats[i].T < base.Floats[j].T
+ })
+ sort.Slice(base.Histograms, func(i, j int) bool {
+ return base.Histograms[i].T < base.Histograms[j].T
+ })
+
+ // Check for duplicate timestamps in sorted samples.
+ for i := 1; i < len(base.Floats); i++ {
+ if base.Floats[i].T == base.Floats[i-1].T {
+ ev.errorf("vector cannot contain metrics with the same labelset")
+ }
+ }
+ for i := 1; i < len(base.Histograms); i++ {
+ if base.Histograms[i].T == base.Histograms[i-1].T {
+ ev.errorf("vector cannot contain metrics with the same labelset")
+ }
+ }
+
+ merged = append(merged, base)
+ }
+
+ return merged
}
func addToSeries(ss *Series, ts int64, f float64, h *histogram.FloatHistogram, numSteps int) {
@@ -3838,13 +3963,13 @@ func handleVectorBinopError(err error, e *parser.BinaryExpr) annotations.Annotat
if err == nil {
return nil
}
- op := parser.ItemTypeStr[e.Op]
- pos := e.PositionRange()
if errors.Is(err, annotations.PromQLInfo) || errors.Is(err, annotations.PromQLWarning) {
return annotations.New().Add(err)
}
// TODO(NeerajGartia21): Test the exact annotation output once the testing framework can do so.
if errors.Is(err, histogram.ErrHistogramsIncompatibleSchema) {
+ op := parser.ItemTypeStr[e.Op]
+ pos := e.PositionRange()
return annotations.New().Add(annotations.NewIncompatibleBucketLayoutInBinOpWarning(op, pos))
}
return nil
@@ -4132,13 +4257,17 @@ func makeInt64Pointer(val int64) *int64 {
// RatioSampler allows unit-testing (previously: Randomizer).
type RatioSampler interface {
- // Return this sample "offset" between [0.0, 1.0]
- sampleOffset(ts int64, sample *Sample) float64
- AddRatioSample(r float64, sample *Sample) bool
+ // SampleOffset returns this sample "offset" between [0.0, 1.0].
+ SampleOffset(metric *labels.Labels) float64
+ // AddRatioSample reports whether the sampling offset for the given sample falls within the specified ratio limit.
+ AddRatioSample(ratioLimit float64, sample *Sample) bool
+ // AddRatioSampleWithOffset reports whether the given sampling offset falls within the specified ratio limit.
+ AddRatioSampleWithOffset(ratioLimit, sampleOffset float64) bool
}
// HashRatioSampler uses Hash(labels.String()) / maxUint64 as a "deterministic"
// value in [0.0, 1.0].
+// It is a utility used for limit_ratio aggregations.
type HashRatioSampler struct{}
var ratiosampler RatioSampler = NewHashRatioSampler()
@@ -4147,14 +4276,42 @@ func NewHashRatioSampler() *HashRatioSampler {
return &HashRatioSampler{}
}
-func (*HashRatioSampler) sampleOffset(_ int64, sample *Sample) float64 {
+// SampleOffset returns a deterministic sampling offset in the range [0, 1)
+// derived from the hash of the provided metric labels.
+//
+// The offset is computed by normalizing the 64-bit hash value of the label set
+// to a float64 fraction of math.MaxUint64. This ensures that metrics with the
+// same label set always produce the same offset, while different label sets
+// produce uniformly distributed offsets suitable for sampling decisions.
+func (*HashRatioSampler) SampleOffset(metric *labels.Labels) float64 {
const (
float64MaxUint64 = float64(math.MaxUint64)
)
- return float64(sample.Metric.Hash()) / float64MaxUint64
+ return float64(metric.Hash()) / float64MaxUint64
}
+// AddRatioSample returns a bool indicating if the sampling offset for the given sample is
+// within the given ratio limit.
+//
+// See SampleOffset() for further details on the sample offset.
+// See AddRatioSampleWithOffset() for further details on the ratioLimit and sampling offset comparison.
func (s *HashRatioSampler) AddRatioSample(ratioLimit float64, sample *Sample) bool {
+ sampleOffset := s.SampleOffset(&sample.Metric)
+ return s.AddRatioSampleWithOffset(ratioLimit, sampleOffset)
+}
+
+// AddRatioSampleWithOffset reports whether the given sampling offset falls within
+// the specified ratio limit.
+//
+// The ratioLimit must be in the range [-1, 1]. The sampleOffset should be derived
+// using SampleOffset().
+//
+// When ratioLimit >= 0, the function returns true if sampleOffset < ratioLimit.
+// When ratioLimit < 0, the function returns true if sampleOffset >= 1 + ratioLimit.
+//
+// Note that this method could be moved into AddRatioSample and removed from the Prometheus codebase,
+// but it is useful for downstream projects using this code as a library.
+func (*HashRatioSampler) AddRatioSampleWithOffset(ratioLimit, sampleOffset float64) bool {
// If ratioLimit >= 0: add sample if sampleOffset is lesser than ratioLimit
//
// 0.0 ratioLimit 1.0
@@ -4175,7 +4332,6 @@ func (s *HashRatioSampler) AddRatioSample(ratioLimit float64, sample *Sample) bo
// e.g.:
// sampleOffset==0.3 && ratioLimit==-0.6
// 0.3 >= 0.4 ? --> don't add sample
- sampleOffset := s.sampleOffset(sample.T, sample)
return (ratioLimit >= 0 && sampleOffset < ratioLimit) ||
(ratioLimit < 0 && sampleOffset >= (1.0+ratioLimit))
}
diff --git a/vendor/github.com/prometheus/prometheus/promql/functions.go b/vendor/github.com/prometheus/prometheus/promql/functions.go
index ca8cfdce154..f844bf5ada5 100644
--- a/vendor/github.com/prometheus/prometheus/promql/functions.go
+++ b/vendor/github.com/prometheus/prometheus/promql/functions.go
@@ -400,6 +400,7 @@ func histogramRate(points []HPoint, isCounter bool, metricName string, pos posra
annos.Add(annotations.NewNativeHistogramNotGaugeWarning(metricName, pos))
}
+ h.CounterResetHint = histogram.GaugeType
return h.Compact(0), annos
}
@@ -1709,15 +1710,19 @@ func funcHistogramQuantile(vectorVals []Vector, _ Matrix, args parser.Expression
// pickFirstSampleIndex returns the index of the last sample before
// or at the range start, or 0 if none exist before the range start.
-// If the vector selector is not anchored, it always returns 0.
-func pickFirstSampleIndex(floats []FPoint, args parser.Expressions, enh *EvalNodeHelper) int {
+// If the vector selector is not anchored, it always returns 0, true.
+// The second return value is false if there are no samples in range (for anchored selectors).
+func pickFirstSampleIndex(floats []FPoint, args parser.Expressions, enh *EvalNodeHelper) (int, bool) {
ms := args[0].(*parser.MatrixSelector)
vs := ms.VectorSelector.(*parser.VectorSelector)
if !vs.Anchored {
- return 0
+ return 0, true
}
rangeStart := enh.Ts - durationMilliseconds(ms.Range+vs.Offset)
- return max(0, sort.Search(len(floats)-1, func(i int) bool { return floats[i].T > rangeStart })-1)
+ if len(floats) == 0 || floats[len(floats)-1].T <= rangeStart {
+ return 0, false
+ }
+ return max(0, sort.Search(len(floats)-1, func(i int) bool { return floats[i].T > rangeStart })-1), true
}
// === resets(Matrix parser.ValueTypeMatrix) (Vector, Annotations) ===
@@ -1730,7 +1735,10 @@ func funcResets(_ []Vector, matrixVal Matrix, args parser.Expressions, enh *Eval
}
var prevSample, curSample Sample
- firstSampleIndex := pickFirstSampleIndex(floats, args, enh)
+ firstSampleIndex, found := pickFirstSampleIndex(floats, args, enh)
+ if !found {
+ return enh.Out, nil
+ }
for iFloat, iHistogram := firstSampleIndex, 0; iFloat < len(floats) || iHistogram < len(histograms); {
switch {
// Process a float sample if no histogram sample remains or its timestamp is earlier.
@@ -1776,7 +1784,10 @@ func funcChanges(_ []Vector, matrixVal Matrix, args parser.Expressions, enh *Eva
}
var prevSample, curSample Sample
- firstSampleIndex := pickFirstSampleIndex(floats, args, enh)
+ firstSampleIndex, found := pickFirstSampleIndex(floats, args, enh)
+ if !found {
+ return enh.Out, nil
+ }
for iFloat, iHistogram := firstSampleIndex, 0; iFloat < len(floats) || iHistogram < len(histograms); {
switch {
// Process a float sample if no histogram sample remains or its timestamp is earlier.
@@ -1848,11 +1859,8 @@ func (ev *evaluator) evalLabelReplace(ctx context.Context, args parser.Expressio
}
}
}
- if matrix.ContainsSameLabelset() {
- ev.errorf("vector cannot contain metrics with the same labelset")
- }
- return matrix, ws
+ return ev.mergeSeriesWithSameLabelset(matrix), ws
}
// === Vector(s Scalar) (Vector, Annotations) ===
@@ -1902,11 +1910,8 @@ func (ev *evaluator) evalLabelJoin(ctx context.Context, args parser.Expressions)
matrix[i].DropName = el.DropName
}
}
- if matrix.ContainsSameLabelset() {
- ev.errorf("vector cannot contain metrics with the same labelset")
- }
- return matrix, ws
+ return ev.mergeSeriesWithSameLabelset(matrix), ws
}
// Common code for date related functions.
diff --git a/vendor/github.com/prometheus/prometheus/promql/info.go b/vendor/github.com/prometheus/prometheus/promql/info.go
index 0067fce845b..d5ffda6af2b 100644
--- a/vendor/github.com/prometheus/prometheus/promql/info.go
+++ b/vendor/github.com/prometheus/prometheus/promql/info.go
@@ -337,10 +337,10 @@ func (ev *evaluator) combineWithInfoVector(base, info Vector, ignoreSeries map[u
}
// All samples from the info Vector hashed by the matching label/values.
- if enh.rightSigs == nil {
- enh.rightSigs = make(map[string]Sample, len(enh.Out))
+ if enh.rightStrSigs == nil {
+ enh.rightStrSigs = make(map[string]Sample, len(enh.Out))
} else {
- clear(enh.rightSigs)
+ clear(enh.rightStrSigs)
}
for _, s := range info {
@@ -351,7 +351,7 @@ func (ev *evaluator) combineWithInfoVector(base, info Vector, ignoreSeries map[u
origT := int64(s.F)
sig := infoSigs[s.Metric.Hash()]
- if existing, exists := enh.rightSigs[sig]; exists {
+ if existing, exists := enh.rightStrSigs[sig]; exists {
// We encode original info sample timestamps via the float value.
existingOrigT := int64(existing.F)
switch {
@@ -359,14 +359,14 @@ func (ev *evaluator) combineWithInfoVector(base, info Vector, ignoreSeries map[u
// Keep the other info sample, since it's newer.
case existingOrigT < origT:
// Keep this info sample, since it's newer.
- enh.rightSigs[sig] = s
+ enh.rightStrSigs[sig] = s
default:
// The two info samples have the same timestamp - conflict.
ev.errorf("found duplicate series for info metric: existing %s @ %d, new %s @ %d",
existing.Metric.String(), existingOrigT, s.Metric.String(), origT)
}
} else {
- enh.rightSigs[sig] = s
+ enh.rightStrSigs[sig] = s
}
}
@@ -389,7 +389,7 @@ func (ev *evaluator) combineWithInfoVector(base, info Vector, ignoreSeries map[u
// For every info metric name, try to find an info series with the same signature.
seenInfoMetrics := map[string]struct{}{}
for infoName, sig := range baseSigs[hash] {
- is, exists := enh.rightSigs[sig]
+ is, exists := enh.rightStrSigs[sig]
if !exists {
continue
}
diff --git a/vendor/github.com/prometheus/prometheus/promql/parser/features.go b/vendor/github.com/prometheus/prometheus/promql/parser/features.go
new file mode 100644
index 00000000000..ec64678237f
--- /dev/null
+++ b/vendor/github.com/prometheus/prometheus/promql/parser/features.go
@@ -0,0 +1,57 @@
+// 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 parser
+
+import "github.com/prometheus/prometheus/util/features"
+
+// RegisterFeatures registers all PromQL features with the feature registry.
+// This includes operators (arithmetic and comparison/set), aggregators (standard
+// and experimental), and functions.
+func RegisterFeatures(r features.Collector) {
+ // Register core PromQL language keywords.
+ for keyword, itemType := range key {
+ if itemType.IsKeyword() {
+ // Handle experimental keywords separately.
+ switch keyword {
+ case "anchored", "smoothed":
+ r.Set(features.PromQL, keyword, EnableExtendedRangeSelectors)
+ default:
+ r.Enable(features.PromQL, keyword)
+ }
+ }
+ }
+
+ // Register operators.
+ for o := ItemType(operatorsStart + 1); o < operatorsEnd; o++ {
+ if o.IsOperator() {
+ r.Set(features.PromQLOperators, o.String(), true)
+ }
+ }
+
+ // Register aggregators.
+ for a := ItemType(aggregatorsStart + 1); a < aggregatorsEnd; a++ {
+ if a.IsAggregator() {
+ experimental := a.IsExperimentalAggregator() && !EnableExperimentalFunctions
+ r.Set(features.PromQLOperators, a.String(), !experimental)
+ }
+ }
+
+ // Register functions.
+ for f, fc := range Functions {
+ r.Set(features.PromQLFunctions, f, !fc.Experimental || EnableExperimentalFunctions)
+ }
+
+ // Register experimental parser features.
+ r.Set(features.PromQL, "duration_expr", ExperimentalDurationExpr)
+}
diff --git a/vendor/github.com/prometheus/prometheus/promql/parser/printer.go b/vendor/github.com/prometheus/prometheus/promql/parser/printer.go
index a562b88044d..961167428b0 100644
--- a/vendor/github.com/prometheus/prometheus/promql/parser/printer.go
+++ b/vendor/github.com/prometheus/prometheus/promql/parser/printer.go
@@ -37,15 +37,16 @@ func tree(node Node, level string) string {
}
typs := strings.Split(fmt.Sprintf("%T", node), ".")[1]
- t := fmt.Sprintf("%s |---- %s :: %s\n", level, typs, node)
+ var t strings.Builder
+ t.WriteString(fmt.Sprintf("%s |---- %s :: %s\n", level, typs, node))
level += " · · ·"
for e := range ChildrenIter(node) {
- t += tree(e, level)
+ t.WriteString(tree(e, level))
}
- return t
+ return t.String()
}
func (node *EvalStmt) String() string {
@@ -147,12 +148,14 @@ func (node *BinaryExpr) ShortString() string {
func (node *BinaryExpr) getMatchingStr() string {
matching := ""
vm := node.VectorMatching
- if vm != nil && (len(vm.MatchingLabels) > 0 || vm.On) {
- vmTag := "ignoring"
- if vm.On {
- vmTag = "on"
+ if vm != nil {
+ if len(vm.MatchingLabels) > 0 || vm.On || vm.Card == CardManyToOne || vm.Card == CardOneToMany {
+ vmTag := "ignoring"
+ if vm.On {
+ vmTag = "on"
+ }
+ matching = fmt.Sprintf(" %s (%s)", vmTag, strings.Join(vm.MatchingLabels, ", "))
}
- matching = fmt.Sprintf(" %s (%s)", vmTag, strings.Join(vm.MatchingLabels, ", "))
if vm.Card == CardManyToOne || vm.Card == CardOneToMany {
vmCard := "right"
diff --git a/vendor/github.com/prometheus/prometheus/promql/promqltest/test.go b/vendor/github.com/prometheus/prometheus/promql/promqltest/test.go
index 41d8cdde20a..d4a11b9e502 100644
--- a/vendor/github.com/prometheus/prometheus/promql/promqltest/test.go
+++ b/vendor/github.com/prometheus/prometheus/promql/promqltest/test.go
@@ -231,7 +231,7 @@ func raise(line int, format string, v ...any) error {
}
}
-func parseLoad(lines []string, i int) (int, *loadCmd, error) {
+func parseLoad(lines []string, i int, startTime time.Time) (int, *loadCmd, error) {
if !patLoad.MatchString(lines[i]) {
return i, nil, raise(i, "invalid load command. (load[_with_nhcb] )")
}
@@ -245,6 +245,7 @@ func parseLoad(lines []string, i int) (int, *loadCmd, error) {
return i, nil, raise(i, "invalid step definition %q: %s", step, err)
}
cmd := newLoadCmd(time.Duration(gap), withNHCB)
+ cmd.startTime = startTime
for i+1 < len(lines) {
i++
defLine := lines[i]
@@ -579,7 +580,7 @@ func (t *test) parse(input string) error {
case c == "clear":
cmd = &clearCmd{}
case strings.HasPrefix(c, "load"):
- i, cmd, err = parseLoad(lines, i)
+ i, cmd, err = parseLoad(lines, i, testStartTime)
case strings.HasPrefix(c, "eval"):
i, cmd, err = t.parseEval(lines, i)
default:
@@ -611,6 +612,7 @@ type loadCmd struct {
defs map[uint64][]promql.Sample
exemplars map[uint64][]exemplar.Exemplar
withNHCB bool
+ startTime time.Time
}
func newLoadCmd(gap time.Duration, withNHCB bool) *loadCmd {
@@ -620,6 +622,7 @@ func newLoadCmd(gap time.Duration, withNHCB bool) *loadCmd {
defs: map[uint64][]promql.Sample{},
exemplars: map[uint64][]exemplar.Exemplar{},
withNHCB: withNHCB,
+ startTime: testStartTime,
}
}
@@ -632,7 +635,7 @@ func (cmd *loadCmd) set(m labels.Labels, vals ...parser.SequenceValue) {
h := m.Hash()
samples := make([]promql.Sample, 0, len(vals))
- ts := testStartTime
+ ts := cmd.startTime
for _, v := range vals {
if !v.Omitted {
samples = append(samples, promql.Sample{
@@ -1430,6 +1433,11 @@ func (t *test) execEval(cmd *evalCmd, engine promql.QueryEngine) error {
func (t *test) execRangeEval(cmd *evalCmd, engine promql.QueryEngine) error {
q, err := engine.NewRangeQuery(t.context, t.storage, nil, cmd.expr, cmd.start, cmd.end, cmd.step)
if err != nil {
+ if cmd.isFail() {
+ if err := cmd.checkExpectedFailure(err); err == nil {
+ return nil
+ }
+ }
return fmt.Errorf("error creating range query for %q (line %d): %w", cmd.expr, cmd.line, err)
}
defer q.Close()
@@ -1473,6 +1481,11 @@ func (t *test) execInstantEval(cmd *evalCmd, engine promql.QueryEngine) error {
func (t *test) runInstantQuery(iq atModifierTestCase, cmd *evalCmd, engine promql.QueryEngine) error {
q, err := engine.NewInstantQuery(t.context, t.storage, nil, iq.expr, iq.evalTime)
if err != nil {
+ if cmd.isFail() {
+ if err := cmd.checkExpectedFailure(err); err == nil {
+ return nil
+ }
+ }
return fmt.Errorf("error creating instant query for %q (line %d): %w", cmd.expr, cmd.line, err)
}
defer q.Close()
@@ -1617,6 +1630,8 @@ type LazyLoaderOpts struct {
// Currently defaults to false, matches the "promql-delayed-name-removal"
// feature flag.
EnableDelayedNameRemoval bool
+ // StartTime is the start time for the test. If zero, defaults to Unix epoch.
+ StartTime time.Time
}
// NewLazyLoader returns an initialized empty LazyLoader.
@@ -1642,7 +1657,12 @@ func (ll *LazyLoader) parse(input string) error {
continue
}
if strings.HasPrefix(strings.ToLower(patSpace.Split(l, 2)[0]), "load") {
- _, cmd, err := parseLoad(lines, i)
+ // Determine the start time to use for loading samples.
+ startTime := testStartTime
+ if !ll.opts.StartTime.IsZero() {
+ startTime = ll.opts.StartTime
+ }
+ _, cmd, err := parseLoad(lines, i, startTime)
if err != nil {
return err
}
diff --git a/vendor/github.com/prometheus/prometheus/promql/promqltest/testdata/extended_vectors.test b/vendor/github.com/prometheus/prometheus/promql/promqltest/testdata/extended_vectors.test
index 8e116b1ac58..8f431dcfd31 100644
--- a/vendor/github.com/prometheus/prometheus/promql/promqltest/testdata/extended_vectors.test
+++ b/vendor/github.com/prometheus/prometheus/promql/promqltest/testdata/extended_vectors.test
@@ -319,7 +319,6 @@ eval instant at 2m changes(metric[1m] anchored)
{id="2"} 1
eval instant at 3m changes(metric[1m] anchored)
- {id="1"} 1
{id="2"} 1
eval instant at 8m changes(metric[1m] anchored)
@@ -342,7 +341,6 @@ eval instant at 2m resets(metric[1m] anchored)
{id="2"} 1
eval instant at 3m resets(metric[1m] anchored)
- {id="1"} 1
{id="2"} 1
eval instant at 8m resets(metric[1m] anchored)
diff --git a/vendor/github.com/prometheus/prometheus/promql/promqltest/testdata/functions.test b/vendor/github.com/prometheus/prometheus/promql/promqltest/testdata/functions.test
index ba3df76ff61..7bc4bcb6244 100644
--- a/vendor/github.com/prometheus/prometheus/promql/promqltest/testdata/functions.test
+++ b/vendor/github.com/prometheus/prometheus/promql/promqltest/testdata/functions.test
@@ -2014,3 +2014,38 @@ eval instant at 0m scalar({type="histogram"})
# One float in the vector.
eval instant at 0m scalar({l="x"})
1
+
+clear
+load 20m
+ series{label="a", idx="1"} 2 _
+ series{label="a", idx="2"} _ 4
+
+eval instant at 0 label_replace(series, "idx", "replaced", "idx", ".*")
+ series{label="a", idx="replaced"} 2
+
+eval instant at 20m label_replace(series, "idx", "replaced", "idx", ".*")
+ series{label="a", idx="replaced"} 4
+
+eval range from 0 to 20m step 20m label_replace(series, "idx", "replaced", "idx", ".*")
+ series{label="a", idx="replaced"} 2 4
+
+# Test label_join with non-overlapping series.
+eval instant at 0 label_join(series, "idx", ",", "label", "label")
+ series{label="a", idx="a,a"} 2
+
+eval instant at 20m label_join(series, "idx", ",", "label", "label")
+ series{label="a", idx="a,a"} 4
+
+eval range from 0 to 20m step 20m label_join(series, "idx", ",", "label", "label")
+ series{label="a", idx="a,a"} 2 4
+
+# Test label_replace failure with overlapping timestamps (same labelset at same time).
+clear
+load 1m
+ overlap{label="a", idx="1"} 1
+ overlap{label="a", idx="2"} 2
+
+eval_fail instant at 0 label_replace(overlap, "idx", "same", "idx", ".*")
+
+# Test label_join failure with overlapping timestamps (same labelset at same time).
+eval_fail instant at 0 label_join(overlap, "idx", ",", "label", "label")
diff --git a/vendor/github.com/prometheus/prometheus/promql/promqltest/testdata/name_label_dropping.test b/vendor/github.com/prometheus/prometheus/promql/promqltest/testdata/name_label_dropping.test
index 3682021ba93..e0180c7ffe9 100644
--- a/vendor/github.com/prometheus/prometheus/promql/promqltest/testdata/name_label_dropping.test
+++ b/vendor/github.com/prometheus/prometheus/promql/promqltest/testdata/name_label_dropping.test
@@ -91,3 +91,48 @@ eval instant at 10m topk(10, sum by (__name__, env) (metric_total{env="1"}))
eval instant at 10m topk(10, sum by (__name__, env) (rate(metric_total{env="1"}[10m])))
{env="1"} 0.2
+
+clear
+
+# More testing for __name__ label drop with different input series.
+load 1m
+ metric_total{env="1"} 0+1x10
+ metric_total{env="2"} 0+3x10
+
+# Metric name is preserved as there is no function that drops it.
+eval instant at 10m sum by (__name__) (metric_total{env="1"})
+ metric_total 10
+
+# Metric name is dropped at the end because of rate and because there is no label function to preserve it.
+eval instant at 10m sum by (__name__) (rate(metric_total{env="2"}[5m]))
+ {} 0.05
+
+# Metric name is preserved with label_replace even though it would have been dropped with rate.
+eval instant at 10m label_replace(sum by (__name__) (rate(metric_total{env="2"}[5m])), "__name__", "$1", "__name__", "(.+)")
+ metric_total 0.05
+
+# Combining the above cases in an OR expression, we drop the name if any of the series drops it.
+eval instant at 10m sum by (__name__) (metric_total{env="1"} or rate(metric_total{env="2"}[5m]))
+ {} 10.05
+
+# Changing the order of the OR expression should not change the result.
+eval instant at 10m sum by (__name__) (rate(metric_total{env="2"}[5m]) or metric_total{env="1"})
+ {} 10.05
+
+# With non-matching first selector, we use the second to determine if __name__ is dropped.
+eval instant at 10m sum by (__name__) (metric_total{env="3"} or rate(metric_total{env="2"}[5m]))
+ {} 0.05
+
+# Same as above, but with reversed order.
+eval instant at 10m sum by (__name__) (rate(metric_total{env="3"}[5m]) or metric_total{env="1"})
+ metric_total 10
+
+clear
+
+# Test delayed name removal with range queries and OR operator.
+load 10m
+ metric_a 1 _
+ metric_b 3 4
+
+eval range from 0 to 20m step 10m -metric_a or -metric_b
+ {} -1 -4 _
diff --git a/vendor/github.com/prometheus/prometheus/promql/promqltest/testdata/operators.test b/vendor/github.com/prometheus/prometheus/promql/promqltest/testdata/operators.test
index 0e779f192cb..cd608b3c36b 100644
--- a/vendor/github.com/prometheus/prometheus/promql/promqltest/testdata/operators.test
+++ b/vendor/github.com/prometheus/prometheus/promql/promqltest/testdata/operators.test
@@ -316,6 +316,27 @@ eval instant at 5m http_requests_histogram == http_requests_histogram
eval instant at 5m http_requests_histogram != http_requests_histogram
expect no_info
+clear
+
+# Check that we track many-to-one vector matching errors even when all but 0 or 1
+# series on the "many" side are filtered away.
+load 5m
+ many_side{label="foo",job="test"} 0
+ many_side{label="bar",job="test"} 1
+ one_side{job="test"} 1
+
+# Check 0 series surviving the filtering producing an error.
+eval instant at 0m many_side > on(job) one_side
+ expect fail
+
+# Check 1 series surviving the filtering producing an error.
+eval instant at 0m many_side >= on(job) one_side
+ expect fail
+
+# Check 2 series surviving the filtering producing an error.
+eval instant at 0m many_side <= on(job) one_side
+ expect fail
+
# group_left/group_right.
clear
@@ -959,3 +980,40 @@ eval instant at 10m (testhistogram) and on() (vector(-1) == 1)
eval range from 0 to 10m step 5m (testhistogram) and on() (vector(-1) == 1)
clear
+
+# Test unary negation with non-overlapping series that have different metric names.
+# After negation, the __name__ label is dropped, so series with different names
+# but same other labels should merge if they don't overlap in time.
+load 20m
+ http_requests{job="api"} 2 _
+ http_errors{job="api"} _ 4
+
+eval instant at 0 -{job="api"}
+ {job="api"} -2
+
+eval instant at 20m -{job="api"}
+ {job="api"} -4
+
+eval range from 0 to 20m step 20m -{job="api"}
+ {job="api"} -2 -4
+
+# Test unary negation failure with overlapping timestamps (same labelset at same time).
+clear
+load 1m
+ http_requests{job="api"} 1
+ http_errors{job="api"} 2
+
+eval_fail instant at 0 -{job="api"}
+
+clear
+
+# Test unary negation with "or" operator combining metrics with removed names.
+load 10m
+ metric_a 1 _
+ metric_b 3 4
+
+# Use "-" unary operator as a simple way to remove the metric name.
+eval range from 0 to 20m step 10m -metric_a or -metric_b
+ {} -1 -4
+
+clear
diff --git a/vendor/github.com/prometheus/prometheus/rules/alerting.go b/vendor/github.com/prometheus/prometheus/rules/alerting.go
index b0151d7cb38..bb0763fbc64 100644
--- a/vendor/github.com/prometheus/prometheus/rules/alerting.go
+++ b/vendor/github.com/prometheus/prometheus/rules/alerting.go
@@ -46,6 +46,10 @@ const (
alertStateLabel = "alertstate"
)
+// ErrDuplicateAlertLabelSet is returned when an alerting rule evaluation produces
+// metrics with identical labelsets after applying alert labels.
+var ErrDuplicateAlertLabelSet = errors.New("vector contains metrics with the same labelset after applying alert labels")
+
// AlertState denotes the state of an active alert.
type AlertState int
@@ -441,7 +445,7 @@ func (r *AlertingRule) Eval(ctx context.Context, queryOffset time.Duration, ts t
resultFPs[h] = struct{}{}
if _, ok := alerts[h]; ok {
- return nil, errors.New("vector contains metrics with the same labelset after applying alert labels")
+ return nil, ErrDuplicateAlertLabelSet
}
alerts[h] = &Alert{
diff --git a/vendor/github.com/prometheus/prometheus/rules/group.go b/vendor/github.com/prometheus/prometheus/rules/group.go
index 8cedcd40d16..47afe6f7158 100644
--- a/vendor/github.com/prometheus/prometheus/rules/group.go
+++ b/vendor/github.com/prometheus/prometheus/rules/group.go
@@ -519,6 +519,7 @@ func (g *Group) Eval(ctx context.Context, ts time.Time) {
since := time.Since(t)
g.metrics.EvalDuration.Observe(since.Seconds())
+ g.metrics.EvalDurationHistogram.Observe(since.Seconds())
rule.SetEvaluationDuration(since)
rule.SetEvaluationTimestamp(t)
}(time.Now())
@@ -910,19 +911,21 @@ const namespace = "prometheus"
// Metrics for rule evaluation.
type Metrics struct {
- EvalDuration prometheus.Summary
- IterationDuration prometheus.Summary
- IterationsMissed *prometheus.CounterVec
- IterationsScheduled *prometheus.CounterVec
- EvalTotal *prometheus.CounterVec
- EvalFailures *prometheus.CounterVec
- GroupInterval *prometheus.GaugeVec
- GroupLastEvalTime *prometheus.GaugeVec
- GroupLastDuration *prometheus.GaugeVec
- GroupLastRuleDurationSum *prometheus.GaugeVec
- GroupLastRestoreDuration *prometheus.GaugeVec
- GroupRules *prometheus.GaugeVec
- GroupSamples *prometheus.GaugeVec
+ EvalDuration prometheus.Summary
+ EvalDurationHistogram prometheus.Histogram
+ IterationDuration prometheus.Summary
+ IterationDurationHistogram prometheus.Histogram
+ IterationsMissed *prometheus.CounterVec
+ IterationsScheduled *prometheus.CounterVec
+ EvalTotal *prometheus.CounterVec
+ EvalFailures *prometheus.CounterVec
+ GroupInterval *prometheus.GaugeVec
+ GroupLastEvalTime *prometheus.GaugeVec
+ GroupLastDuration *prometheus.GaugeVec
+ GroupLastRuleDurationSum *prometheus.GaugeVec
+ GroupLastRestoreDuration *prometheus.GaugeVec
+ GroupRules *prometheus.GaugeVec
+ GroupSamples *prometheus.GaugeVec
}
// NewGroupMetrics creates a new instance of Metrics and registers it with the provided registerer,
@@ -936,12 +939,30 @@ func NewGroupMetrics(reg prometheus.Registerer) *Metrics {
Help: "The duration for a rule to execute.",
Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001},
}),
+ EvalDurationHistogram: prometheus.NewHistogram(prometheus.HistogramOpts{
+ Namespace: namespace,
+ Name: "rule_evaluation_duration_histogram_seconds",
+ Help: "The duration for a rule to execute.",
+ Buckets: []float64{.01, .1, 1, 10},
+ NativeHistogramBucketFactor: 1.1,
+ NativeHistogramMaxBucketNumber: 100,
+ NativeHistogramMinResetDuration: 1 * time.Hour,
+ }),
IterationDuration: prometheus.NewSummary(prometheus.SummaryOpts{
Namespace: namespace,
Name: "rule_group_duration_seconds",
Help: "The duration of rule group evaluations.",
Objectives: map[float64]float64{0.01: 0.001, 0.05: 0.005, 0.5: 0.05, 0.90: 0.01, 0.99: 0.001},
}),
+ IterationDurationHistogram: prometheus.NewHistogram(prometheus.HistogramOpts{
+ Namespace: namespace,
+ Name: "rule_group_duration_histogram_seconds",
+ Help: "The duration of rule group evaluations.",
+ Buckets: []float64{.01, .1, 1, 10},
+ NativeHistogramBucketFactor: 1.1,
+ NativeHistogramMaxBucketNumber: 100,
+ NativeHistogramMinResetDuration: 1 * time.Hour,
+ }),
IterationsMissed: prometheus.NewCounterVec(
prometheus.CounterOpts{
Namespace: namespace,
@@ -1035,7 +1056,9 @@ func NewGroupMetrics(reg prometheus.Registerer) *Metrics {
if reg != nil {
reg.MustRegister(
m.EvalDuration,
+ m.EvalDurationHistogram,
m.IterationDuration,
+ m.IterationDurationHistogram,
m.IterationsMissed,
m.IterationsScheduled,
m.EvalTotal,
diff --git a/vendor/github.com/prometheus/prometheus/rules/manager.go b/vendor/github.com/prometheus/prometheus/rules/manager.go
index d2fb0a77974..d610c154be2 100644
--- a/vendor/github.com/prometheus/prometheus/rules/manager.go
+++ b/vendor/github.com/prometheus/prometheus/rules/manager.go
@@ -37,6 +37,7 @@ import (
"github.com/prometheus/prometheus/promql"
"github.com/prometheus/prometheus/promql/parser"
"github.com/prometheus/prometheus/storage"
+ "github.com/prometheus/prometheus/util/features"
"github.com/prometheus/prometheus/util/strutil"
)
@@ -85,6 +86,7 @@ func DefaultEvalIterationFunc(ctx context.Context, g *Group, evalTimestamp time.
timeSinceStart := time.Since(start)
g.metrics.IterationDuration.Observe(timeSinceStart.Seconds())
+ g.metrics.IterationDurationHistogram.Observe(timeSinceStart.Seconds())
g.updateRuleEvaluationTimeSum()
g.setEvaluationTime(timeSinceStart)
g.setLastEvaluation(start)
@@ -133,6 +135,9 @@ type ManagerOptions struct {
RestoreNewRuleGroups bool
Metrics *Metrics
+
+ // FeatureRegistry is used to register rule manager features.
+ FeatureRegistry features.Collector
}
// NewManager returns an implementation of Manager, ready to be started
@@ -173,6 +178,13 @@ func NewManager(o *ManagerOptions) *Manager {
o.Logger = promslog.NewNopLogger()
}
+ // Register rule manager features if a registry is provided.
+ if o.FeatureRegistry != nil {
+ o.FeatureRegistry.Set(features.Rules, "concurrent_rule_eval", o.ConcurrentEvalsEnabled)
+ o.FeatureRegistry.Enable(features.Rules, "query_offset")
+ o.FeatureRegistry.Enable(features.Rules, "keep_firing_for")
+ }
+
m := &Manager{
groups: map[string]*Group{},
opts: o,
diff --git a/vendor/github.com/prometheus/prometheus/rules/recording.go b/vendor/github.com/prometheus/prometheus/rules/recording.go
index 2da6885f5b9..1bc41b834ad 100644
--- a/vendor/github.com/prometheus/prometheus/rules/recording.go
+++ b/vendor/github.com/prometheus/prometheus/rules/recording.go
@@ -30,6 +30,10 @@ import (
"github.com/prometheus/prometheus/promql/parser"
)
+// ErrDuplicateRecordingLabelSet is returned when a recording rule evaluation produces
+// metrics with identical labelsets after applying rule labels.
+var ErrDuplicateRecordingLabelSet = errors.New("vector contains metrics with the same labelset after applying rule labels")
+
// A RecordingRule records its vector expression into new timeseries.
type RecordingRule struct {
name string
@@ -104,7 +108,7 @@ func (rule *RecordingRule) Eval(ctx context.Context, queryOffset time.Duration,
// Check that the rule does not produce identical metrics after applying
// labels.
if vector.ContainsSameLabelset() {
- return nil, errors.New("vector contains metrics with the same labelset after applying rule labels")
+ return nil, ErrDuplicateRecordingLabelSet
}
numSeries := len(vector)
diff --git a/vendor/github.com/prometheus/prometheus/schema/labels.go b/vendor/github.com/prometheus/prometheus/schema/labels.go
index 6df74451711..05329af7f6a 100644
--- a/vendor/github.com/prometheus/prometheus/schema/labels.go
+++ b/vendor/github.com/prometheus/prometheus/schema/labels.go
@@ -19,20 +19,10 @@ import (
"github.com/prometheus/prometheus/model/labels"
)
-const (
- // Special label names and selectors for schema.Metadata fields.
- // They are currently private to ensure __name__, __type__ and __unit__ are used
- // together and remain extensible in Prometheus. See NewMetadataFromLabels and Metadata
- // methods for the interactions with the labels package structs.
- metricName = "__name__"
- metricType = "__type__"
- metricUnit = "__unit__"
-)
-
// IsMetadataLabel returns true if the given label name is a special
// schema Metadata label.
func IsMetadataLabel(name string) bool {
- return name == metricName || name == metricType || name == metricUnit
+ return name == model.MetricNameLabel || name == model.MetricTypeLabel || name == model.MetricUnitLabel
}
// Metadata represents the core metric schema/metadata elements that:
@@ -79,13 +69,13 @@ type Metadata struct {
// NewMetadataFromLabels returns the schema metadata from the labels.
func NewMetadataFromLabels(ls labels.Labels) Metadata {
typ := model.MetricTypeUnknown
- if got := ls.Get(metricType); got != "" {
+ if got := ls.Get(model.MetricTypeLabel); got != "" {
typ = model.MetricType(got)
}
return Metadata{
- Name: ls.Get(metricName),
+ Name: ls.Get(model.MetricNameLabel),
Type: typ,
- Unit: ls.Get(metricUnit),
+ Unit: ls.Get(model.MetricUnitLabel),
}
}
@@ -99,11 +89,11 @@ func (m Metadata) IsTypeEmpty() bool {
// IsEmptyFor returns true.
func (m Metadata) IsEmptyFor(labelName string) bool {
switch labelName {
- case metricName:
+ case model.MetricNameLabel:
return m.Name == ""
- case metricType:
+ case model.MetricTypeLabel:
return m.IsTypeEmpty()
- case metricUnit:
+ case model.MetricUnitLabel:
return m.Unit == ""
default:
return true
@@ -114,13 +104,13 @@ func (m Metadata) IsEmptyFor(labelName string) bool {
// Empty Metadata fields will be ignored (not added).
func (m Metadata) AddToLabels(b *labels.ScratchBuilder) {
if m.Name != "" {
- b.Add(metricName, m.Name)
+ b.Add(model.MetricNameLabel, m.Name)
}
if !m.IsTypeEmpty() {
- b.Add(metricType, string(m.Type))
+ b.Add(model.MetricTypeLabel, string(m.Type))
}
if m.Unit != "" {
- b.Add(metricUnit, m.Unit)
+ b.Add(model.MetricUnitLabel, m.Unit)
}
}
@@ -128,15 +118,15 @@ func (m Metadata) AddToLabels(b *labels.ScratchBuilder) {
// It follows the labels.Builder.Set semantics, so empty Metadata fields will
// remove the corresponding existing labels if they were previously set.
func (m Metadata) SetToLabels(b *labels.Builder) {
- b.Set(metricName, m.Name)
+ b.Set(model.MetricNameLabel, m.Name)
if m.Type == model.MetricTypeUnknown {
// Unknown equals empty semantically, so remove the label on unknown too as per
// method signature comment.
- b.Set(metricType, "")
+ b.Set(model.MetricTypeLabel, "")
} else {
- b.Set(metricType, string(m.Type))
+ b.Set(model.MetricTypeLabel, string(m.Type))
}
- b.Set(metricUnit, m.Unit)
+ b.Set(model.MetricUnitLabel, m.Unit)
}
// NewIgnoreOverriddenMetadataLabelScratchBuilder creates IgnoreOverriddenMetadataLabelScratchBuilder.
diff --git a/vendor/github.com/prometheus/prometheus/scrape/manager.go b/vendor/github.com/prometheus/prometheus/scrape/manager.go
index 7389f24b523..9bb6988df9e 100644
--- a/vendor/github.com/prometheus/prometheus/scrape/manager.go
+++ b/vendor/github.com/prometheus/prometheus/scrape/manager.go
@@ -33,6 +33,7 @@ import (
"github.com/prometheus/prometheus/discovery/targetgroup"
"github.com/prometheus/prometheus/model/labels"
"github.com/prometheus/prometheus/storage"
+ "github.com/prometheus/prometheus/util/features"
"github.com/prometheus/prometheus/util/logging"
"github.com/prometheus/prometheus/util/osutil"
"github.com/prometheus/prometheus/util/pool"
@@ -67,6 +68,13 @@ func NewManager(o *Options, logger *slog.Logger, newScrapeFailureLogger func(str
m.metrics.setTargetMetadataCacheGatherer(m)
+ // Register scrape features.
+ if r := o.FeatureRegistry; r != nil {
+ r.Set(features.Scrape, "extra_scrape_metrics", o.ExtraMetrics)
+ r.Set(features.Scrape, "start_timestamp_zero_ingestion", o.EnableStartTimestampZeroIngestion)
+ r.Set(features.Scrape, "type_and_unit_labels", o.EnableTypeAndUnitLabels)
+ }
+
return m, nil
}
@@ -85,7 +93,7 @@ type Options struct {
DiscoveryReloadInterval model.Duration
// Option to enable the ingestion of the created timestamp as a synthetic zero sample.
// See: https://github.com/prometheus/proposals/blob/main/proposals/2023-06-13_created-timestamp.md
- EnableCreatedTimestampZeroIngestion bool
+ EnableStartTimestampZeroIngestion bool
// EnableTypeAndUnitLabels
EnableTypeAndUnitLabels bool
@@ -93,6 +101,9 @@ type Options struct {
// Optional HTTP client options to use when scraping.
HTTPClientOptions []config_util.HTTPClientOption
+ // FeatureRegistry is the registry for tracking enabled/disabled features.
+ FeatureRegistry features.Collector
+
// private option for testability.
skipOffsetting bool
}
diff --git a/vendor/github.com/prometheus/prometheus/scrape/metrics.go b/vendor/github.com/prometheus/prometheus/scrape/metrics.go
index e7395c6191c..634c52fb2d7 100644
--- a/vendor/github.com/prometheus/prometheus/scrape/metrics.go
+++ b/vendor/github.com/prometheus/prometheus/scrape/metrics.go
@@ -15,6 +15,7 @@ package scrape
import (
"fmt"
+ "time"
"github.com/prometheus/client_golang/prometheus"
)
@@ -36,6 +37,7 @@ type scrapeMetrics struct {
targetScrapePoolTargetsAdded *prometheus.GaugeVec
targetScrapePoolSymbolTableItems *prometheus.GaugeVec
targetSyncIntervalLength *prometheus.SummaryVec
+ targetSyncIntervalLengthHistogram *prometheus.HistogramVec
targetSyncFailed *prometheus.CounterVec
// Used by targetScraper.
@@ -46,6 +48,7 @@ type scrapeMetrics struct {
// Used by scrapeLoop.
targetIntervalLength *prometheus.SummaryVec
+ targetIntervalLengthHistogram *prometheus.HistogramVec
targetScrapeSampleLimit prometheus.Counter
targetScrapeSampleDuplicate prometheus.Counter
targetScrapeSampleOutOfOrder prometheus.Counter
@@ -152,6 +155,17 @@ func newScrapeMetrics(reg prometheus.Registerer) (*scrapeMetrics, error) {
},
[]string{"scrape_job"},
)
+ sm.targetSyncIntervalLengthHistogram = prometheus.NewHistogramVec(
+ prometheus.HistogramOpts{
+ Name: "prometheus_target_sync_length_histogram_seconds",
+ Help: "Actual interval to sync the scrape pool.",
+ Buckets: []float64{.01, .1, 1, 10},
+ NativeHistogramBucketFactor: 1.1,
+ NativeHistogramMaxBucketNumber: 100,
+ NativeHistogramMinResetDuration: 1 * time.Hour,
+ },
+ []string{"scrape_job"},
+ )
sm.targetSyncFailed = prometheus.NewCounterVec(
prometheus.CounterOpts{
Name: "prometheus_target_sync_failed_total",
@@ -185,6 +199,17 @@ func newScrapeMetrics(reg prometheus.Registerer) (*scrapeMetrics, error) {
},
[]string{"interval"},
)
+ sm.targetIntervalLengthHistogram = prometheus.NewHistogramVec(
+ prometheus.HistogramOpts{
+ Name: "prometheus_target_interval_length_histogram_seconds",
+ Help: "Actual intervals between scrapes.",
+ Buckets: []float64{.01, .1, 1, 10},
+ NativeHistogramBucketFactor: 1.1,
+ NativeHistogramMaxBucketNumber: 100,
+ NativeHistogramMinResetDuration: 1 * time.Hour,
+ },
+ []string{"interval"},
+ )
sm.targetScrapeSampleLimit = prometheus.NewCounter(
prometheus.CounterOpts{
Name: "prometheus_target_scrapes_exceeded_sample_limit_total",
@@ -238,6 +263,7 @@ func newScrapeMetrics(reg prometheus.Registerer) (*scrapeMetrics, error) {
sm.targetScrapePoolReloads,
sm.targetScrapePoolReloadsFailed,
sm.targetSyncIntervalLength,
+ sm.targetSyncIntervalLengthHistogram,
sm.targetScrapePoolSyncsCounter,
sm.targetScrapePoolExceededTargetLimit,
sm.targetScrapePoolTargetLimit,
@@ -250,6 +276,7 @@ func newScrapeMetrics(reg prometheus.Registerer) (*scrapeMetrics, error) {
sm.targetScrapeCacheFlushForced,
// Used by scrapeLoop.
sm.targetIntervalLength,
+ sm.targetIntervalLengthHistogram,
sm.targetScrapeSampleLimit,
sm.targetScrapeSampleDuplicate,
sm.targetScrapeSampleOutOfOrder,
@@ -279,6 +306,7 @@ func (sm *scrapeMetrics) Unregister() {
sm.reg.Unregister(sm.targetScrapePoolReloads)
sm.reg.Unregister(sm.targetScrapePoolReloadsFailed)
sm.reg.Unregister(sm.targetSyncIntervalLength)
+ sm.reg.Unregister(sm.targetSyncIntervalLengthHistogram)
sm.reg.Unregister(sm.targetScrapePoolSyncsCounter)
sm.reg.Unregister(sm.targetScrapePoolExceededTargetLimit)
sm.reg.Unregister(sm.targetScrapePoolTargetLimit)
@@ -288,6 +316,7 @@ func (sm *scrapeMetrics) Unregister() {
sm.reg.Unregister(sm.targetScrapeExceededBodySizeLimit)
sm.reg.Unregister(sm.targetScrapeCacheFlushForced)
sm.reg.Unregister(sm.targetIntervalLength)
+ sm.reg.Unregister(sm.targetIntervalLengthHistogram)
sm.reg.Unregister(sm.targetScrapeSampleLimit)
sm.reg.Unregister(sm.targetScrapeSampleDuplicate)
sm.reg.Unregister(sm.targetScrapeSampleOutOfOrder)
diff --git a/vendor/github.com/prometheus/prometheus/scrape/scrape.go b/vendor/github.com/prometheus/prometheus/scrape/scrape.go
index 09652d04849..a8cd15d30c4 100644
--- a/vendor/github.com/prometheus/prometheus/scrape/scrape.go
+++ b/vendor/github.com/prometheus/prometheus/scrape/scrape.go
@@ -215,7 +215,7 @@ func newScrapePool(cfg *config.ScrapeConfig, app storage.Appendable, offsetSeed
opts.alwaysScrapeClassicHist,
opts.convertClassicHistToNHCB,
cfg.ScrapeNativeHistogramsEnabled(),
- options.EnableCreatedTimestampZeroIngestion,
+ options.EnableStartTimestampZeroIngestion,
options.EnableTypeAndUnitLabels,
options.ExtraMetrics,
options.AppendMetadata,
@@ -309,6 +309,7 @@ func (sp *scrapePool) stop() {
sp.metrics.targetScrapePoolTargetsAdded.DeleteLabelValues(sp.config.JobName)
sp.metrics.targetScrapePoolSymbolTableItems.DeleteLabelValues(sp.config.JobName)
sp.metrics.targetSyncIntervalLength.DeleteLabelValues(sp.config.JobName)
+ sp.metrics.targetSyncIntervalLengthHistogram.DeleteLabelValues(sp.config.JobName)
sp.metrics.targetSyncFailed.DeleteLabelValues(sp.config.JobName)
}
}
@@ -505,6 +506,9 @@ func (sp *scrapePool) Sync(tgs []*targetgroup.Group) {
sp.metrics.targetSyncIntervalLength.WithLabelValues(sp.config.JobName).Observe(
time.Since(start).Seconds(),
)
+ sp.metrics.targetSyncIntervalLengthHistogram.WithLabelValues(sp.config.JobName).Observe(
+ time.Since(start).Seconds(),
+ )
sp.metrics.targetScrapePoolSyncsCounter.WithLabelValues(sp.config.JobName).Inc()
}
@@ -712,13 +716,11 @@ func mutateSampleLabels(lset labels.Labels, target *Target, honor bool, rc []*re
}
}
- res := lb.Labels()
-
- if len(rc) > 0 {
- res, _ = relabel.Process(res, rc...)
+ if keep := relabel.ProcessBuilder(lb, rc...); !keep {
+ return labels.EmptyLabels()
}
- return res
+ return lb.Labels()
}
func resolveConflictingExposedLabels(lb *labels.Builder, conflictingExposedLabels []labels.Label) {
@@ -951,7 +953,7 @@ type scrapeLoop struct {
alwaysScrapeClassicHist bool
convertClassicHistToNHCB bool
- enableCTZeroIngestion bool
+ enableSTZeroIngestion bool
enableTypeAndUnitLabels bool
fallbackScrapeProtocol string
@@ -996,11 +998,9 @@ type scrapeCache struct {
// be a pointer so we can update it.
droppedSeries map[string]*uint64
- // seriesCur and seriesPrev store the labels of series that were seen
- // in the current and previous scrape.
- // We hold two maps and swap them out to save allocations.
- seriesCur map[uint64]*cacheEntry
- seriesPrev map[uint64]*cacheEntry
+ // Series that were seen in the current and previous scrape, for staleness detection.
+ seriesCur map[storage.SeriesRef]*cacheEntry
+ seriesPrev map[storage.SeriesRef]*cacheEntry
// TODO(bwplotka): Consider moving Metadata API to use WAL instead of scrape loop to
// avoid locking (using metadata API can block scraping).
@@ -1027,8 +1027,8 @@ func newScrapeCache(metrics *scrapeMetrics) *scrapeCache {
return &scrapeCache{
series: map[string]*cacheEntry{},
droppedSeries: map[string]*uint64{},
- seriesCur: map[uint64]*cacheEntry{},
- seriesPrev: map[uint64]*cacheEntry{},
+ seriesCur: map[storage.SeriesRef]*cacheEntry{},
+ seriesPrev: map[storage.SeriesRef]*cacheEntry{},
metadata: map[string]*metaEntry{},
metrics: metrics,
}
@@ -1076,13 +1076,9 @@ func (c *scrapeCache) iterDone(flushCache bool) {
c.metaMtx.Unlock()
}
- // Swap current and previous series.
+ // Swap current and previous series then clear the new current, to save allocations.
c.seriesPrev, c.seriesCur = c.seriesCur, c.seriesPrev
-
- // We have to delete every single key in the map.
- for k := range c.seriesCur {
- delete(c.seriesCur, k)
- }
+ clear(c.seriesCur)
c.iter++
}
@@ -1119,13 +1115,13 @@ func (c *scrapeCache) getDropped(met []byte) bool {
return ok
}
-func (c *scrapeCache) trackStaleness(hash uint64, ce *cacheEntry) {
- c.seriesCur[hash] = ce
+func (c *scrapeCache) trackStaleness(ref storage.SeriesRef, ce *cacheEntry) {
+ c.seriesCur[ref] = ce
}
func (c *scrapeCache) forEachStale(f func(storage.SeriesRef, labels.Labels) bool) {
- for h, ce := range c.seriesPrev {
- if _, ok := c.seriesCur[h]; !ok {
+ for ref, ce := range c.seriesPrev {
+ if _, ok := c.seriesCur[ref]; !ok {
if !f(ce.ref, ce.lset) {
break
}
@@ -1264,7 +1260,7 @@ func newScrapeLoop(ctx context.Context,
alwaysScrapeClassicHist bool,
convertClassicHistToNHCB bool,
enableNativeHistogramScraping bool,
- enableCTZeroIngestion bool,
+ enableSTZeroIngestion bool,
enableTypeAndUnitLabels bool,
reportExtraMetrics bool,
appendMetadataToWAL bool,
@@ -1321,7 +1317,7 @@ func newScrapeLoop(ctx context.Context,
timeout: timeout,
alwaysScrapeClassicHist: alwaysScrapeClassicHist,
convertClassicHistToNHCB: convertClassicHistToNHCB,
- enableCTZeroIngestion: enableCTZeroIngestion,
+ enableSTZeroIngestion: enableSTZeroIngestion,
enableTypeAndUnitLabels: enableTypeAndUnitLabels,
fallbackScrapeProtocol: fallbackScrapeProtocol,
enableNativeHistogramScraping: enableNativeHistogramScraping,
@@ -1426,6 +1422,9 @@ func (sl *scrapeLoop) scrapeAndReport(last, appendTime time.Time, errc chan<- er
sl.metrics.targetIntervalLength.WithLabelValues(sl.interval.String()).Observe(
time.Since(last).Seconds(),
)
+ sl.metrics.targetIntervalLengthHistogram.WithLabelValues(sl.interval.String()).Observe(
+ time.Since(last).Seconds(),
+ )
}
var total, added, seriesAdded, bytesRead int
@@ -1458,7 +1457,10 @@ func (sl *scrapeLoop) scrapeAndReport(last, appendTime time.Time, errc chan<- er
sl.l.Warn("Append failed", "err", err)
}
if errc != nil {
- errc <- forcedErr
+ select {
+ case errc <- forcedErr:
+ case <-sl.ctx.Done():
+ }
}
return start
@@ -1495,7 +1497,10 @@ func (sl *scrapeLoop) scrapeAndReport(last, appendTime time.Time, errc chan<- er
}
sl.scrapeFailureLoggerMtx.RUnlock()
if errc != nil {
- errc <- scrapeErr
+ select {
+ case errc <- scrapeErr:
+ case <-sl.ctx.Done():
+ }
}
if errors.Is(scrapeErr, errBodySizeLimit) {
bytesRead = -1
@@ -1660,7 +1665,7 @@ func (sl *scrapeLoop) append(app storage.Appender, b []byte, contentType string,
IgnoreNativeHistograms: !sl.enableNativeHistogramScraping,
ConvertClassicHistogramsToNHCB: sl.convertClassicHistToNHCB,
KeepClassicOnClassicAndNativeHistograms: sl.alwaysScrapeClassicHist,
- OpenMetricsSkipCTSeries: sl.enableCTZeroIngestion,
+ OpenMetricsSkipSTSeries: sl.enableSTZeroIngestion,
FallbackContentType: sl.fallbackScrapeProtocol,
})
if p == nil {
@@ -1801,21 +1806,21 @@ loop:
if seriesAlreadyScraped && parsedTimestamp == nil {
err = storage.ErrDuplicateSampleForTimestamp
} else {
- if sl.enableCTZeroIngestion {
- if ctMs := p.CreatedTimestamp(); ctMs != 0 {
+ if sl.enableSTZeroIngestion {
+ if stMs := p.StartTimestamp(); stMs != 0 {
if isHistogram {
if h != nil {
- ref, err = app.AppendHistogramCTZeroSample(ref, lset, t, ctMs, h, nil)
+ ref, err = app.AppendHistogramSTZeroSample(ref, lset, t, stMs, h, nil)
} else {
- ref, err = app.AppendHistogramCTZeroSample(ref, lset, t, ctMs, nil, fh)
+ ref, err = app.AppendHistogramSTZeroSample(ref, lset, t, stMs, nil, fh)
}
} else {
- ref, err = app.AppendCTZeroSample(ref, lset, t, ctMs)
+ ref, err = app.AppendSTZeroSample(ref, lset, t, stMs)
}
- if err != nil && !errors.Is(err, storage.ErrOutOfOrderCT) { // OOO is a common case, ignoring completely for now.
- // CT is an experimental feature. For now, we don't need to fail the
+ if err != nil && !errors.Is(err, storage.ErrOutOfOrderST) { // OOO is a common case, ignoring completely for now.
+ // ST is an experimental feature. For now, we don't need to fail the
// scrape on errors updating the created timestamp, log debug.
- sl.l.Debug("Error when appending CT in scrape loop", "series", string(met), "ct", ctMs, "t", t, "err", err)
+ sl.l.Debug("Error when appending ST in scrape loop", "series", string(met), "ct", stMs, "t", t, "err", err)
}
}
}
@@ -1833,7 +1838,7 @@ loop:
if err == nil {
if (parsedTimestamp == nil || sl.trackTimestampsStaleness) && ce != nil {
- sl.cache.trackStaleness(ce.hash, ce)
+ sl.cache.trackStaleness(ce.ref, ce)
}
}
@@ -1854,7 +1859,7 @@ loop:
if ce != nil && (parsedTimestamp == nil || sl.trackTimestampsStaleness) {
// Bypass staleness logic if there is an explicit timestamp.
// But make sure we only do this if we have a cache entry (ce) for our series.
- sl.cache.trackStaleness(hash, ce)
+ sl.cache.trackStaleness(ref, ce)
}
if sampleAdded && sampleLimitErr == nil && bucketLimitErr == nil {
seriesAdded++
@@ -1913,7 +1918,7 @@ loop:
if !seriesCached || lastMeta.lastIterChange == sl.cache.iter {
// In majority cases we can trust that the current series/histogram is matching the lastMeta and lastMFName.
// However, optional TYPE etc metadata and broken OM text can break this, detect those cases here.
- // TODO(bwplotka): Consider moving this to parser as many parser users end up doing this (e.g. CT and NHCB parsing).
+ // TODO(bwplotka): Consider moving this to parser as many parser users end up doing this (e.g. ST and NHCB parsing).
if isSeriesPartOfFamily(lset.Get(labels.MetricName), lastMFName, lastMeta.Type) {
if _, merr := app.UpdateMetadata(ref, lset, lastMeta.Metadata); merr != nil {
// No need to fail the scrape on errors appending metadata.
diff --git a/vendor/github.com/prometheus/prometheus/scrape/target.go b/vendor/github.com/prometheus/prometheus/scrape/target.go
index 563fe33f82f..2aabff20e2c 100644
--- a/vendor/github.com/prometheus/prometheus/scrape/target.go
+++ b/vendor/github.com/prometheus/prometheus/scrape/target.go
@@ -389,6 +389,7 @@ type bucketLimitAppender struct {
}
func (app *bucketLimitAppender) AppendHistogram(ref storage.SeriesRef, lset labels.Labels, t int64, h *histogram.Histogram, fh *histogram.FloatHistogram) (storage.SeriesRef, error) {
+ var err error
if h != nil {
// Return with an early error if the histogram has too many buckets and the
// schema is not exponential, in which case we can't reduce the resolution.
@@ -399,7 +400,9 @@ func (app *bucketLimitAppender) AppendHistogram(ref storage.SeriesRef, lset labe
if h.Schema <= histogram.ExponentialSchemaMin {
return 0, errBucketLimit
}
- h = h.ReduceResolution(h.Schema - 1)
+ if err = h.ReduceResolution(h.Schema - 1); err != nil {
+ return 0, err
+ }
}
}
if fh != nil {
@@ -412,11 +415,12 @@ func (app *bucketLimitAppender) AppendHistogram(ref storage.SeriesRef, lset labe
if fh.Schema <= histogram.ExponentialSchemaMin {
return 0, errBucketLimit
}
- fh = fh.ReduceResolution(fh.Schema - 1)
+ if err = fh.ReduceResolution(fh.Schema - 1); err != nil {
+ return 0, err
+ }
}
}
- ref, err := app.Appender.AppendHistogram(ref, lset, t, h, fh)
- if err != nil {
+ if ref, err = app.Appender.AppendHistogram(ref, lset, t, h, fh); err != nil {
return 0, err
}
return ref, nil
@@ -429,18 +433,22 @@ type maxSchemaAppender struct {
}
func (app *maxSchemaAppender) AppendHistogram(ref storage.SeriesRef, lset labels.Labels, t int64, h *histogram.Histogram, fh *histogram.FloatHistogram) (storage.SeriesRef, error) {
+ var err error
if h != nil {
if histogram.IsExponentialSchemaReserved(h.Schema) && h.Schema > app.maxSchema {
- h = h.ReduceResolution(app.maxSchema)
+ if err = h.ReduceResolution(app.maxSchema); err != nil {
+ return 0, err
+ }
}
}
if fh != nil {
if histogram.IsExponentialSchemaReserved(fh.Schema) && fh.Schema > app.maxSchema {
- fh = fh.ReduceResolution(app.maxSchema)
+ if err = fh.ReduceResolution(app.maxSchema); err != nil {
+ return 0, err
+ }
}
}
- ref, err := app.Appender.AppendHistogram(ref, lset, t, h, fh)
- if err != nil {
+ if ref, err = app.Appender.AppendHistogram(ref, lset, t, h, fh); err != nil {
return 0, err
}
return ref, nil
diff --git a/vendor/github.com/prometheus/prometheus/storage/fanout.go b/vendor/github.com/prometheus/prometheus/storage/fanout.go
index f99edb473a0..a699a97b023 100644
--- a/vendor/github.com/prometheus/prometheus/storage/fanout.go
+++ b/vendor/github.com/prometheus/prometheus/storage/fanout.go
@@ -199,14 +199,14 @@ func (f *fanoutAppender) AppendHistogram(ref SeriesRef, l labels.Labels, t int64
return ref, nil
}
-func (f *fanoutAppender) AppendHistogramCTZeroSample(ref SeriesRef, l labels.Labels, t, ct int64, h *histogram.Histogram, fh *histogram.FloatHistogram) (SeriesRef, error) {
- ref, err := f.primary.AppendHistogramCTZeroSample(ref, l, t, ct, h, fh)
+func (f *fanoutAppender) AppendHistogramSTZeroSample(ref SeriesRef, l labels.Labels, t, st int64, h *histogram.Histogram, fh *histogram.FloatHistogram) (SeriesRef, error) {
+ ref, err := f.primary.AppendHistogramSTZeroSample(ref, l, t, st, h, fh)
if err != nil {
return ref, err
}
for _, appender := range f.secondaries {
- if _, err := appender.AppendHistogramCTZeroSample(ref, l, t, ct, h, fh); err != nil {
+ if _, err := appender.AppendHistogramSTZeroSample(ref, l, t, st, h, fh); err != nil {
return 0, err
}
}
@@ -227,14 +227,14 @@ func (f *fanoutAppender) UpdateMetadata(ref SeriesRef, l labels.Labels, m metada
return ref, nil
}
-func (f *fanoutAppender) AppendCTZeroSample(ref SeriesRef, l labels.Labels, t, ct int64) (SeriesRef, error) {
- ref, err := f.primary.AppendCTZeroSample(ref, l, t, ct)
+func (f *fanoutAppender) AppendSTZeroSample(ref SeriesRef, l labels.Labels, t, st int64) (SeriesRef, error) {
+ ref, err := f.primary.AppendSTZeroSample(ref, l, t, st)
if err != nil {
return ref, err
}
for _, appender := range f.secondaries {
- if _, err := appender.AppendCTZeroSample(ref, l, t, ct); err != nil {
+ if _, err := appender.AppendSTZeroSample(ref, l, t, st); err != nil {
return 0, err
}
}
diff --git a/vendor/github.com/prometheus/prometheus/storage/interface.go b/vendor/github.com/prometheus/prometheus/storage/interface.go
index 9d7e5d93a6b..ae8bec033e5 100644
--- a/vendor/github.com/prometheus/prometheus/storage/interface.go
+++ b/vendor/github.com/prometheus/prometheus/storage/interface.go
@@ -44,13 +44,14 @@ var (
ErrExemplarsDisabled = errors.New("exemplar storage is disabled or max exemplars is less than or equal to 0")
ErrNativeHistogramsDisabled = errors.New("native histograms are disabled")
- // ErrOutOfOrderCT indicates failed append of CT to the storage
- // due to CT being older the then newer sample.
+ // ErrOutOfOrderST indicates failed append of ST to the storage
+ // due to ST being older the then newer sample.
// NOTE(bwplotka): This can be both an instrumentation failure or commonly expected
// behaviour, and we currently don't have a way to determine this. As a result
// it's recommended to ignore this error for now.
- ErrOutOfOrderCT = errors.New("created timestamp out of order, ignoring")
- ErrCTNewerThanSample = errors.New("CT is newer or the same as sample's timestamp, ignoring")
+ // TODO(bwplotka): Remove with appender v1 flow; not used in v2.
+ ErrOutOfOrderST = errors.New("start timestamp out of order, ignoring")
+ ErrSTNewerThanSample = errors.New("ST is newer or the same as sample's timestamp, ignoring")
)
// SeriesRef is a generic series reference. In prometheus it is either a
@@ -58,11 +59,14 @@ var (
// their own reference types.
type SeriesRef uint64
-// Appendable allows creating appenders.
+// Appendable allows creating Appender.
+//
+// WARNING: Work AppendableV2 is in progress. Appendable will be removed soon (ETA: Q2 2026).
type Appendable interface {
- // Appender returns a new appender for the storage. The implementation
- // can choose whether or not to use the context, for deadlines or to check
- // for errors.
+ // Appender returns a new appender for the storage.
+ //
+ // Implementations CAN choose whether to use the context e.g. for deadlines,
+ // but it's not mandatory.
Appender(ctx context.Context) Appender
}
@@ -255,7 +259,13 @@ func (f QueryableFunc) Querier(mint, maxt int64) (Querier, error) {
return f(mint, maxt)
}
+// AppendOptions provides options for implementations of the Appender interface.
+//
+// WARNING: Work AppendableV2 is in progress. Appendable will be removed soon (ETA: Q2 2026).
type AppendOptions struct {
+ // DiscardOutOfOrder tells implementation that this append should not be out
+ // of order. An OOO append MUST be rejected with storage.ErrOutOfOrderSample
+ // error.
DiscardOutOfOrder bool
}
@@ -264,9 +274,14 @@ type AppendOptions struct {
//
// Operations on the Appender interface are not goroutine-safe.
//
-// The type of samples (float64, histogram, etc) appended for a given series must remain same within an Appender.
-// The behaviour is undefined if samples of different types are appended to the same series in a single Commit().
+// The order of samples appended via the Appender is preserved within each series.
+// I.e. timestamp order within batch is not validated, samples are not reordered per timestamp or by float/histogram
+// type.
+//
+// WARNING: Work AppendableV2 is in progress. Appendable will be removed soon (ETA: Q2 2026).
type Appender interface {
+ AppenderTransaction
+
// Append adds a sample pair for the given series.
// An optional series reference can be provided to accelerate calls.
// A series reference number is returned which can be used to add further
@@ -277,16 +292,6 @@ type Appender interface {
// If the reference is 0 it must not be used for caching.
Append(ref SeriesRef, l labels.Labels, t int64, v float64) (SeriesRef, error)
- // Commit submits the collected samples and purges the batch. If Commit
- // returns a non-nil error, it also rolls back all modifications made in
- // the appender so far, as Rollback would do. In any case, an Appender
- // must not be used anymore after Commit has been called.
- Commit() error
-
- // Rollback rolls back all modifications made in the appender so far.
- // Appender has to be discarded after rollback.
- Rollback() error
-
// SetOptions configures the appender with specific append options such as
// discarding out-of-order samples even if out-of-order is enabled in the TSDB.
SetOptions(opts *AppendOptions)
@@ -294,14 +299,14 @@ type Appender interface {
ExemplarAppender
HistogramAppender
MetadataUpdater
- CreatedTimestampAppender
+ StartTimestampAppender
}
// GetRef is an extra interface on Appenders used by downstream projects
// (e.g. Cortex) to avoid maintaining a parallel set of references.
type GetRef interface {
- // Returns reference number that can be used to pass to Appender.Append(),
- // and a set of labels that will not cause another copy when passed to Appender.Append().
+ // GetRef returns a reference number that can be used to pass to AppenderV2.Append(),
+ // and a set of labels that will not cause another copy when passed to AppenderV2.Append().
// 0 means the appender does not have a reference to this series.
// hash should be a hash of lset.
GetRef(lset labels.Labels, hash uint64) (SeriesRef, labels.Labels)
@@ -309,6 +314,8 @@ type GetRef interface {
// ExemplarAppender provides an interface for adding samples to exemplar storage, which
// within Prometheus is in-memory only.
+//
+// WARNING: Work AppendableV2 is in progress. Appendable will be removed soon (ETA: Q2 2026).
type ExemplarAppender interface {
// AppendExemplar adds an exemplar for the given series labels.
// An optional reference number can be provided to accelerate calls.
@@ -325,6 +332,8 @@ type ExemplarAppender interface {
}
// HistogramAppender provides an interface for appending histograms to the storage.
+//
+// WARNING: Work AppendableV2 is in progress. Appendable will be removed soon (ETA: Q2 2026).
type HistogramAppender interface {
// AppendHistogram adds a histogram for the given series labels. An
// optional reference number can be provided to accelerate calls. A
@@ -338,23 +347,25 @@ type HistogramAppender interface {
// pointer. AppendHistogram won't mutate the histogram, but in turn
// depends on the caller to not mutate it either.
AppendHistogram(ref SeriesRef, l labels.Labels, t int64, h *histogram.Histogram, fh *histogram.FloatHistogram) (SeriesRef, error)
- // AppendHistogramCTZeroSample adds synthetic zero sample for the given ct timestamp,
+ // AppendHistogramSTZeroSample adds synthetic zero sample for the given st timestamp,
// which will be associated with given series, labels and the incoming
- // sample's t (timestamp). AppendHistogramCTZeroSample returns error if zero sample can't be
- // appended, for example when ct is too old, or when it would collide with
+ // sample's t (timestamp). AppendHistogramSTZeroSample returns error if zero sample can't be
+ // appended, for example when st is too old, or when it would collide with
// incoming sample (sample has priority).
//
- // AppendHistogramCTZeroSample has to be called before the corresponding histogram AppendHistogram.
+ // AppendHistogramSTZeroSample has to be called before the corresponding histogram AppendHistogram.
// A series reference number is returned which can be used to modify the
- // CT for the given series in the same or later transactions.
+ // ST for the given series in the same or later transactions.
// Returned reference numbers are ephemeral and may be rejected in calls
- // to AppendHistogramCTZeroSample() at any point.
+ // to AppendHistogramSTZeroSample() at any point.
//
// If the reference is 0 it must not be used for caching.
- AppendHistogramCTZeroSample(ref SeriesRef, l labels.Labels, t, ct int64, h *histogram.Histogram, fh *histogram.FloatHistogram) (SeriesRef, error)
+ AppendHistogramSTZeroSample(ref SeriesRef, l labels.Labels, t, st int64, h *histogram.Histogram, fh *histogram.FloatHistogram) (SeriesRef, error)
}
// MetadataUpdater provides an interface for associating metadata to stored series.
+//
+// WARNING: Work AppendableV2 is in progress. Appendable will be removed soon (ETA: Q2 2026).
type MetadataUpdater interface {
// UpdateMetadata updates a metadata entry for the given series and labels.
// A series reference number is returned which can be used to modify the
@@ -366,22 +377,24 @@ type MetadataUpdater interface {
UpdateMetadata(ref SeriesRef, l labels.Labels, m metadata.Metadata) (SeriesRef, error)
}
-// CreatedTimestampAppender provides an interface for appending CT to storage.
-type CreatedTimestampAppender interface {
- // AppendCTZeroSample adds synthetic zero sample for the given ct timestamp,
+// StartTimestampAppender provides an interface for appending ST to storage.
+//
+// WARNING: Work AppendableV2 is in progress. Appendable will be removed soon (ETA: Q2 2026).
+type StartTimestampAppender interface {
+ // AppendSTZeroSample adds synthetic zero sample for the given st timestamp,
// which will be associated with given series, labels and the incoming
- // sample's t (timestamp). AppendCTZeroSample returns error if zero sample can't be
- // appended, for example when ct is too old, or when it would collide with
+ // sample's t (timestamp). AppendSTZeroSample returns error if zero sample can't be
+ // appended, for example when st is too old, or when it would collide with
// incoming sample (sample has priority).
//
- // AppendCTZeroSample has to be called before the corresponding sample Append.
+ // AppendSTZeroSample has to be called before the corresponding sample Append.
// A series reference number is returned which can be used to modify the
- // CT for the given series in the same or later transactions.
+ // ST for the given series in the same or later transactions.
// Returned reference numbers are ephemeral and may be rejected in calls
- // to AppendCTZeroSample() at any point.
+ // to AppendSTZeroSample() at any point.
//
// If the reference is 0 it must not be used for caching.
- AppendCTZeroSample(ref SeriesRef, l labels.Labels, t, ct int64) (SeriesRef, error)
+ AppendSTZeroSample(ref SeriesRef, l labels.Labels, t, st int64) (SeriesRef, error)
}
// SeriesSet contains a set of series.
@@ -389,10 +402,10 @@ type SeriesSet interface {
Next() bool
// At returns full series. Returned series should be iterable even after Next is called.
At() Series
- // The error that iteration has failed with.
+ // Err returns the error that iteration has failed with.
// When an error occurs, set cannot continue to iterate.
Err() error
- // A collection of warnings for the whole set.
+ // Warnings returns a collection of warnings for the whole set.
// Warnings could be return even iteration has not failed with error.
Warnings() annotations.Annotations
}
diff --git a/vendor/github.com/prometheus/prometheus/storage/interface_append.go b/vendor/github.com/prometheus/prometheus/storage/interface_append.go
new file mode 100644
index 00000000000..cc7045dbd58
--- /dev/null
+++ b/vendor/github.com/prometheus/prometheus/storage/interface_append.go
@@ -0,0 +1,182 @@
+// 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 storage
+
+import (
+ "context"
+ "errors"
+
+ "github.com/prometheus/prometheus/model/exemplar"
+ "github.com/prometheus/prometheus/model/histogram"
+ "github.com/prometheus/prometheus/model/labels"
+ "github.com/prometheus/prometheus/model/metadata"
+)
+
+// AppendableV2 allows creating AppenderV2.
+type AppendableV2 interface {
+ // AppenderV2 returns a new appender for the storage.
+ //
+ // Implementations CAN choose whether to use the context e.g. for deadlines,
+ // but it's not mandatory.
+ AppenderV2(ctx context.Context) AppenderV2
+}
+
+// AOptions is a shorthand for AppendV2Options.
+// NOTE: AppendOption is used already.
+type AOptions = AppendV2Options
+
+// AppendV2Options provides optional, auxiliary data and configuration for AppenderV2.Append.
+type AppendV2Options struct {
+ // MetricFamilyName (optional) provides metric family name for the appended sample's
+ // series. If the client of the AppenderV2 has this information
+ // (e.g. from scrape) it's recommended to pass it to the appender.
+ //
+ // Provided string bytes are unsafe to reuse, it only lives for the duration of the Append call.
+ //
+ // Some implementations use this to avoid slow and prone to error metric family detection for:
+ // * Metadata per metric family storages (e.g. Prometheus metadata WAL/API/RW1)
+ // * Strictly complex types storages (e.g. OpenTelemetry Collector).
+ //
+ // NOTE(krajorama): Example purpose is highlighted in OTLP ingestion: OTLP calculates the
+ // metric family name for all metrics and uses it for generating summary,
+ // histogram series by adding the magic suffixes. The metric family name is
+ // passed down to the appender in case the storage needs it for metadata updates.
+ // Known user of this is Mimir that implements /api/v1/metadata and uses
+ // Remote-Write 1.0 for this. Might be removed later if no longer
+ // needed by any downstream project.
+ // NOTE(bwplotka): Long term, once Prometheus uses complex types on storage level
+ // the MetricFamilyName can be removed as MetricFamilyName will equal to __name__ always.
+ MetricFamilyName string
+
+ // Metadata (optional) attached to the appended sample.
+ // Metadata strings are safe for reuse.
+ // IMPORTANT: Appender v1 was only providing update. This field MUST be
+ // set (if known) even if it didn't change since the last iteration.
+ // This moves the responsibility for metadata storage options to TSDB.
+ Metadata metadata.Metadata
+
+ // Exemplars (optional) attached to the appended sample.
+ // Exemplar slice MUST be sorted by Exemplar.TS.
+ // Exemplar slice is unsafe for reuse.
+ Exemplars []exemplar.Exemplar
+
+ // RejectOutOfOrder tells implementation that this append should not be out
+ // of order. An OOO append MUST be rejected with storage.ErrOutOfOrderSample
+ // error.
+ RejectOutOfOrder bool
+}
+
+// AppendPartialError represents an AppenderV2.Append error that tells
+// callers sample was written but some auxiliary optional data (e.g. exemplars)
+// was not (or partially written)
+//
+// It's up to the caller to decide if it's an ignorable error or not, plus
+// it allows extra reporting (e.g. for Remote Write 2.0 X-Remote-Write-Written headers).
+type AppendPartialError struct {
+ ExemplarErrors []error
+}
+
+// Error returns combined error string.
+func (e *AppendPartialError) Error() string {
+ errs := errors.Join(e.ExemplarErrors...)
+ if errs == nil {
+ return ""
+ }
+ return errs.Error()
+}
+
+var _ error = &AppendPartialError{}
+
+// AppenderV2 provides appends against a storage for all types of samples.
+// It must be completed with a call to Commit or Rollback and must not be reused afterwards.
+//
+// Operations on the AppenderV2 interface are not goroutine-safe.
+//
+// The order of samples appended via the AppenderV2 is preserved within each series.
+// I.e. timestamp order within batch is not validated, samples are not reordered per timestamp or by float/histogram
+// type.
+type AppenderV2 interface {
+ AppenderTransaction
+
+ // Append appends a sample and related exemplars, metadata, and start timestamp (st) to the storage.
+ //
+ // ref (optional) represents the stable ID for the given series identified by ls (excluding metadata).
+ // Callers MAY provide the ref to help implementation avoid ls -> ref computation, otherwise ref MUST be 0 (unknown).
+ //
+ // ls represents labels for the sample's series.
+ //
+ // st (optional) represents sample start timestamp. 0 means unknown. Implementations
+ // are responsible for any potential ST storage logic (e.g. ST zero injections).
+ //
+ // t represents sample timestamp.
+ //
+ // v, h, fh represents sample value for each sample type.
+ // Callers MUST only provide one of the sample types (either v, h or fh).
+ // Implementations can detect the type of the sample with the following switch:
+ //
+ // switch {
+ // case fh != nil: It's a float histogram append.
+ // case h != nil: It's a histogram append.
+ // default: It's a float append.
+ // }
+ // TODO(bwplotka): We plan to experiment on using generics for complex sampleType, but do it after we unify interface (derisk) and before we add native summaries.
+ //
+ // Implementations MUST attempt to append sample even if metadata, exemplar or (st) start timestamp appends fail.
+ // Implementations MAY return AppendPartialError as an error. Use errors.As to detect.
+ // For the successful Append, Implementations MUST return valid SeriesRef that represents ls.
+ // NOTE(bwplotka): Given OTLP and native histograms and the relaxation of the requirement for
+ // type and unit suffixes in metric names we start to hit cases of ls being not enough for id
+ // of the series (metadata matters). Current solution is to enable 'type-and-unit-label' features for those cases, but we may
+ // start to extend the id with metadata one day.
+ Append(ref SeriesRef, ls labels.Labels, st, t int64, v float64, h *histogram.Histogram, fh *histogram.FloatHistogram, opts AppendV2Options) (SeriesRef, error)
+}
+
+// AppenderTransaction allows transactional appends.
+type AppenderTransaction interface {
+ // Commit submits the collected samples and purges the batch. If Commit
+ // returns a non-nil error, it also rolls back all modifications made in
+ // the appender so far, as Rollback would do. In any case, an Appender
+ // must not be used anymore after Commit has been called.
+ Commit() error
+
+ // Rollback rolls back all modifications made in the appender so far.
+ // Appender has to be discarded after rollback.
+ Rollback() error
+}
+
+// LimitedAppenderV1 is an Appender that only supports appending float and histogram samples.
+// This is to support migration to AppenderV2.
+// TODO(bwplotka): Remove once migration to AppenderV2 is fully complete.
+type LimitedAppenderV1 interface {
+ Append(ref SeriesRef, l labels.Labels, t int64, v float64) (SeriesRef, error)
+ AppendHistogram(ref SeriesRef, l labels.Labels, t int64, h *histogram.Histogram, fh *histogram.FloatHistogram) (SeriesRef, error)
+}
+
+// AppenderV2AsLimitedV1 returns appender that exposes AppenderV2 as LimitedAppenderV1
+// TODO(bwplotka): Remove once migration to AppenderV2 is fully complete.
+func AppenderV2AsLimitedV1(app AppenderV2) LimitedAppenderV1 {
+ return &limitedAppenderV1{AppenderV2: app}
+}
+
+type limitedAppenderV1 struct {
+ AppenderV2
+}
+
+func (a *limitedAppenderV1) Append(ref SeriesRef, l labels.Labels, t int64, v float64) (SeriesRef, error) {
+ return a.AppenderV2.Append(ref, l, 0, t, v, nil, nil, AppendV2Options{})
+}
+
+func (a *limitedAppenderV1) AppendHistogram(ref SeriesRef, l labels.Labels, t int64, h *histogram.Histogram, fh *histogram.FloatHistogram) (SeriesRef, error) {
+ return a.AppenderV2.Append(ref, l, 0, t, 0, h, fh, AppendV2Options{})
+}
diff --git a/vendor/github.com/prometheus/prometheus/storage/remote/azuread/azuread.go b/vendor/github.com/prometheus/prometheus/storage/remote/azuread/azuread.go
index ea2a816d942..638ba586fce 100644
--- a/vendor/github.com/prometheus/prometheus/storage/remote/azuread/azuread.go
+++ b/vendor/github.com/prometheus/prometheus/storage/remote/azuread/azuread.go
@@ -103,6 +103,9 @@ type AzureADConfig struct { //nolint:revive // exported.
// Cloud is the Azure cloud in which the service is running. Example: AzurePublic/AzureGovernment/AzureChina.
Cloud string `yaml:"cloud,omitempty"`
+
+ // Scope is the custom OAuth 2.0 scope to request when acquiring tokens.
+ Scope string `yaml:"scope,omitempty"`
}
// azureADRoundTripper is used to store the roundtripper and the tokenprovider.
@@ -211,6 +214,12 @@ func (c *AzureADConfig) Validate() error {
}
}
+ if c.Scope != "" {
+ if matched, err := regexp.MatchString("^[\\w\\s:/.\\-]+$", c.Scope); err != nil || !matched {
+ return errors.New("the provided scope contains invalid characters")
+ }
+ }
+
return nil
}
@@ -360,14 +369,22 @@ func newSDKTokenCredential(clientOpts *azcore.ClientOptions, sdkConfig *SDKConfi
// newTokenProvider helps to fetch accessToken for different types of credential. This also takes care of
// refreshing the accessToken before expiry. This accessToken is attached to the Authorization header while making requests.
func newTokenProvider(cfg *AzureADConfig, cred azcore.TokenCredential) (*tokenProvider, error) {
- audience, err := getAudience(cfg.Cloud)
- if err != nil {
- return nil, err
+ var scopes []string
+
+ // Use custom scope if provided, otherwise fallback to cloud-specific audience
+ if cfg.Scope != "" {
+ scopes = []string{cfg.Scope}
+ } else {
+ audience, err := getAudience(cfg.Cloud)
+ if err != nil {
+ return nil, err
+ }
+ scopes = []string{audience}
}
tokenProvider := &tokenProvider{
credentialClient: cred,
- options: &policy.TokenRequestOptions{Scopes: []string{audience}},
+ options: &policy.TokenRequestOptions{Scopes: scopes},
}
return tokenProvider, nil
diff --git a/vendor/github.com/prometheus/prometheus/storage/remote/codec.go b/vendor/github.com/prometheus/prometheus/storage/remote/codec.go
index 7e21909354e..059d5e66ce2 100644
--- a/vendor/github.com/prometheus/prometheus/storage/remote/codec.go
+++ b/vendor/github.com/prometheus/prometheus/storage/remote/codec.go
@@ -389,6 +389,11 @@ type concreteSeriesIterator struct {
curValType chunkenc.ValueType
series *concreteSeries
err error
+
+ // These are pre-filled with the current model histogram if curValType
+ // is ValHistogram or ValFloatHistogram, respectively.
+ curH *histogram.Histogram
+ curFH *histogram.FloatHistogram
}
func newConcreteSeriesIterator(series *concreteSeries) chunkenc.Iterator {
@@ -461,9 +466,7 @@ func (c *concreteSeriesIterator) Seek(t int64) chunkenc.ValueType {
c.curValType = chunkenc.ValHistogram
}
if c.curValType == chunkenc.ValHistogram {
- h := &c.series.histograms[c.histogramsCur]
- c.curValType = getHistogramValType(h)
- c.err = validateHistogramSchema(h)
+ c.setCurrentHistogram()
}
if c.err != nil {
c.curValType = chunkenc.ValNone
@@ -471,18 +474,57 @@ func (c *concreteSeriesIterator) Seek(t int64) chunkenc.ValueType {
return c.curValType
}
-func validateHistogramSchema(h *prompb.Histogram) error {
- if histogram.IsKnownSchema(h.Schema) {
- return nil
+// setCurrentHistogram pre-fills either the curH or the curFH field with a
+// converted model histogram and sets c.curValType accordingly. It validates the
+// histogram and sets c.err accordingly. This all has to be done in Seek() and
+// Next() already so that we know if the histogram we got from the remote-read
+// source is valid or not before we allow the AtHistogram()/AtFloatHistogram()
+// call.
+func (c *concreteSeriesIterator) setCurrentHistogram() {
+ pbH := c.series.histograms[c.histogramsCur]
+
+ // Basic schema check first.
+ schema := pbH.Schema
+ if !histogram.IsKnownSchema(schema) {
+ c.err = histogram.UnknownSchemaError(schema)
+ return
}
- return histogram.UnknownSchemaError(h.Schema)
-}
-func getHistogramValType(h *prompb.Histogram) chunkenc.ValueType {
- if h.IsFloatHistogram() {
- return chunkenc.ValFloatHistogram
+ if pbH.IsFloatHistogram() {
+ c.curValType = chunkenc.ValFloatHistogram
+ mFH := pbH.ToFloatHistogram()
+ if mFH.Schema > histogram.ExponentialSchemaMax && mFH.Schema <= histogram.ExponentialSchemaMaxReserved {
+ // This is a very slow path, but it should only happen if the
+ // sample is from a newer Prometheus version that supports higher
+ // resolution.
+ if err := mFH.ReduceResolution(histogram.ExponentialSchemaMax); err != nil {
+ c.err = err
+ return
+ }
+ }
+ if err := mFH.Validate(); err != nil {
+ c.err = err
+ return
+ }
+ c.curFH = mFH
+ return
}
- return chunkenc.ValHistogram
+ c.curValType = chunkenc.ValHistogram
+ mH := pbH.ToIntHistogram()
+ if mH.Schema > histogram.ExponentialSchemaMax && mH.Schema <= histogram.ExponentialSchemaMaxReserved {
+ // This is a very slow path, but it should only happen if the
+ // sample is from a newer Prometheus version that supports higher
+ // resolution.
+ if err := mH.ReduceResolution(histogram.ExponentialSchemaMax); err != nil {
+ c.err = err
+ return
+ }
+ }
+ if err := mH.Validate(); err != nil {
+ c.err = err
+ return
+ }
+ c.curH = mH
}
// At implements chunkenc.Iterator.
@@ -499,31 +541,19 @@ func (c *concreteSeriesIterator) AtHistogram(*histogram.Histogram) (int64, *hist
if c.curValType != chunkenc.ValHistogram {
panic("iterator is not on an integer histogram sample")
}
- h := c.series.histograms[c.histogramsCur]
- mh := h.ToIntHistogram()
- if mh.Schema > histogram.ExponentialSchemaMax && mh.Schema <= histogram.ExponentialSchemaMaxReserved {
- // This is a very slow path, but it should only happen if the
- // sample is from a newer Prometheus version that supports higher
- // resolution.
- mh.ReduceResolution(histogram.ExponentialSchemaMax)
- }
- return h.Timestamp, mh
+ return c.series.histograms[c.histogramsCur].Timestamp, c.curH
}
// AtFloatHistogram implements chunkenc.Iterator.
func (c *concreteSeriesIterator) AtFloatHistogram(*histogram.FloatHistogram) (int64, *histogram.FloatHistogram) {
- if c.curValType == chunkenc.ValHistogram || c.curValType == chunkenc.ValFloatHistogram {
- fh := c.series.histograms[c.histogramsCur]
- mfh := fh.ToFloatHistogram() // integer will be auto-converted.
- if mfh.Schema > histogram.ExponentialSchemaMax && mfh.Schema <= histogram.ExponentialSchemaMaxReserved {
- // This is a very slow path, but it should only happen if the
- // sample is from a newer Prometheus version that supports higher
- // resolution.
- mfh.ReduceResolution(histogram.ExponentialSchemaMax)
- }
- return fh.Timestamp, mfh
+ switch c.curValType {
+ case chunkenc.ValFloatHistogram:
+ return c.series.histograms[c.histogramsCur].Timestamp, c.curFH
+ case chunkenc.ValHistogram:
+ return c.series.histograms[c.histogramsCur].Timestamp, c.curH.ToFloat(nil)
+ default:
+ panic("iterator is not on a histogram sample")
}
- panic("iterator is not on a histogram sample")
}
// AtT implements chunkenc.Iterator.
@@ -571,9 +601,7 @@ func (c *concreteSeriesIterator) Next() chunkenc.ValueType {
}
if c.curValType == chunkenc.ValHistogram {
- h := &c.series.histograms[c.histogramsCur]
- c.curValType = getHistogramValType(h)
- c.err = validateHistogramSchema(h)
+ c.setCurrentHistogram()
}
if c.err != nil {
c.curValType = chunkenc.ValNone
diff --git a/vendor/github.com/prometheus/prometheus/storage/remote/otlptranslator/prometheusremotewrite/combined_appender.go b/vendor/github.com/prometheus/prometheus/storage/remote/otlptranslator/prometheusremotewrite/combined_appender.go
index 1441aecb6d0..883b8d31423 100644
--- a/vendor/github.com/prometheus/prometheus/storage/remote/otlptranslator/prometheusremotewrite/combined_appender.go
+++ b/vendor/github.com/prometheus/prometheus/storage/remote/otlptranslator/prometheusremotewrite/combined_appender.go
@@ -49,10 +49,10 @@ type Metadata struct {
type CombinedAppender interface {
// AppendSample appends a sample and related exemplars, metadata, and
// created timestamp to the storage.
- AppendSample(ls labels.Labels, meta Metadata, ct, t int64, v float64, es []exemplar.Exemplar) error
+ AppendSample(ls labels.Labels, meta Metadata, st, t int64, v float64, es []exemplar.Exemplar) error
// AppendHistogram appends a histogram and related exemplars, metadata, and
// created timestamp to the storage.
- AppendHistogram(ls labels.Labels, meta Metadata, ct, t int64, h *histogram.Histogram, es []exemplar.Exemplar) error
+ AppendHistogram(ls labels.Labels, meta Metadata, st, t int64, h *histogram.Histogram, es []exemplar.Exemplar) error
}
// CombinedAppenderMetrics is for the metrics observed by the
@@ -82,11 +82,12 @@ func NewCombinedAppenderMetrics(reg prometheus.Registerer) CombinedAppenderMetri
// NewCombinedAppender creates a combined appender that sets start times and
// updates metadata for each series only once, and appends samples and
// exemplars for each call.
-func NewCombinedAppender(app storage.Appender, logger *slog.Logger, ingestCTZeroSample bool, metrics CombinedAppenderMetrics) CombinedAppender {
+func NewCombinedAppender(app storage.Appender, logger *slog.Logger, ingestSTZeroSample, appendMetadata bool, metrics CombinedAppenderMetrics) CombinedAppender {
return &combinedAppender{
app: app,
logger: logger,
- ingestCTZeroSample: ingestCTZeroSample,
+ ingestSTZeroSample: ingestSTZeroSample,
+ appendMetadata: appendMetadata,
refs: make(map[uint64]seriesRef),
samplesAppendedWithoutMetadata: metrics.samplesAppendedWithoutMetadata,
outOfOrderExemplars: metrics.outOfOrderExemplars,
@@ -95,7 +96,7 @@ func NewCombinedAppender(app storage.Appender, logger *slog.Logger, ingestCTZero
type seriesRef struct {
ref storage.SeriesRef
- ct int64
+ st int64
ls labels.Labels
meta metadata.Metadata
}
@@ -105,27 +106,28 @@ type combinedAppender struct {
logger *slog.Logger
samplesAppendedWithoutMetadata prometheus.Counter
outOfOrderExemplars prometheus.Counter
- ingestCTZeroSample bool
+ ingestSTZeroSample bool
+ appendMetadata bool
// Used to ensure we only update metadata and created timestamps once, and to share storage.SeriesRefs.
// To detect hash collision it also stores the labels.
// There is no overflow/conflict list, the TSDB will handle that part.
refs map[uint64]seriesRef
}
-func (b *combinedAppender) AppendSample(ls labels.Labels, meta Metadata, ct, t int64, v float64, es []exemplar.Exemplar) (err error) {
- return b.appendFloatOrHistogram(ls, meta.Metadata, ct, t, v, nil, es)
+func (b *combinedAppender) AppendSample(ls labels.Labels, meta Metadata, st, t int64, v float64, es []exemplar.Exemplar) (err error) {
+ return b.appendFloatOrHistogram(ls, meta.Metadata, st, t, v, nil, es)
}
-func (b *combinedAppender) AppendHistogram(ls labels.Labels, meta Metadata, ct, t int64, h *histogram.Histogram, es []exemplar.Exemplar) (err error) {
+func (b *combinedAppender) AppendHistogram(ls labels.Labels, meta Metadata, st, t int64, h *histogram.Histogram, es []exemplar.Exemplar) (err error) {
if h == nil {
// Sanity check, we should never get here with a nil histogram.
b.logger.Error("Received nil histogram in CombinedAppender.AppendHistogram", "series", ls.String())
return errors.New("internal error, attempted to append nil histogram")
}
- return b.appendFloatOrHistogram(ls, meta.Metadata, ct, t, 0, h, es)
+ return b.appendFloatOrHistogram(ls, meta.Metadata, st, t, 0, h, es)
}
-func (b *combinedAppender) appendFloatOrHistogram(ls labels.Labels, meta metadata.Metadata, ct, t int64, v float64, h *histogram.Histogram, es []exemplar.Exemplar) (err error) {
+func (b *combinedAppender) appendFloatOrHistogram(ls labels.Labels, meta metadata.Metadata, st, t int64, v float64, h *histogram.Histogram, es []exemplar.Exemplar) (err error) {
hash := ls.Hash()
series, exists := b.refs[hash]
ref := series.ref
@@ -138,28 +140,28 @@ func (b *combinedAppender) appendFloatOrHistogram(ls labels.Labels, meta metadat
exists = false
ref = 0
}
- updateRefs := !exists || series.ct != ct
- if updateRefs && ct != 0 && ct < t && b.ingestCTZeroSample {
+ updateRefs := !exists || series.st != st
+ if updateRefs && st != 0 && st < t && b.ingestSTZeroSample {
var newRef storage.SeriesRef
if h != nil {
- newRef, err = b.app.AppendHistogramCTZeroSample(ref, ls, t, ct, h, nil)
+ newRef, err = b.app.AppendHistogramSTZeroSample(ref, ls, t, st, h, nil)
} else {
- newRef, err = b.app.AppendCTZeroSample(ref, ls, t, ct)
+ newRef, err = b.app.AppendSTZeroSample(ref, ls, t, st)
}
if err != nil {
- if !errors.Is(err, storage.ErrOutOfOrderCT) && !errors.Is(err, storage.ErrDuplicateSampleForTimestamp) {
+ if !errors.Is(err, storage.ErrOutOfOrderST) && !errors.Is(err, storage.ErrDuplicateSampleForTimestamp) {
// Even for the first sample OOO is a common scenario because
- // we can't tell if a CT was already ingested in a previous request.
+ // we can't tell if a ST was already ingested in a previous request.
// We ignore the error.
// ErrDuplicateSampleForTimestamp is also a common scenario because
// unknown start times in Opentelemetry are indicated by setting
// the start time to the same as the first sample time.
// https://opentelemetry.io/docs/specs/otel/metrics/data-model/#cumulative-streams-handling-unknown-start-time
- b.logger.Warn("Error when appending CT from OTLP", "err", err, "series", ls.String(), "created_timestamp", ct, "timestamp", t, "sample_type", sampleType(h))
+ b.logger.Warn("Error when appending ST from OTLP", "err", err, "series", ls.String(), "start_timestamp", st, "timestamp", t, "sample_type", sampleType(h))
}
} else {
// We only use the returned reference on success as otherwise an
- // error of CT append could invalidate the series reference.
+ // error of ST append could invalidate the series reference.
ref = newRef
}
}
@@ -189,25 +191,29 @@ func (b *combinedAppender) appendFloatOrHistogram(ls labels.Labels, meta metadat
return err
}
- if !exists || series.meta.Help != meta.Help || series.meta.Type != meta.Type || series.meta.Unit != meta.Unit {
- updateRefs = true
- // If this is the first time we see this series, set the metadata.
- _, err := b.app.UpdateMetadata(ref, ls, meta)
- if err != nil {
- b.samplesAppendedWithoutMetadata.Add(1)
- b.logger.Warn("Error while updating metadata from OTLP", "err", err)
- }
- }
+ metadataChanged := exists && (series.meta.Help != meta.Help || series.meta.Type != meta.Type || series.meta.Unit != meta.Unit)
- if updateRefs {
+ // Update cache if references changed or metadata changed.
+ if updateRefs || metadataChanged {
b.refs[hash] = seriesRef{
ref: ref,
- ct: ct,
+ st: st,
ls: ls,
meta: meta,
}
}
+ // Update metadata in storage if enabled and needed.
+ if b.appendMetadata && (!exists || metadataChanged) {
+ // Only update metadata in WAL if the metadata-wal-records feature is enabled.
+ // Without this feature, metadata is not persisted to WAL.
+ _, err := b.app.UpdateMetadata(ref, ls, meta)
+ if err != nil {
+ b.samplesAppendedWithoutMetadata.Add(1)
+ b.logger.Warn("Error while updating metadata from OTLP", "err", err)
+ }
+ }
+
b.appendExemplars(ref, ls, es)
return err
diff --git a/vendor/github.com/prometheus/prometheus/storage/remote/otlptranslator/prometheusremotewrite/histograms.go b/vendor/github.com/prometheus/prometheus/storage/remote/otlptranslator/prometheusremotewrite/histograms.go
index 5606fa4d91a..c93a00db760 100644
--- a/vendor/github.com/prometheus/prometheus/storage/remote/otlptranslator/prometheusremotewrite/histograms.go
+++ b/vendor/github.com/prometheus/prometheus/storage/remote/otlptranslator/prometheusremotewrite/histograms.go
@@ -67,13 +67,13 @@ func (c *PrometheusConverter) addExponentialHistogramDataPoints(ctx context.Cont
return annots, err
}
ts := convertTimeStamp(pt.Timestamp())
- ct := convertTimeStamp(pt.StartTimestamp())
+ st := convertTimeStamp(pt.StartTimestamp())
exemplars, err := c.getPromExemplars(ctx, pt.Exemplars())
if err != nil {
return annots, err
}
// OTel exponential histograms are always Int Histograms.
- if err = c.appender.AppendHistogram(lbls, meta, ct, ts, hp, exemplars); err != nil {
+ if err = c.appender.AppendHistogram(lbls, meta, st, ts, hp, exemplars); err != nil {
return annots, err
}
}
@@ -286,12 +286,12 @@ func (c *PrometheusConverter) addCustomBucketsHistogramDataPoints(ctx context.Co
return annots, err
}
ts := convertTimeStamp(pt.Timestamp())
- ct := convertTimeStamp(pt.StartTimestamp())
+ st := convertTimeStamp(pt.StartTimestamp())
exemplars, err := c.getPromExemplars(ctx, pt.Exemplars())
if err != nil {
return annots, err
}
- if err = c.appender.AppendHistogram(lbls, meta, ct, ts, hp, exemplars); err != nil {
+ if err = c.appender.AppendHistogram(lbls, meta, st, ts, hp, exemplars); err != nil {
return annots, err
}
}
diff --git a/vendor/github.com/prometheus/prometheus/storage/remote/otlptranslator/prometheusremotewrite/metrics_to_prw.go b/vendor/github.com/prometheus/prometheus/storage/remote/otlptranslator/prometheusremotewrite/metrics_to_prw.go
index 5e575e61744..f43e4964b11 100644
--- a/vendor/github.com/prometheus/prometheus/storage/remote/otlptranslator/prometheusremotewrite/metrics_to_prw.go
+++ b/vendor/github.com/prometheus/prometheus/storage/remote/otlptranslator/prometheusremotewrite/metrics_to_prw.go
@@ -140,14 +140,6 @@ func (c *PrometheusConverter) FromMetrics(ctx context.Context, md pmetric.Metric
c.seenTargetInfo = make(map[targetInfoKey]struct{})
resourceMetricsSlice := md.ResourceMetrics()
- numMetrics := 0
- for i := 0; i < resourceMetricsSlice.Len(); i++ {
- scopeMetricsSlice := resourceMetricsSlice.At(i).ScopeMetrics()
- for j := 0; j < scopeMetricsSlice.Len(); j++ {
- numMetrics += scopeMetricsSlice.At(j).Metrics().Len()
- }
- }
-
for i := 0; i < resourceMetricsSlice.Len(); i++ {
resourceMetrics := resourceMetricsSlice.At(i)
resource := resourceMetrics.Resource()
diff --git a/vendor/github.com/prometheus/prometheus/storage/remote/otlptranslator/prometheusremotewrite/number_data_points.go b/vendor/github.com/prometheus/prometheus/storage/remote/otlptranslator/prometheusremotewrite/number_data_points.go
index cdae9787365..8f30dbb6b60 100644
--- a/vendor/github.com/prometheus/prometheus/storage/remote/otlptranslator/prometheusremotewrite/number_data_points.go
+++ b/vendor/github.com/prometheus/prometheus/storage/remote/otlptranslator/prometheusremotewrite/number_data_points.go
@@ -61,8 +61,8 @@ func (c *PrometheusConverter) addGaugeNumberDataPoints(ctx context.Context, data
val = math.Float64frombits(value.StaleNaN)
}
ts := convertTimeStamp(pt.Timestamp())
- ct := convertTimeStamp(pt.StartTimestamp())
- if err := c.appender.AppendSample(labels, meta, ct, ts, val, nil); err != nil {
+ st := convertTimeStamp(pt.StartTimestamp())
+ if err := c.appender.AppendSample(labels, meta, st, ts, val, nil); err != nil {
return err
}
}
@@ -104,12 +104,12 @@ func (c *PrometheusConverter) addSumNumberDataPoints(ctx context.Context, dataPo
val = math.Float64frombits(value.StaleNaN)
}
ts := convertTimeStamp(pt.Timestamp())
- ct := convertTimeStamp(pt.StartTimestamp())
+ st := convertTimeStamp(pt.StartTimestamp())
exemplars, err := c.getPromExemplars(ctx, pt.Exemplars())
if err != nil {
return err
}
- if err := c.appender.AppendSample(lbls, meta, ct, ts, val, exemplars); err != nil {
+ if err := c.appender.AppendSample(lbls, meta, st, ts, val, exemplars); err != nil {
return err
}
}
diff --git a/vendor/github.com/prometheus/prometheus/storage/remote/queue_manager.go b/vendor/github.com/prometheus/prometheus/storage/remote/queue_manager.go
index 25d3a94b6ac..5fc5f5564ba 100644
--- a/vendor/github.com/prometheus/prometheus/storage/remote/queue_manager.go
+++ b/vendor/github.com/prometheus/prometheus/storage/remote/queue_manager.go
@@ -31,6 +31,7 @@ import (
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
semconv "go.opentelemetry.io/otel/semconv/v1.21.0"
+ "go.opentelemetry.io/otel/trace"
"go.uber.org/atomic"
"github.com/prometheus/prometheus/config"
@@ -644,63 +645,78 @@ func isSampleOld(baseTime time.Time, sampleAgeLimit time.Duration, ts int64) boo
return sampleTs.Before(limitTs)
}
+// timeSeriesAgeChecker encapsulates the logic for checking if time series data is too old.
+type timeSeriesAgeChecker struct {
+ metrics *queueManagerMetrics
+ baseTime time.Time
+ sampleAgeLimit time.Duration
+}
+
+// checkAndRecordIfOld checks if a timestamp is too old and records the appropriate metric.
+// Returns true if the data should be dropped.
+func (c *timeSeriesAgeChecker) checkAndRecordIfOld(timestamp int64, dataType string) bool {
+ if c.sampleAgeLimit == 0 {
+ // If sampleAgeLimit is unset, then we never skip samples due to their age.
+ return false
+ }
+
+ if !isSampleOld(c.baseTime, c.sampleAgeLimit, timestamp) {
+ return false
+ }
+
+ // Record the drop in metrics.
+ switch dataType {
+ case "sample":
+ c.metrics.droppedSamplesTotal.WithLabelValues(reasonTooOld).Inc()
+ case "histogram":
+ c.metrics.droppedHistogramsTotal.WithLabelValues(reasonTooOld).Inc()
+ case "exemplar":
+ c.metrics.droppedExemplarsTotal.WithLabelValues(reasonTooOld).Inc()
+ }
+ return true
+}
+
func isTimeSeriesOldFilter(metrics *queueManagerMetrics, baseTime time.Time, sampleAgeLimit time.Duration) func(ts prompb.TimeSeries) bool {
+ checker := &timeSeriesAgeChecker{
+ metrics: metrics,
+ baseTime: baseTime,
+ sampleAgeLimit: sampleAgeLimit,
+ }
+
return func(ts prompb.TimeSeries) bool {
- if sampleAgeLimit == 0 {
- // If sampleAgeLimit is unset, then we never skip samples due to their age.
- return false
- }
- switch {
// Only the first element should be set in the series, therefore we only check the first element.
+ switch {
case len(ts.Samples) > 0:
- if isSampleOld(baseTime, sampleAgeLimit, ts.Samples[0].Timestamp) {
- metrics.droppedSamplesTotal.WithLabelValues(reasonTooOld).Inc()
- return true
- }
+ return checker.checkAndRecordIfOld(ts.Samples[0].Timestamp, "sample")
case len(ts.Histograms) > 0:
- if isSampleOld(baseTime, sampleAgeLimit, ts.Histograms[0].Timestamp) {
- metrics.droppedHistogramsTotal.WithLabelValues(reasonTooOld).Inc()
- return true
- }
+ return checker.checkAndRecordIfOld(ts.Histograms[0].Timestamp, "histogram")
case len(ts.Exemplars) > 0:
- if isSampleOld(baseTime, sampleAgeLimit, ts.Exemplars[0].Timestamp) {
- metrics.droppedExemplarsTotal.WithLabelValues(reasonTooOld).Inc()
- return true
- }
+ return checker.checkAndRecordIfOld(ts.Exemplars[0].Timestamp, "exemplar")
default:
return false
}
- return false
}
}
func isV2TimeSeriesOldFilter(metrics *queueManagerMetrics, baseTime time.Time, sampleAgeLimit time.Duration) func(ts writev2.TimeSeries) bool {
+ checker := &timeSeriesAgeChecker{
+ metrics: metrics,
+ baseTime: baseTime,
+ sampleAgeLimit: sampleAgeLimit,
+ }
+
return func(ts writev2.TimeSeries) bool {
- if sampleAgeLimit == 0 {
- // If sampleAgeLimit is unset, then we never skip samples due to their age.
- return false
- }
- switch {
// Only the first element should be set in the series, therefore we only check the first element.
+ switch {
case len(ts.Samples) > 0:
- if isSampleOld(baseTime, sampleAgeLimit, ts.Samples[0].Timestamp) {
- metrics.droppedSamplesTotal.WithLabelValues(reasonTooOld).Inc()
- return true
- }
+ return checker.checkAndRecordIfOld(ts.Samples[0].Timestamp, "sample")
case len(ts.Histograms) > 0:
- if isSampleOld(baseTime, sampleAgeLimit, ts.Histograms[0].Timestamp) {
- metrics.droppedHistogramsTotal.WithLabelValues(reasonTooOld).Inc()
- return true
- }
+ return checker.checkAndRecordIfOld(ts.Histograms[0].Timestamp, "histogram")
case len(ts.Exemplars) > 0:
- if isSampleOld(baseTime, sampleAgeLimit, ts.Exemplars[0].Timestamp) {
- metrics.droppedExemplarsTotal.WithLabelValues(reasonTooOld).Inc()
- return true
- }
+ return checker.checkAndRecordIfOld(ts.Exemplars[0].Timestamp, "exemplar")
default:
return false
}
- return false
}
}
@@ -1737,6 +1753,20 @@ func (s *shards) sendSamplesWithBackoff(ctx context.Context, samples []prompb.Ti
}
reqSize := len(req)
+ sc := sendBatchContext{
+ ctx: ctx,
+ sampleCount: sampleCount,
+ exemplarCount: exemplarCount,
+ histogramCount: histogramCount,
+ metadataCount: metadataCount,
+ reqSize: reqSize,
+ }
+
+ metricsUpdater := batchMetricsUpdater{
+ metrics: s.qm.metrics,
+ storeClient: s.qm.storeClient,
+ sentDuration: s.qm.metrics.sentBatchDuration,
+ }
// Since we retry writes via attemptStore and sendWriteRequestWithBackoff we need
// to track the total amount of accepted data across the various attempts.
@@ -1772,33 +1802,14 @@ func (s *shards) sendSamplesWithBackoff(ctx context.Context, samples []prompb.Ti
req = req2
}
- ctx, span := otel.Tracer("").Start(ctx, "Remote Send Batch")
+ ctx, span := createBatchSpan(sc.ctx, sc, s.qm.storeClient.Name(), s.qm.storeClient.Endpoint(), try)
defer span.End()
- span.SetAttributes(
- attribute.Int("request_size", reqSize),
- attribute.Int("samples", sampleCount),
- attribute.Int("try", try),
- attribute.String("remote_name", s.qm.storeClient.Name()),
- attribute.String("remote_url", s.qm.storeClient.Endpoint()),
- )
-
- if exemplarCount > 0 {
- span.SetAttributes(attribute.Int("exemplars", exemplarCount))
- }
- if histogramCount > 0 {
- span.SetAttributes(attribute.Int("histograms", histogramCount))
- }
-
begin := time.Now()
- s.qm.metrics.samplesTotal.Add(float64(sampleCount))
- s.qm.metrics.exemplarsTotal.Add(float64(exemplarCount))
- s.qm.metrics.histogramsTotal.Add(float64(histogramCount))
- s.qm.metrics.metadataTotal.Add(float64(metadataCount))
+ metricsUpdater.recordBatchAttempt(sc, begin)
// Technically for v1, we will likely have empty response stats, but for
// newer Receivers this might be not, so used it in a best effort.
rs, err := s.qm.client().Store(ctx, req, try)
- s.qm.metrics.sentBatchDuration.Observe(time.Since(begin).Seconds())
// TODO(bwplotka): Revisit this once we have Receivers doing retriable partial error
// so far we don't have those, so it's ok to potentially skew statistics.
addStats(rs)
@@ -1811,9 +1822,7 @@ func (s *shards) sendSamplesWithBackoff(ctx context.Context, samples []prompb.Ti
}
onRetry := func() {
- s.qm.metrics.retriedSamplesTotal.Add(float64(sampleCount))
- s.qm.metrics.retriedExemplarsTotal.Add(float64(exemplarCount))
- s.qm.metrics.retriedHistogramsTotal.Add(float64(histogramCount))
+ metricsUpdater.recordRetry(sc)
}
err = s.qm.sendWriteRequestWithBackoff(ctx, attemptStore, onRetry)
@@ -1850,6 +1859,20 @@ func (s *shards) sendV2SamplesWithBackoff(ctx context.Context, samples []writev2
}
reqSize := len(req)
+ sc := sendBatchContext{
+ ctx: ctx,
+ sampleCount: sampleCount,
+ exemplarCount: exemplarCount,
+ histogramCount: histogramCount,
+ metadataCount: metadataCount,
+ reqSize: reqSize,
+ }
+
+ metricsUpdater := batchMetricsUpdater{
+ metrics: s.qm.metrics,
+ storeClient: s.qm.storeClient,
+ sentDuration: s.qm.metrics.sentBatchDuration,
+ }
// Since we retry writes via attemptStore and sendWriteRequestWithBackoff we need
// to track the total amount of accepted data across the various attempts.
@@ -1885,31 +1908,12 @@ func (s *shards) sendV2SamplesWithBackoff(ctx context.Context, samples []writev2
req = req2
}
- ctx, span := otel.Tracer("").Start(ctx, "Remote Send Batch")
+ ctx, span := createBatchSpan(sc.ctx, sc, s.qm.storeClient.Name(), s.qm.storeClient.Endpoint(), try)
defer span.End()
- span.SetAttributes(
- attribute.Int("request_size", reqSize),
- attribute.Int("samples", sampleCount),
- attribute.Int("try", try),
- attribute.String("remote_name", s.qm.storeClient.Name()),
- attribute.String("remote_url", s.qm.storeClient.Endpoint()),
- )
-
- if exemplarCount > 0 {
- span.SetAttributes(attribute.Int("exemplars", exemplarCount))
- }
- if histogramCount > 0 {
- span.SetAttributes(attribute.Int("histograms", histogramCount))
- }
-
begin := time.Now()
- s.qm.metrics.samplesTotal.Add(float64(sampleCount))
- s.qm.metrics.exemplarsTotal.Add(float64(exemplarCount))
- s.qm.metrics.histogramsTotal.Add(float64(histogramCount))
- s.qm.metrics.metadataTotal.Add(float64(metadataCount))
+ metricsUpdater.recordBatchAttempt(sc, begin)
rs, err := s.qm.client().Store(ctx, req, try)
- s.qm.metrics.sentBatchDuration.Observe(time.Since(begin).Seconds())
// TODO(bwplotka): Revisit this once we have Receivers doing retriable partial error
// so far we don't have those, so it's ok to potentially skew statistics.
addStats(rs)
@@ -1933,9 +1937,7 @@ func (s *shards) sendV2SamplesWithBackoff(ctx context.Context, samples []writev2
}
onRetry := func() {
- s.qm.metrics.retriedSamplesTotal.Add(float64(sampleCount))
- s.qm.metrics.retriedExemplarsTotal.Add(float64(exemplarCount))
- s.qm.metrics.retriedHistogramsTotal.Add(float64(histogramCount))
+ metricsUpdater.recordRetry(sc)
}
err = s.qm.sendWriteRequestWithBackoff(ctx, attemptStore, onRetry)
@@ -2101,48 +2103,27 @@ func setAtomicToNewer(value *atomic.Int64, newValue int64) (previous int64, upda
}
}
-func buildTimeSeries(timeSeries []prompb.TimeSeries, filter func(prompb.TimeSeries) bool) (int64, int64, []prompb.TimeSeries, int, int, int) {
- var highest int64
- var lowest int64
- var droppedSamples, droppedExemplars, droppedHistograms int
-
+func buildTimeSeries(timeSeries []prompb.TimeSeries, filter func(prompb.TimeSeries) bool) ([]prompb.TimeSeries, *timeSeriesStats) {
+ stats := newTimeSeriesStats()
keepIdx := 0
- lowest = math.MaxInt64
+
for i, ts := range timeSeries {
if filter != nil && filter(ts) {
- if len(ts.Samples) > 0 {
- droppedSamples++
- }
- if len(ts.Exemplars) > 0 {
- droppedExemplars++
- }
- if len(ts.Histograms) > 0 {
- droppedHistograms++
- }
+ stats.recordDropped(len(ts.Samples) > 0, len(ts.Exemplars) > 0, len(ts.Histograms) > 0)
continue
}
// At the moment we only ever append a TimeSeries with a single sample or exemplar in it.
- if len(ts.Samples) > 0 && ts.Samples[0].Timestamp > highest {
- highest = ts.Samples[0].Timestamp
+ if len(ts.Samples) > 0 {
+ stats.updateTimestamp(ts.Samples[0].Timestamp)
}
- if len(ts.Exemplars) > 0 && ts.Exemplars[0].Timestamp > highest {
- highest = ts.Exemplars[0].Timestamp
+ if len(ts.Exemplars) > 0 {
+ stats.updateTimestamp(ts.Exemplars[0].Timestamp)
}
- if len(ts.Histograms) > 0 && ts.Histograms[0].Timestamp > highest {
- highest = ts.Histograms[0].Timestamp
+ if len(ts.Histograms) > 0 {
+ stats.updateTimestamp(ts.Histograms[0].Timestamp)
}
- // Get lowest timestamp
- if len(ts.Samples) > 0 && ts.Samples[0].Timestamp < lowest {
- lowest = ts.Samples[0].Timestamp
- }
- if len(ts.Exemplars) > 0 && ts.Exemplars[0].Timestamp < lowest {
- lowest = ts.Exemplars[0].Timestamp
- }
- if len(ts.Histograms) > 0 && ts.Histograms[0].Timestamp < lowest {
- lowest = ts.Histograms[0].Timestamp
- }
if i != keepIdx {
// We have to swap the kept timeseries with the one which should be dropped.
// Copying any elements within timeSeries could cause data corruptions when reusing the slice in a next batch (shards.populateTimeSeries).
@@ -2151,16 +2132,14 @@ func buildTimeSeries(timeSeries []prompb.TimeSeries, filter func(prompb.TimeSeri
keepIdx++
}
- timeSeries = timeSeries[:keepIdx]
- return highest, lowest, timeSeries, droppedSamples, droppedExemplars, droppedHistograms
+ return timeSeries[:keepIdx], stats
}
func buildWriteRequest(logger *slog.Logger, timeSeries []prompb.TimeSeries, metadata []prompb.MetricMetadata, pBuf *proto.Buffer, filter func(prompb.TimeSeries) bool, buf compression.EncodeBuffer, compr compression.Type) (_ []byte, highest, lowest int64, _ error) {
- highest, lowest, timeSeries,
- droppedSamples, droppedExemplars, droppedHistograms := buildTimeSeries(timeSeries, filter)
+ timeSeries, stats := buildTimeSeries(timeSeries, filter)
- if droppedSamples > 0 || droppedExemplars > 0 || droppedHistograms > 0 {
- logger.Debug("dropped data due to their age", "droppedSamples", droppedSamples, "droppedExemplars", droppedExemplars, "droppedHistograms", droppedHistograms)
+ if stats.droppedSamples > 0 || stats.droppedExemplars > 0 || stats.droppedHistograms > 0 {
+ logger.Debug("dropped data due to their age", "droppedSamples", stats.droppedSamples, "droppedExemplars", stats.droppedExemplars, "droppedHistograms", stats.droppedHistograms)
}
req := &prompb.WriteRequest{
@@ -2174,21 +2153,21 @@ func buildWriteRequest(logger *slog.Logger, timeSeries []prompb.TimeSeries, meta
pBuf.Reset()
}
if err := pBuf.Marshal(req); err != nil {
- return nil, highest, lowest, err
+ return nil, stats.highest, stats.lowest, err
}
compressed, err := compression.Encode(compr, pBuf.Bytes(), buf)
if err != nil {
- return nil, highest, lowest, err
+ return nil, stats.highest, stats.lowest, err
}
- return compressed, highest, lowest, nil
+ return compressed, stats.highest, stats.lowest, nil
}
func buildV2WriteRequest(logger *slog.Logger, samples []writev2.TimeSeries, labels []string, pBuf *[]byte, filter func(writev2.TimeSeries) bool, buf compression.EncodeBuffer, compr compression.Type) (compressed []byte, highest, lowest int64, _ error) {
- highest, lowest, timeSeries, droppedSamples, droppedExemplars, droppedHistograms := buildV2TimeSeries(samples, filter)
+ timeSeries, stats := buildV2TimeSeries(samples, filter)
- if droppedSamples > 0 || droppedExemplars > 0 || droppedHistograms > 0 {
- logger.Debug("dropped data due to their age", "droppedSamples", droppedSamples, "droppedExemplars", droppedExemplars, "droppedHistograms", droppedHistograms)
+ if stats.droppedSamples > 0 || stats.droppedExemplars > 0 || stats.droppedHistograms > 0 {
+ logger.Debug("dropped data due to their age", "droppedSamples", stats.droppedSamples, "droppedExemplars", stats.droppedExemplars, "droppedHistograms", stats.droppedHistograms)
}
req := &writev2.Request{
@@ -2202,59 +2181,38 @@ func buildV2WriteRequest(logger *slog.Logger, samples []writev2.TimeSeries, labe
data, err := req.OptimizedMarshal(*pBuf)
if err != nil {
- return nil, highest, lowest, err
+ return nil, stats.highest, stats.lowest, err
}
*pBuf = data
compressed, err = compression.Encode(compr, *pBuf, buf)
if err != nil {
- return nil, highest, lowest, err
+ return nil, stats.highest, stats.lowest, err
}
- return compressed, highest, lowest, nil
+ return compressed, stats.highest, stats.lowest, nil
}
-func buildV2TimeSeries(timeSeries []writev2.TimeSeries, filter func(writev2.TimeSeries) bool) (int64, int64, []writev2.TimeSeries, int, int, int) {
- var highest int64
- var lowest int64
- var droppedSamples, droppedExemplars, droppedHistograms int
-
+func buildV2TimeSeries(timeSeries []writev2.TimeSeries, filter func(writev2.TimeSeries) bool) ([]writev2.TimeSeries, *timeSeriesStats) {
+ stats := newTimeSeriesStats()
keepIdx := 0
- lowest = math.MaxInt64
+
for i, ts := range timeSeries {
if filter != nil && filter(ts) {
- if len(ts.Samples) > 0 {
- droppedSamples++
- }
- if len(ts.Exemplars) > 0 {
- droppedExemplars++
- }
- if len(ts.Histograms) > 0 {
- droppedHistograms++
- }
+ stats.recordDropped(len(ts.Samples) > 0, len(ts.Exemplars) > 0, len(ts.Histograms) > 0)
continue
}
// At the moment we only ever append a TimeSeries with a single sample or exemplar in it.
- if len(ts.Samples) > 0 && ts.Samples[0].Timestamp > highest {
- highest = ts.Samples[0].Timestamp
+ if len(ts.Samples) > 0 {
+ stats.updateTimestamp(ts.Samples[0].Timestamp)
}
- if len(ts.Exemplars) > 0 && ts.Exemplars[0].Timestamp > highest {
- highest = ts.Exemplars[0].Timestamp
+ if len(ts.Exemplars) > 0 {
+ stats.updateTimestamp(ts.Exemplars[0].Timestamp)
}
- if len(ts.Histograms) > 0 && ts.Histograms[0].Timestamp > highest {
- highest = ts.Histograms[0].Timestamp
+ if len(ts.Histograms) > 0 {
+ stats.updateTimestamp(ts.Histograms[0].Timestamp)
}
- // Get the lowest timestamp.
- if len(ts.Samples) > 0 && ts.Samples[0].Timestamp < lowest {
- lowest = ts.Samples[0].Timestamp
- }
- if len(ts.Exemplars) > 0 && ts.Exemplars[0].Timestamp < lowest {
- lowest = ts.Exemplars[0].Timestamp
- }
- if len(ts.Histograms) > 0 && ts.Histograms[0].Timestamp < lowest {
- lowest = ts.Histograms[0].Timestamp
- }
if i != keepIdx {
// We have to swap the kept timeseries with the one which should be dropped.
// Copying any elements within timeSeries could cause data corruptions when reusing the slice in a next batch (shards.populateTimeSeries).
@@ -2263,6 +2221,99 @@ func buildV2TimeSeries(timeSeries []writev2.TimeSeries, filter func(writev2.Time
keepIdx++
}
- timeSeries = timeSeries[:keepIdx]
- return highest, lowest, timeSeries, droppedSamples, droppedExemplars, droppedHistograms
+ return timeSeries[:keepIdx], stats
+}
+
+// timeSeriesStats tracks statistics during time series processing.
+type timeSeriesStats struct {
+ highest int64
+ lowest int64
+ droppedSamples int
+ droppedExemplars int
+ droppedHistograms int
+}
+
+// newTimeSeriesStats creates a new timeSeriesStats with lowest initialized to MaxInt64.
+func newTimeSeriesStats() *timeSeriesStats {
+ return &timeSeriesStats{
+ lowest: math.MaxInt64,
+ }
+}
+
+// updateTimestamp updates highest and lowest timestamps if the given timestamp is valid.
+func (s *timeSeriesStats) updateTimestamp(timestamp int64) {
+ if timestamp > 0 {
+ if timestamp > s.highest {
+ s.highest = timestamp
+ }
+ if timestamp < s.lowest {
+ s.lowest = timestamp
+ }
+ }
+}
+
+// recordDropped increments the dropped counters based on what data exists.
+func (s *timeSeriesStats) recordDropped(hasSamples, hasExemplars, hasHistograms bool) {
+ if hasSamples {
+ s.droppedSamples++
+ }
+ if hasExemplars {
+ s.droppedExemplars++
+ }
+ if hasHistograms {
+ s.droppedHistograms++
+ }
+}
+
+// sendBatchContext encapsulates the common parameters for sending batches.
+// This reduces the number of function arguments (addresses "too many arguments" issue).
+type sendBatchContext struct {
+ ctx context.Context
+ sampleCount int
+ exemplarCount int
+ histogramCount int
+ metadataCount int
+ reqSize int
+}
+
+// batchMetricsUpdater encapsulates metrics update operations for batch sending.
+type batchMetricsUpdater struct {
+ metrics *queueManagerMetrics
+ storeClient WriteClient
+ sentDuration prometheus.Observer
+}
+
+// recordBatchAttempt records metrics for a batch send attempt.
+func (b *batchMetricsUpdater) recordBatchAttempt(sc sendBatchContext, begin time.Time) {
+ b.metrics.samplesTotal.Add(float64(sc.sampleCount))
+ b.metrics.exemplarsTotal.Add(float64(sc.exemplarCount))
+ b.metrics.histogramsTotal.Add(float64(sc.histogramCount))
+ b.metrics.metadataTotal.Add(float64(sc.metadataCount))
+ b.sentDuration.Observe(time.Since(begin).Seconds())
+}
+
+// recordRetry records retry metrics for a batch.
+func (b *batchMetricsUpdater) recordRetry(sc sendBatchContext) {
+ b.metrics.retriedSamplesTotal.Add(float64(sc.sampleCount))
+ b.metrics.retriedExemplarsTotal.Add(float64(sc.exemplarCount))
+ b.metrics.retriedHistogramsTotal.Add(float64(sc.histogramCount))
+}
+
+// createBatchSpan creates and configures an OpenTelemetry span for batch sending.
+func createBatchSpan(ctx context.Context, sc sendBatchContext, remoteName, remoteURL string, try int) (context.Context, trace.Span) {
+ ctx, span := otel.Tracer("").Start(ctx, "Remote Send Batch")
+ span.SetAttributes(
+ attribute.Int("request_size", sc.reqSize),
+ attribute.Int("samples", sc.sampleCount),
+ attribute.Int("try", try),
+ attribute.String("remote_name", remoteName),
+ attribute.String("remote_url", remoteURL),
+ )
+ if sc.exemplarCount > 0 {
+ span.SetAttributes(attribute.Int("exemplars", sc.exemplarCount))
+ }
+ if sc.histogramCount > 0 {
+ span.SetAttributes(attribute.Int("histograms", sc.histogramCount))
+ }
+ return ctx, span
}
diff --git a/vendor/github.com/prometheus/prometheus/storage/remote/write.go b/vendor/github.com/prometheus/prometheus/storage/remote/write.go
index 6bc02bd6fed..1a036c17956 100644
--- a/vendor/github.com/prometheus/prometheus/storage/remote/write.go
+++ b/vendor/github.com/prometheus/prometheus/storage/remote/write.go
@@ -318,22 +318,22 @@ func (t *timestampTracker) AppendHistogram(_ storage.SeriesRef, _ labels.Labels,
return 0, nil
}
-func (t *timestampTracker) AppendCTZeroSample(_ storage.SeriesRef, _ labels.Labels, _, ct int64) (storage.SeriesRef, error) {
+func (t *timestampTracker) AppendSTZeroSample(_ storage.SeriesRef, _ labels.Labels, _, st int64) (storage.SeriesRef, error) {
t.samples++
- if ct > t.highestTimestamp {
- // Theoretically, we should never see a CT zero sample with a timestamp higher than the highest timestamp we've seen so far.
+ if st > t.highestTimestamp {
+ // Theoretically, we should never see a ST zero sample with a timestamp higher than the highest timestamp we've seen so far.
// However, we're not going to enforce that here, as it is not the responsibility of the tracker to enforce this.
- t.highestTimestamp = ct
+ t.highestTimestamp = st
}
return 0, nil
}
-func (t *timestampTracker) AppendHistogramCTZeroSample(_ storage.SeriesRef, _ labels.Labels, _, ct int64, _ *histogram.Histogram, _ *histogram.FloatHistogram) (storage.SeriesRef, error) {
+func (t *timestampTracker) AppendHistogramSTZeroSample(_ storage.SeriesRef, _ labels.Labels, _, st int64, _ *histogram.Histogram, _ *histogram.FloatHistogram) (storage.SeriesRef, error) {
t.histograms++
- if ct > t.highestTimestamp {
- // Theoretically, we should never see a CT zero sample with a timestamp higher than the highest timestamp we've seen so far.
+ if st > t.highestTimestamp {
+ // Theoretically, we should never see a ST zero sample with a timestamp higher than the highest timestamp we've seen so far.
// However, we're not going to enforce that here, as it is not the responsibility of the tracker to enforce this.
- t.highestTimestamp = ct
+ t.highestTimestamp = st
}
return 0, nil
}
diff --git a/vendor/github.com/prometheus/prometheus/storage/remote/write_handler.go b/vendor/github.com/prometheus/prometheus/storage/remote/write_handler.go
index 2bc65e8286a..4291b0505a3 100644
--- a/vendor/github.com/prometheus/prometheus/storage/remote/write_handler.go
+++ b/vendor/github.com/prometheus/prometheus/storage/remote/write_handler.go
@@ -53,7 +53,7 @@ type writeHandler struct {
samplesWithInvalidLabelsTotal prometheus.Counter
samplesAppendedWithoutMetadata prometheus.Counter
- ingestCTZeroSample bool
+ ingestSTZeroSample bool
enableTypeAndUnitLabels bool
appendMetadata bool
}
@@ -65,7 +65,7 @@ const maxAheadTime = 10 * time.Minute
//
// NOTE(bwplotka): When accepting v2 proto and spec, partial writes are possible
// as per https://prometheus.io/docs/specs/remote_write_spec_2_0/#partial-write.
-func NewWriteHandler(logger *slog.Logger, reg prometheus.Registerer, appendable storage.Appendable, acceptedMsgs remoteapi.MessageTypes, ingestCTZeroSample, enableTypeAndUnitLabels, appendMetadata bool) http.Handler {
+func NewWriteHandler(logger *slog.Logger, reg prometheus.Registerer, appendable storage.Appendable, acceptedMsgs remoteapi.MessageTypes, ingestSTZeroSample, enableTypeAndUnitLabels, appendMetadata bool) http.Handler {
h := &writeHandler{
logger: logger,
appendable: appendable,
@@ -82,7 +82,7 @@ func NewWriteHandler(logger *slog.Logger, reg prometheus.Registerer, appendable
Help: "The total number of received remote write samples (and histogram samples) which were ingested without corresponding metadata.",
}),
- ingestCTZeroSample: ingestCTZeroSample,
+ ingestSTZeroSample: ingestSTZeroSample,
enableTypeAndUnitLabels: enableTypeAndUnitLabels,
appendMetadata: appendMetadata,
}
@@ -329,7 +329,11 @@ func (h *writeHandler) appendV2(app storage.Appender, req *writev2.Request, rs *
if h.enableTypeAndUnitLabels && (m.Type != model.MetricTypeUnknown || m.Unit != "") {
slb := labels.NewScratchBuilder(ls.Len() + 2) // +2 for __type__ and __unit__
ls.Range(func(l labels.Label) {
- slb.Add(l.Name, l.Value)
+ // Skip __type__ and __unit__ labels if they exist in the incoming labels.
+ // They will be added from metadata to avoid duplicates.
+ if l.Name != model.MetricTypeLabel && l.Name != model.MetricUnitLabel {
+ slb.Add(l.Name, l.Value)
+ }
})
schema.Metadata{Type: m.Type, Unit: m.Unit}.AddToLabels(&slb)
slb.Sort()
@@ -358,13 +362,13 @@ func (h *writeHandler) appendV2(app storage.Appender, req *writev2.Request, rs *
allSamplesSoFar := rs.AllSamples()
var ref storage.SeriesRef
for _, s := range ts.Samples {
- if h.ingestCTZeroSample && s.StartTimestamp != 0 && s.Timestamp != 0 {
- ref, err = app.AppendCTZeroSample(ref, ls, s.Timestamp, s.StartTimestamp)
+ if h.ingestSTZeroSample && s.StartTimestamp != 0 && s.Timestamp != 0 {
+ ref, err = app.AppendSTZeroSample(ref, ls, s.Timestamp, s.StartTimestamp)
// We treat OOO errors specially as it's a common scenario given:
// * We can't tell if ST was already ingested in a previous request.
// * We don't check if ST changed for stream of samples (we typically have one though),
// as it's checked in the AppendSTZeroSample reliably.
- if err != nil && !errors.Is(err, storage.ErrOutOfOrderCT) {
+ if err != nil && !errors.Is(err, storage.ErrOutOfOrderST) {
h.logger.Debug("Error when appending ST from remote write request", "err", err, "series", ls.String(), "start_timestamp", s.StartTimestamp, "timestamp", s.Timestamp)
}
}
@@ -389,13 +393,13 @@ func (h *writeHandler) appendV2(app storage.Appender, req *writev2.Request, rs *
// Native Histograms.
for _, hp := range ts.Histograms {
- if h.ingestCTZeroSample && hp.StartTimestamp != 0 && hp.Timestamp != 0 {
+ if h.ingestSTZeroSample && hp.StartTimestamp != 0 && hp.Timestamp != 0 {
ref, err = h.handleHistogramZeroSample(app, ref, ls, hp, hp.StartTimestamp)
// We treat OOO errors specially as it's a common scenario given:
// * We can't tell if ST was already ingested in a previous request.
// * We don't check if ST changed for stream of samples (we typically have one though),
// as it's checked in the ingestSTZeroSample reliably.
- if err != nil && !errors.Is(err, storage.ErrOutOfOrderCT) {
+ if err != nil && !errors.Is(err, storage.ErrOutOfOrderST) {
h.logger.Debug("Error when appending ST from remote write request", "err", err, "series", ls.String(), "start_timestamp", hp.StartTimestamp, "timestamp", hp.Timestamp)
}
}
@@ -480,9 +484,9 @@ func (h *writeHandler) appendV2(app storage.Appender, req *writev2.Request, rs *
func (*writeHandler) handleHistogramZeroSample(app storage.Appender, ref storage.SeriesRef, l labels.Labels, hist writev2.Histogram, st int64) (storage.SeriesRef, error) {
var err error
if hist.IsFloatHistogram() {
- ref, err = app.AppendHistogramCTZeroSample(ref, l, hist.Timestamp, st, nil, hist.ToFloatHistogram())
+ ref, err = app.AppendHistogramSTZeroSample(ref, l, hist.Timestamp, st, nil, hist.ToFloatHistogram())
} else {
- ref, err = app.AppendHistogramCTZeroSample(ref, l, hist.Timestamp, st, hist.ToIntHistogram(), nil)
+ ref, err = app.AppendHistogramSTZeroSample(ref, l, hist.Timestamp, st, hist.ToIntHistogram(), nil)
}
return ref, err
}
@@ -499,9 +503,11 @@ type OTLPOptions struct {
LookbackDelta time.Duration
// Add type and unit labels to the metrics.
EnableTypeAndUnitLabels bool
- // IngestCTZeroSample enables writing zero samples based on the start time
+ // IngestSTZeroSample enables writing zero samples based on the start time
// of metrics.
- IngestCTZeroSample bool
+ IngestSTZeroSample bool
+ // AppendMetadata enables writing metadata to WAL when metadata-wal-records feature is enabled.
+ AppendMetadata bool
}
// NewOTLPWriteHandler creates a http.Handler that accepts OTLP write requests and
@@ -518,8 +524,9 @@ func NewOTLPWriteHandler(logger *slog.Logger, reg prometheus.Registerer, appenda
config: configFunc,
allowDeltaTemporality: opts.NativeDelta,
lookbackDelta: opts.LookbackDelta,
- ingestCTZeroSample: opts.IngestCTZeroSample,
+ ingestSTZeroSample: opts.IngestSTZeroSample,
enableTypeAndUnitLabels: opts.EnableTypeAndUnitLabels,
+ appendMetadata: opts.AppendMetadata,
// Register metrics.
metrics: otlptranslator.NewCombinedAppenderMetrics(reg),
}
@@ -560,8 +567,9 @@ type rwExporter struct {
config func() config.Config
allowDeltaTemporality bool
lookbackDelta time.Duration
- ingestCTZeroSample bool
+ ingestSTZeroSample bool
enableTypeAndUnitLabels bool
+ appendMetadata bool
// Metrics.
metrics otlptranslator.CombinedAppenderMetrics
@@ -573,7 +581,7 @@ func (rw *rwExporter) ConsumeMetrics(ctx context.Context, md pmetric.Metrics) er
Appender: rw.appendable.Appender(ctx),
maxTime: timestamp.FromTime(time.Now().Add(maxAheadTime)),
}
- combinedAppender := otlptranslator.NewCombinedAppender(app, rw.logger, rw.ingestCTZeroSample, rw.metrics)
+ combinedAppender := otlptranslator.NewCombinedAppender(app, rw.logger, rw.ingestSTZeroSample, rw.appendMetadata, rw.metrics)
converter := otlptranslator.NewPrometheusConverter(combinedAppender)
annots, err := converter.FromMetrics(ctx, md, otlptranslator.Settings{
AddMetricSuffixes: otlpCfg.TranslationStrategy.ShouldAddSuffixes(),
@@ -693,19 +701,23 @@ func (app *remoteWriteAppender) Append(ref storage.SeriesRef, lset labels.Labels
}
func (app *remoteWriteAppender) AppendHistogram(ref storage.SeriesRef, l labels.Labels, t int64, h *histogram.Histogram, fh *histogram.FloatHistogram) (storage.SeriesRef, error) {
+ var err 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 err = h.ReduceResolution(histogram.ExponentialSchemaMax); err != nil {
+ return 0, err
+ }
}
if fh != nil && histogram.IsExponentialSchemaReserved(fh.Schema) && fh.Schema > histogram.ExponentialSchemaMax {
- fh = fh.ReduceResolution(histogram.ExponentialSchemaMax)
+ if err = fh.ReduceResolution(histogram.ExponentialSchemaMax); err != nil {
+ return 0, err
+ }
}
- ref, err := app.Appender.AppendHistogram(ref, l, t, h, fh)
- if err != nil {
+ if ref, err = app.Appender.AppendHistogram(ref, l, t, h, fh); err != nil {
return 0, err
}
return ref, nil
diff --git a/vendor/github.com/prometheus/prometheus/template/template.go b/vendor/github.com/prometheus/prometheus/template/template.go
index ea7e93b18c1..572e8450d35 100644
--- a/vendor/github.com/prometheus/prometheus/template/template.go
+++ b/vendor/github.com/prometheus/prometheus/template/template.go
@@ -36,6 +36,7 @@ import (
"golang.org/x/text/language"
"github.com/prometheus/prometheus/promql"
+ "github.com/prometheus/prometheus/util/features"
"github.com/prometheus/prometheus/util/strutil"
)
@@ -413,3 +414,29 @@ func floatToTime(v float64) (*time.Time, error) {
t := model.TimeFromUnixNano(int64(timestamp)).Time().UTC()
return &t, nil
}
+
+// templateFunctions returns a representative funcMap with all available template functions.
+// This is used to discover which functions are available for feature registration.
+func templateFunctions() text_template.FuncMap {
+ // Create a dummy expander to get the function map.
+ expander := NewTemplateExpander(
+ context.Background(),
+ "",
+ "",
+ nil,
+ 0,
+ nil,
+ &url.URL{},
+ nil,
+ )
+ return expander.funcMap
+}
+
+// RegisterFeatures registers all template functions with the feature registry.
+func RegisterFeatures(r features.Collector) {
+ // Get all function names from the template function map.
+ funcMap := templateFunctions()
+ for name := range funcMap {
+ r.Enable(features.TemplatingFunctions, name)
+ }
+}
diff --git a/vendor/github.com/prometheus/prometheus/tsdb/block.go b/vendor/github.com/prometheus/prometheus/tsdb/block.go
index 44c6ef50531..dcbb172e723 100644
--- a/vendor/github.com/prometheus/prometheus/tsdb/block.go
+++ b/vendor/github.com/prometheus/prometheus/tsdb/block.go
@@ -102,11 +102,6 @@ type IndexReader interface {
// LabelNames returns all the unique label names present in the index in sorted order.
LabelNames(ctx context.Context, matchers ...*labels.Matcher) ([]string, error)
- // LabelValueFor returns label value for the given label name in the series referred to by ID.
- // If the series couldn't be found or the series doesn't have the requested label a
- // storage.ErrNotFound is returned as error.
- LabelValueFor(ctx context.Context, id storage.SeriesRef, label string) (string, error)
-
// LabelNamesFor returns all the label names for the series referred to by the postings.
// The names returned are sorted.
LabelNamesFor(ctx context.Context, postings index.Postings) ([]string, error)
@@ -551,11 +546,6 @@ func (r blockIndexReader) Close() error {
return nil
}
-// LabelValueFor returns label value for the given label name in the series referred to by ID.
-func (r blockIndexReader) LabelValueFor(ctx context.Context, id storage.SeriesRef, label string) (string, error) {
- return r.ir.LabelValueFor(ctx, id, label)
-}
-
// LabelNamesFor returns all the label names for the series referred to by the postings.
// The names returned are sorted.
func (r blockIndexReader) LabelNamesFor(ctx context.Context, postings index.Postings) ([]string, error) {
diff --git a/vendor/github.com/prometheus/prometheus/tsdb/blockwriter.go b/vendor/github.com/prometheus/prometheus/tsdb/blockwriter.go
index 14137f12cca..e0388122241 100644
--- a/vendor/github.com/prometheus/prometheus/tsdb/blockwriter.go
+++ b/vendor/github.com/prometheus/prometheus/tsdb/blockwriter.go
@@ -86,6 +86,12 @@ func (w *BlockWriter) Appender(ctx context.Context) storage.Appender {
return w.head.Appender(ctx)
}
+// AppenderV2 returns a new appender on the database.
+// AppenderV2 can't be called concurrently. However, the returned AppenderV2 can safely be used concurrently.
+func (w *BlockWriter) AppenderV2(ctx context.Context) storage.AppenderV2 {
+ return w.head.AppenderV2(ctx)
+}
+
// Flush implements the Writer interface. This is where actual block writing
// happens. After flush completes, no writes can be done.
func (w *BlockWriter) Flush(ctx context.Context) (ulid.ULID, error) {
diff --git a/vendor/github.com/prometheus/prometheus/tsdb/chunkenc/float_histogram.go b/vendor/github.com/prometheus/prometheus/tsdb/chunkenc/float_histogram.go
index 8002dd0d4e9..d960e835f25 100644
--- a/vendor/github.com/prometheus/prometheus/tsdb/chunkenc/float_histogram.go
+++ b/vendor/github.com/prometheus/prometheus/tsdb/chunkenc/float_histogram.go
@@ -884,7 +884,14 @@ func (it *floatHistogramIterator) AtFloatHistogram(fh *histogram.FloatHistogram)
// chunk is from a newer Prometheus version that supports higher
// resolution.
fh = fh.Copy()
- fh.ReduceResolution(histogram.ExponentialSchemaMax)
+ if err := fh.ReduceResolution(histogram.ExponentialSchemaMax); err != nil {
+ // With the checks above, this can only happen
+ // with invalid data in a chunk. As this is a
+ // rare edge case of a rare edge case, we'd
+ // rather not create all the plumbing to handle
+ // this error gracefully.
+ panic(err)
+ }
}
return it.t, fh
}
@@ -915,7 +922,13 @@ func (it *floatHistogramIterator) AtFloatHistogram(fh *histogram.FloatHistogram)
// This is a very slow path, but it should only happen if the
// chunk is from a newer Prometheus version that supports higher
// resolution.
- fh.ReduceResolution(histogram.ExponentialSchemaMax)
+ if err := fh.ReduceResolution(histogram.ExponentialSchemaMax); err != nil {
+ // With the checks above, this can only happen with
+ // invalid data in a chunk. As this is a rare edge case
+ // of a rare edge case, we'd rather not create all the
+ // plumbing to handle this error gracefully.
+ panic(err)
+ }
}
return it.t, fh
diff --git a/vendor/github.com/prometheus/prometheus/tsdb/chunkenc/histogram.go b/vendor/github.com/prometheus/prometheus/tsdb/chunkenc/histogram.go
index cc1d771235d..be1c31ae76e 100644
--- a/vendor/github.com/prometheus/prometheus/tsdb/chunkenc/histogram.go
+++ b/vendor/github.com/prometheus/prometheus/tsdb/chunkenc/histogram.go
@@ -939,7 +939,14 @@ func (it *histogramIterator) AtHistogram(h *histogram.Histogram) (int64, *histog
// chunk is from a newer Prometheus version that supports higher
// resolution.
h = h.Copy()
- h.ReduceResolution(histogram.ExponentialSchemaMax)
+ if err := h.ReduceResolution(histogram.ExponentialSchemaMax); err != nil {
+ // With the checks above, this can only happen
+ // with invalid data in a chunk. As this is a
+ // rare edge case of a rare edge case, we'd
+ // rather not create all the plumbing to handle
+ // this error gracefully.
+ panic(err)
+ }
}
return it.t, h
}
@@ -970,7 +977,13 @@ func (it *histogramIterator) AtHistogram(h *histogram.Histogram) (int64, *histog
// This is a very slow path, but it should only happen if the
// chunk is from a newer Prometheus version that supports higher
// resolution.
- h.ReduceResolution(histogram.ExponentialSchemaMax)
+ if err := h.ReduceResolution(histogram.ExponentialSchemaMax); err != nil {
+ // With the checks above, this can only happen with
+ // invalid data in a chunk. As this is a rare edge case
+ // of a rare edge case, we'd rather not create all the
+ // plumbing to handle this error gracefully.
+ panic(err)
+ }
}
return it.t, h
@@ -1000,7 +1013,14 @@ func (it *histogramIterator) AtFloatHistogram(fh *histogram.FloatHistogram) (int
// chunk is from a newer Prometheus version that supports higher
// resolution.
fh = fh.Copy()
- fh.ReduceResolution(histogram.ExponentialSchemaMax)
+ if err := fh.ReduceResolution(histogram.ExponentialSchemaMax); err != nil {
+ // With the checks above, this can only happen
+ // with invalid data in a chunk. As this is a
+ // rare edge case of a rare edge case, we'd
+ // rather not create all the plumbing to handle
+ // this error gracefully.
+ panic(err)
+ }
}
return it.t, fh
}
@@ -1039,7 +1059,13 @@ func (it *histogramIterator) AtFloatHistogram(fh *histogram.FloatHistogram) (int
// This is a very slow path, but it should only happen if the
// chunk is from a newer Prometheus version that supports higher
// resolution.
- fh.ReduceResolution(histogram.ExponentialSchemaMax)
+ if err := fh.ReduceResolution(histogram.ExponentialSchemaMax); err != nil {
+ // With the checks above, this can only happen with
+ // invalid data in a chunk. As this is a rare edge case
+ // of a rare edge case, we'd rather not create all the
+ // plumbing to handle this error gracefully.
+ panic(err)
+ }
}
return it.t, fh
diff --git a/vendor/github.com/prometheus/prometheus/tsdb/chunks/head_chunks.go b/vendor/github.com/prometheus/prometheus/tsdb/chunks/head_chunks.go
index 41fce69c723..5e143b8b322 100644
--- a/vendor/github.com/prometheus/prometheus/tsdb/chunks/head_chunks.go
+++ b/vendor/github.com/prometheus/prometheus/tsdb/chunks/head_chunks.go
@@ -20,6 +20,7 @@ import (
"fmt"
"hash"
"io"
+ "math"
"os"
"path/filepath"
"slices"
@@ -768,8 +769,25 @@ func (cdm *ChunkDiskMapper) Chunk(ref ChunkDiskMapperRef) (chunkenc.Chunk, error
}
}
+ if chkDataLen > uint64(math.MaxInt) {
+ return nil, &CorruptionErr{
+ Dir: cdm.dir.Name(),
+ FileIndex: sgmIndex,
+ Err: fmt.Errorf("chunk length %d exceeds supported size", chkDataLen),
+ }
+ }
+
+ chkDataLenInt := int(chkDataLen)
+ if chkDataLenStart > math.MaxInt-n-chkDataLenInt {
+ return nil, &CorruptionErr{
+ Dir: cdm.dir.Name(),
+ FileIndex: sgmIndex,
+ Err: fmt.Errorf("chunk data end overflows supported size (start=%d, len=%d, n=%d)", chkDataLenStart, chkDataLenInt, n),
+ }
+ }
+
// Verify the chunk data end.
- chkDataEnd := chkDataLenStart + n + int(chkDataLen)
+ chkDataEnd := chkDataLenStart + n + chkDataLenInt
if chkDataEnd > mmapFile.byteSlice.Len() {
return nil, &CorruptionErr{
Dir: cdm.dir.Name(),
diff --git a/vendor/github.com/prometheus/prometheus/tsdb/compact.go b/vendor/github.com/prometheus/prometheus/tsdb/compact.go
index 49e88d63205..7ad6f8bb240 100644
--- a/vendor/github.com/prometheus/prometheus/tsdb/compact.go
+++ b/vendor/github.com/prometheus/prometheus/tsdb/compact.go
@@ -87,6 +87,7 @@ type LeveledCompactor struct {
maxBlockChunkSegmentSize int64
useUncachedIO bool
mergeFunc storage.VerticalChunkSeriesMergeFunc
+ blockExcludeFunc BlockExcludeFilterFunc
postingsEncoder index.PostingsEncoder
postingsDecoderFactory PostingsDecoderFactory
enableOverlappingCompaction bool
@@ -160,16 +161,24 @@ type LeveledCompactorOptions struct {
// PE specifies the postings encoder. It is called when compactor is writing out the postings for a label name/value pair during compaction.
// If it is nil then the default encoder is used. At the moment that is the "raw" encoder. See index.EncodePostingsRaw for more.
PE index.PostingsEncoder
+
// PD specifies the postings decoder factory to return different postings decoder based on BlockMeta. It is called when opening a block or opening the index file.
// If it is nil then a default decoder is used, compatible with Prometheus v2.
PD PostingsDecoderFactory
+
// MaxBlockChunkSegmentSize is the max block chunk segment size. If it is 0 then the default chunks.DefaultChunkSegmentSize is used.
MaxBlockChunkSegmentSize int64
+
// MergeFunc is used for merging series together in vertical compaction. By default storage.NewCompactingChunkSeriesMerger(storage.ChainedSeriesMerge) is used.
MergeFunc storage.VerticalChunkSeriesMergeFunc
+
+ // BlockExcludeFilter is used to decide which blocks are exluded from compactions.
+ BlockExcludeFilter BlockExcludeFilterFunc
+
// EnableOverlappingCompaction enables compaction of overlapping blocks. In Prometheus it is always enabled.
// It is useful for downstream projects like Mimir, Cortex, Thanos where they have a separate component that does compaction.
EnableOverlappingCompaction bool
+
// Metrics is set of metrics for Compactor. By default, NewCompactorMetrics would be called to initialize metrics unless it is provided.
Metrics *CompactorMetrics
// UseUncachedIO allows bypassing the page cache when appropriate.
@@ -178,7 +187,9 @@ type LeveledCompactorOptions struct {
type PostingsDecoderFactory func(meta *BlockMeta) index.PostingsDecoder
-func DefaultPostingsDecoderFactory(*BlockMeta) index.PostingsDecoder {
+type BlockExcludeFilterFunc func(meta *BlockMeta) bool
+
+func DefaultPostingsDecoderFactory(_ *BlockMeta) index.PostingsDecoder {
return index.DecodePostingsRaw
}
@@ -226,6 +237,7 @@ func NewLeveledCompactorWithOptions(ctx context.Context, r prometheus.Registerer
postingsEncoder: pe,
postingsDecoderFactory: opts.PD,
enableOverlappingCompaction: opts.EnableOverlappingCompaction,
+ blockExcludeFunc: opts.BlockExcludeFilter,
}, nil
}
@@ -250,12 +262,19 @@ func (c *LeveledCompactor) Plan(dir string) ([]string, error) {
if err != nil {
return nil, err
}
+ if c.blockExcludeFunc != nil && c.blockExcludeFunc(meta) {
+ break
+ }
dms = append(dms, dirMeta{dir, meta})
}
return c.plan(dms)
}
func (c *LeveledCompactor) plan(dms []dirMeta) ([]string, error) {
+ if len(dms) == 0 {
+ return nil, nil
+ }
+
slices.SortFunc(dms, func(a, b dirMeta) int {
switch {
case a.meta.MinTime < b.meta.MinTime:
diff --git a/vendor/github.com/prometheus/prometheus/tsdb/db.go b/vendor/github.com/prometheus/prometheus/tsdb/db.go
index c57ae84c9c0..f765710dd72 100644
--- a/vendor/github.com/prometheus/prometheus/tsdb/db.go
+++ b/vendor/github.com/prometheus/prometheus/tsdb/db.go
@@ -47,6 +47,7 @@ import (
"github.com/prometheus/prometheus/tsdb/tsdbutil"
"github.com/prometheus/prometheus/tsdb/wlog"
"github.com/prometheus/prometheus/util/compression"
+ "github.com/prometheus/prometheus/util/features"
)
const (
@@ -93,6 +94,7 @@ func DefaultOptions() *Options {
CompactionDelayMaxPercent: DefaultCompactionDelayMaxPercent,
CompactionDelay: time.Duration(0),
PostingsDecoderFactory: DefaultPostingsDecoderFactory,
+ BlockReloadInterval: 1 * time.Minute,
}
}
@@ -219,6 +221,30 @@ type Options struct {
// UseUncachedIO allows bypassing the page cache when appropriate.
UseUncachedIO bool
+
+ // EnableSTAsZeroSample represents 'created-timestamp-zero-ingestion' feature flag.
+ // If true, ST, if non-zero and earlier than sample timestamp, will be stored
+ // as a zero sample before the actual sample.
+ //
+ // The zero sample is best-effort, only debug log on failure is emitted.
+ // NOTE(bwplotka): This feature might be deprecated and removed once PROM-60
+ // is implemented.
+ EnableSTAsZeroSample bool
+
+ // EnableMetadataWALRecords represents 'metadata-wal-records' feature flag.
+ // NOTE(bwplotka): This feature might be deprecated and removed once PROM-60
+ // is implemented.
+ EnableMetadataWALRecords bool
+
+ // BlockCompactionExcludeFunc is a function which returns true for blocks that should NOT be compacted.
+ // It's passed down to the TSDB compactor.
+ BlockCompactionExcludeFunc BlockExcludeFilterFunc
+
+ // BlockReloadInterval is the interval at which blocks are reloaded.
+ BlockReloadInterval time.Duration
+
+ // FeatureRegistry is used to register TSDB features.
+ FeatureRegistry features.Collector
}
type NewCompactorFunc func(ctx context.Context, r prometheus.Registerer, l *slog.Logger, ranges []int64, pool chunkenc.Pool, opts *Options) (Compactor, error)
@@ -779,6 +805,15 @@ func Open(dir string, l *slog.Logger, r prometheus.Registerer, opts *Options, st
var rngs []int64
opts, rngs = validateOpts(opts, nil)
+ // Register TSDB features if a registry is provided.
+ if opts.FeatureRegistry != nil {
+ opts.FeatureRegistry.Set(features.TSDB, "exemplar_storage", opts.EnableExemplarStorage)
+ opts.FeatureRegistry.Set(features.TSDB, "delayed_compaction", opts.EnableDelayedCompaction)
+ opts.FeatureRegistry.Set(features.TSDB, "isolation", !opts.IsolationDisabled)
+ opts.FeatureRegistry.Set(features.TSDB, "use_uncached_io", opts.UseUncachedIO)
+ opts.FeatureRegistry.Enable(features.TSDB, "native_histograms")
+ }
+
return open(dir, l, r, opts, rngs, stats)
}
@@ -813,6 +848,9 @@ func validateOpts(opts *Options, rngs []int64) (*Options, []int64) {
if opts.OutOfOrderTimeWindow < 0 {
opts.OutOfOrderTimeWindow = 0
}
+ if opts.BlockReloadInterval < 1*time.Second {
+ opts.BlockReloadInterval = 1 * time.Second
+ }
if len(rngs) == 0 {
// Start with smallest block duration and create exponential buckets until the exceed the
@@ -908,6 +946,7 @@ func open(dir string, l *slog.Logger, r prometheus.Registerer, opts *Options, rn
EnableOverlappingCompaction: opts.EnableOverlappingCompaction,
PD: opts.PostingsDecoderFactory,
UseUncachedIO: opts.UseUncachedIO,
+ BlockExcludeFilter: opts.BlockCompactionExcludeFunc,
})
}
if err != nil {
@@ -968,6 +1007,8 @@ func open(dir string, l *slog.Logger, r prometheus.Registerer, opts *Options, rn
headOpts.OutOfOrderTimeWindow.Store(opts.OutOfOrderTimeWindow)
headOpts.OutOfOrderCapMax.Store(opts.OutOfOrderCapMax)
headOpts.EnableSharding = opts.EnableSharding
+ headOpts.EnableSTAsZeroSample = opts.EnableSTAsZeroSample
+ headOpts.EnableMetadataWALRecords = opts.EnableMetadataWALRecords
if opts.WALReplayConcurrency > 0 {
headOpts.WALReplayConcurrency = opts.WALReplayConcurrency
}
@@ -1097,7 +1138,7 @@ func (db *DB) run(ctx context.Context) {
}
select {
- case <-time.After(1 * time.Minute):
+ case <-time.After(db.opts.BlockReloadInterval):
db.cmtx.Lock()
if err := db.reloadBlocks(); err != nil {
db.logger.Error("reloadBlocks", "err", err)
@@ -1131,11 +1172,16 @@ func (db *DB) run(ctx context.Context) {
}
}
-// Appender opens a new appender against the database.
+// Appender opens a new Appender against the database.
func (db *DB) Appender(ctx context.Context) storage.Appender {
return dbAppender{db: db, Appender: db.head.Appender(ctx)}
}
+// AppenderV2 opens a new AppenderV2 against the database.
+func (db *DB) AppenderV2(ctx context.Context) storage.AppenderV2 {
+ return dbAppenderV2{db: db, AppenderV2: db.head.AppenderV2(ctx)}
+}
+
// ApplyConfig applies a new config to the DB.
// Behaviour of 'OutOfOrderTimeWindow' is as follows:
// OOO enabled = oooTimeWindow > 0. OOO disabled = oooTimeWindow is 0.
@@ -1249,6 +1295,36 @@ func (a dbAppender) Commit() error {
return err
}
+// dbAppenderV2 wraps the DB's head appender and triggers compactions on commit
+// if necessary.
+type dbAppenderV2 struct {
+ storage.AppenderV2
+ db *DB
+}
+
+var _ storage.GetRef = dbAppenderV2{}
+
+func (a dbAppenderV2) GetRef(lset labels.Labels, hash uint64) (storage.SeriesRef, labels.Labels) {
+ if g, ok := a.AppenderV2.(storage.GetRef); ok {
+ return g.GetRef(lset, hash)
+ }
+ return 0, labels.EmptyLabels()
+}
+
+func (a dbAppenderV2) Commit() error {
+ err := a.AppenderV2.Commit()
+
+ // We could just run this check every few minutes practically. But for benchmarks
+ // and high frequency use cases this is the safer way.
+ if a.db.head.compactable() {
+ select {
+ case a.db.compactc <- struct{}{}:
+ default:
+ }
+ }
+ return err
+}
+
// waitingForCompactionDelay returns true if the DB is waiting for the Head compaction delay.
// This doesn't guarantee that the Head is really compactable.
func (db *DB) waitingForCompactionDelay() bool {
@@ -1981,6 +2057,13 @@ func (db *DB) Head() *Head {
// Close the partition.
func (db *DB) Close() error {
+ // Allow close-after-close operation for simpler use (e.g. tests).
+ select {
+ case <-db.donec:
+ return nil
+ default:
+ }
+
close(db.stopc)
if db.compactCancel != nil {
db.compactCancel()
diff --git a/vendor/github.com/prometheus/prometheus/tsdb/head.go b/vendor/github.com/prometheus/prometheus/tsdb/head.go
index 2c71977b1a9..25a1b88cec7 100644
--- a/vendor/github.com/prometheus/prometheus/tsdb/head.go
+++ b/vendor/github.com/prometheus/prometheus/tsdb/head.go
@@ -187,6 +187,20 @@ type HeadOptions struct {
// EnableSharding enables ShardedPostings() support in the Head.
EnableSharding bool
+
+ // EnableSTAsZeroSample represents 'created-timestamp-zero-ingestion' feature flag.
+ // If true, ST, if non-empty and earlier than sample timestamp, will be stored
+ // as a zero sample before the actual sample.
+ //
+ // The zero sample is best-effort, only debug log on failure is emitted.
+ // NOTE(bwplotka): This feature might be deprecated and removed once PROM-60
+ // is implemented.
+ EnableSTAsZeroSample bool
+
+ // EnableMetadataWALRecords represents 'metadata-wal-records' feature flag.
+ // NOTE(bwplotka): This feature might be deprecated and removed once PROM-60
+ // is implemented.
+ EnableMetadataWALRecords bool
}
const (
@@ -562,6 +576,7 @@ func newHeadMetrics(h *Head, r prometheus.Registerer) *headMetrics {
m.checkpointDeleteTotal,
m.checkpointCreationFail,
m.checkpointCreationTotal,
+ m.oooHistogram,
m.mmapChunksTotal,
m.mmapChunkCorruptionTotal,
m.snapshotReplayErrorTotal,
diff --git a/vendor/github.com/prometheus/prometheus/tsdb/head_append.go b/vendor/github.com/prometheus/prometheus/tsdb/head_append.go
index 8740d2f5ad5..356d1c453f8 100644
--- a/vendor/github.com/prometheus/prometheus/tsdb/head_append.go
+++ b/vendor/github.com/prometheus/prometheus/tsdb/head_append.go
@@ -83,14 +83,14 @@ func (a *initAppender) AppendHistogram(ref storage.SeriesRef, l labels.Labels, t
return a.app.AppendHistogram(ref, l, t, h, fh)
}
-func (a *initAppender) AppendHistogramCTZeroSample(ref storage.SeriesRef, l labels.Labels, t, ct int64, h *histogram.Histogram, fh *histogram.FloatHistogram) (storage.SeriesRef, error) {
+func (a *initAppender) AppendHistogramSTZeroSample(ref storage.SeriesRef, l labels.Labels, t, st int64, h *histogram.Histogram, fh *histogram.FloatHistogram) (storage.SeriesRef, error) {
if a.app != nil {
- return a.app.AppendHistogramCTZeroSample(ref, l, t, ct, h, fh)
+ return a.app.AppendHistogramSTZeroSample(ref, l, t, st, h, fh)
}
a.head.initTime(t)
a.app = a.head.appender()
- return a.app.AppendHistogramCTZeroSample(ref, l, t, ct, h, fh)
+ return a.app.AppendHistogramSTZeroSample(ref, l, t, st, h, fh)
}
func (a *initAppender) UpdateMetadata(ref storage.SeriesRef, l labels.Labels, m metadata.Metadata) (storage.SeriesRef, error) {
@@ -102,15 +102,15 @@ func (a *initAppender) UpdateMetadata(ref storage.SeriesRef, l labels.Labels, m
return a.app.UpdateMetadata(ref, l, m)
}
-func (a *initAppender) AppendCTZeroSample(ref storage.SeriesRef, lset labels.Labels, t, ct int64) (storage.SeriesRef, error) {
+func (a *initAppender) AppendSTZeroSample(ref storage.SeriesRef, lset labels.Labels, t, st int64) (storage.SeriesRef, error) {
if a.app != nil {
- return a.app.AppendCTZeroSample(ref, lset, t, ct)
+ return a.app.AppendSTZeroSample(ref, lset, t, st)
}
a.head.initTime(t)
a.app = a.head.appender()
- return a.app.AppendCTZeroSample(ref, lset, t, ct)
+ return a.app.AppendSTZeroSample(ref, lset, t, st)
}
// initTime initializes a head with the first timestamp. This only needs to be called
@@ -165,17 +165,19 @@ func (h *Head) appender() *headAppender {
minValidTime := h.appendableMinValidTime()
appendID, cleanupAppendIDsBelow := h.iso.newAppendID(minValidTime) // Every appender gets an ID that is cleared upon commit/rollback.
return &headAppender{
- head: h,
- minValidTime: minValidTime,
- mint: math.MaxInt64,
- maxt: math.MinInt64,
- headMaxt: h.MaxTime(),
- oooTimeWindow: h.opts.OutOfOrderTimeWindow.Load(),
- seriesRefs: h.getRefSeriesBuffer(),
- series: h.getSeriesBuffer(),
- typesInBatch: h.getTypeMap(),
- appendID: appendID,
- cleanupAppendIDsBelow: cleanupAppendIDsBelow,
+ headAppenderBase: headAppenderBase{
+ head: h,
+ minValidTime: minValidTime,
+ mint: math.MaxInt64,
+ maxt: math.MinInt64,
+ headMaxt: h.MaxTime(),
+ oooTimeWindow: h.opts.OutOfOrderTimeWindow.Load(),
+ seriesRefs: h.getRefSeriesBuffer(),
+ series: h.getSeriesBuffer(),
+ typesInBatch: h.getTypeMap(),
+ appendID: appendID,
+ cleanupAppendIDsBelow: cleanupAppendIDsBelow,
+ },
}
}
@@ -382,7 +384,7 @@ func (b *appendBatch) close(h *Head) {
b.exemplars = nil
}
-type headAppender struct {
+type headAppenderBase struct {
head *Head
minValidTime int64 // No samples below this timestamp are allowed.
mint, maxt int64
@@ -397,7 +399,10 @@ type headAppender struct {
appendID, cleanupAppendIDsBelow uint64
closed bool
- hints *storage.AppendOptions
+}
+type headAppender struct {
+ headAppenderBase
+ hints *storage.AppendOptions
}
func (a *headAppender) SetOptions(opts *storage.AppendOptions) {
@@ -483,12 +488,12 @@ func (a *headAppender) Append(ref storage.SeriesRef, lset labels.Labels, t int64
return storage.SeriesRef(s.ref), nil
}
-// AppendCTZeroSample appends synthetic zero sample for ct timestamp. It returns
+// AppendSTZeroSample appends synthetic zero sample for st timestamp. It returns
// error when sample can't be appended. See
-// storage.CreatedTimestampAppender.AppendCTZeroSample for further documentation.
-func (a *headAppender) AppendCTZeroSample(ref storage.SeriesRef, lset labels.Labels, t, ct int64) (storage.SeriesRef, error) {
- if ct >= t {
- return 0, storage.ErrCTNewerThanSample
+// storage.StartTimestampAppender.AppendSTZeroSample for further documentation.
+func (a *headAppender) AppendSTZeroSample(ref storage.SeriesRef, lset labels.Labels, t, st int64) (storage.SeriesRef, error) {
+ if st >= t {
+ return 0, storage.ErrSTNewerThanSample
}
s := a.head.series.getByID(chunks.HeadSeriesRef(ref))
@@ -500,11 +505,11 @@ func (a *headAppender) AppendCTZeroSample(ref storage.SeriesRef, lset labels.Lab
}
}
- // Check if CT wouldn't be OOO vs samples we already might have for this series.
+ // Check if ST wouldn't be OOO vs samples we already might have for this series.
// NOTE(bwplotka): This will be often hit as it's expected for long living
- // counters to share the same CT.
+ // counters to share the same ST.
s.Lock()
- isOOO, _, err := s.appendable(ct, 0, a.headMaxt, a.minValidTime, a.oooTimeWindow)
+ isOOO, _, err := s.appendable(st, 0, a.headMaxt, a.minValidTime, a.oooTimeWindow)
if err == nil {
s.pendingCommit = true
}
@@ -513,19 +518,19 @@ func (a *headAppender) AppendCTZeroSample(ref storage.SeriesRef, lset labels.Lab
return 0, err
}
if isOOO {
- return storage.SeriesRef(s.ref), storage.ErrOutOfOrderCT
+ return storage.SeriesRef(s.ref), storage.ErrOutOfOrderST
}
- if ct > a.maxt {
- a.maxt = ct
+ if st > a.maxt {
+ a.maxt = st
}
b := a.getCurrentBatch(stFloat, s.ref)
- b.floats = append(b.floats, record.RefSample{Ref: s.ref, T: ct, V: 0.0})
+ b.floats = append(b.floats, record.RefSample{Ref: s.ref, T: st, V: 0.0})
b.floatSeries = append(b.floatSeries, s)
return storage.SeriesRef(s.ref), nil
}
-func (a *headAppender) getOrCreate(lset labels.Labels) (s *memSeries, created bool, err error) {
+func (a *headAppenderBase) getOrCreate(lset labels.Labels) (s *memSeries, created bool, err error) {
// Ensure no empty labels have gotten through.
lset = lset.WithoutEmpty()
if lset.IsEmpty() {
@@ -550,7 +555,7 @@ func (a *headAppender) getOrCreate(lset labels.Labels) (s *memSeries, created bo
// getCurrentBatch returns the current batch if it fits the provided sampleType
// for the provided series. Otherwise, it adds a new batch and returns it.
-func (a *headAppender) getCurrentBatch(st sampleType, s chunks.HeadSeriesRef) *appendBatch {
+func (a *headAppenderBase) getCurrentBatch(st sampleType, s chunks.HeadSeriesRef) *appendBatch {
h := a.head
newBatch := func() *appendBatch {
@@ -902,9 +907,9 @@ func (a *headAppender) AppendHistogram(ref storage.SeriesRef, lset labels.Labels
return storage.SeriesRef(s.ref), nil
}
-func (a *headAppender) AppendHistogramCTZeroSample(ref storage.SeriesRef, lset labels.Labels, t, ct int64, h *histogram.Histogram, fh *histogram.FloatHistogram) (storage.SeriesRef, error) {
- if ct >= t {
- return 0, storage.ErrCTNewerThanSample
+func (a *headAppender) AppendHistogramSTZeroSample(ref storage.SeriesRef, lset labels.Labels, t, st int64, h *histogram.Histogram, fh *histogram.FloatHistogram) (storage.SeriesRef, error) {
+ if st >= t {
+ return 0, storage.ErrSTNewerThanSample
}
s := a.head.series.getByID(chunks.HeadSeriesRef(ref))
@@ -919,7 +924,7 @@ func (a *headAppender) AppendHistogramCTZeroSample(ref storage.SeriesRef, lset l
switch {
case h != nil:
zeroHistogram := &histogram.Histogram{
- // The CTZeroSample represents a counter reset by definition.
+ // The STZeroSample represents a counter reset by definition.
CounterResetHint: histogram.CounterReset,
// Replicate other fields to avoid needless chunk creation.
Schema: h.Schema,
@@ -927,41 +932,41 @@ func (a *headAppender) AppendHistogramCTZeroSample(ref storage.SeriesRef, lset l
CustomValues: h.CustomValues,
}
s.Lock()
- // For CTZeroSamples OOO is not allowed.
+ // For STZeroSamples OOO is not allowed.
// We set it to true to make this implementation as close as possible to the float implementation.
- isOOO, _, err := s.appendableHistogram(ct, zeroHistogram, a.headMaxt, a.minValidTime, a.oooTimeWindow)
+ isOOO, _, err := s.appendableHistogram(st, zeroHistogram, a.headMaxt, a.minValidTime, a.oooTimeWindow)
if err != nil {
s.Unlock()
if errors.Is(err, storage.ErrOutOfOrderSample) {
- return 0, storage.ErrOutOfOrderCT
+ return 0, storage.ErrOutOfOrderST
}
return 0, err
}
- // OOO is not allowed because after the first scrape, CT will be the same for most (if not all) future samples.
+ // OOO is not allowed because after the first scrape, ST will be the same for most (if not all) future samples.
// This is to prevent the injected zero from being marked as OOO forever.
if isOOO {
s.Unlock()
- return 0, storage.ErrOutOfOrderCT
+ return 0, storage.ErrOutOfOrderST
}
s.pendingCommit = true
s.Unlock()
- st := stHistogram
+ sTyp := stHistogram
if h.UsesCustomBuckets() {
- st = stCustomBucketHistogram
+ sTyp = stCustomBucketHistogram
}
- b := a.getCurrentBatch(st, s.ref)
+ b := a.getCurrentBatch(sTyp, s.ref)
b.histograms = append(b.histograms, record.RefHistogramSample{
Ref: s.ref,
- T: ct,
+ T: st,
H: zeroHistogram,
})
b.histogramSeries = append(b.histogramSeries, s)
case fh != nil:
zeroFloatHistogram := &histogram.FloatHistogram{
- // The CTZeroSample represents a counter reset by definition.
+ // The STZeroSample represents a counter reset by definition.
CounterResetHint: histogram.CounterReset,
// Replicate other fields to avoid needless chunk creation.
Schema: fh.Schema,
@@ -970,40 +975,40 @@ func (a *headAppender) AppendHistogramCTZeroSample(ref storage.SeriesRef, lset l
}
s.Lock()
// We set it to true to make this implementation as close as possible to the float implementation.
- isOOO, _, err := s.appendableFloatHistogram(ct, zeroFloatHistogram, a.headMaxt, a.minValidTime, a.oooTimeWindow) // OOO is not allowed for CTZeroSamples.
+ isOOO, _, err := s.appendableFloatHistogram(st, zeroFloatHistogram, a.headMaxt, a.minValidTime, a.oooTimeWindow) // OOO is not allowed for STZeroSamples.
if err != nil {
s.Unlock()
if errors.Is(err, storage.ErrOutOfOrderSample) {
- return 0, storage.ErrOutOfOrderCT
+ return 0, storage.ErrOutOfOrderST
}
return 0, err
}
- // OOO is not allowed because after the first scrape, CT will be the same for most (if not all) future samples.
+ // OOO is not allowed because after the first scrape, ST will be the same for most (if not all) future samples.
// This is to prevent the injected zero from being marked as OOO forever.
if isOOO {
s.Unlock()
- return 0, storage.ErrOutOfOrderCT
+ return 0, storage.ErrOutOfOrderST
}
s.pendingCommit = true
s.Unlock()
- st := stFloatHistogram
+ sTyp := stFloatHistogram
if fh.UsesCustomBuckets() {
- st = stCustomBucketFloatHistogram
+ sTyp = stCustomBucketFloatHistogram
}
- b := a.getCurrentBatch(st, s.ref)
+ b := a.getCurrentBatch(sTyp, s.ref)
b.floatHistograms = append(b.floatHistograms, record.RefFloatHistogramSample{
Ref: s.ref,
- T: ct,
+ T: st,
FH: zeroFloatHistogram,
})
b.floatHistogramSeries = append(b.floatHistogramSeries, s)
}
- if ct > a.maxt {
- a.maxt = ct
+ if st > a.maxt {
+ a.maxt = st
}
return storage.SeriesRef(s.ref), nil
@@ -1043,7 +1048,7 @@ func (a *headAppender) UpdateMetadata(ref storage.SeriesRef, lset labels.Labels,
var _ storage.GetRef = &headAppender{}
-func (a *headAppender) GetRef(lset labels.Labels, hash uint64) (storage.SeriesRef, labels.Labels) {
+func (a *headAppenderBase) GetRef(lset labels.Labels, hash uint64) (storage.SeriesRef, labels.Labels) {
s := a.head.series.getByHash(hash, lset)
if s == nil {
return 0, labels.EmptyLabels()
@@ -1053,7 +1058,7 @@ func (a *headAppender) GetRef(lset labels.Labels, hash uint64) (storage.SeriesRe
}
// log writes all headAppender's data to the WAL.
-func (a *headAppender) log() error {
+func (a *headAppenderBase) log() error {
if a.head.wal == nil {
return nil
}
@@ -1185,7 +1190,7 @@ type appenderCommitContext struct {
}
// commitExemplars adds all exemplars from the provided batch to the head's exemplar storage.
-func (a *headAppender) commitExemplars(b *appendBatch) {
+func (a *headAppenderBase) commitExemplars(b *appendBatch) {
// No errors logging to WAL, so pass the exemplars along to the in memory storage.
for _, e := range b.exemplars {
s := a.head.series.getByID(chunks.HeadSeriesRef(e.ref))
@@ -1205,7 +1210,7 @@ func (a *headAppender) commitExemplars(b *appendBatch) {
}
}
-func (acc *appenderCommitContext) collectOOORecords(a *headAppender) {
+func (acc *appenderCommitContext) collectOOORecords(a *headAppenderBase) {
if a.head.wbl == nil {
// WBL is not enabled. So no need to collect.
acc.wblSamples = nil
@@ -1310,7 +1315,7 @@ func handleAppendableError(err error, appended, oooRejected, oobRejected, tooOld
// operations on the series after appending the samples.
//
// There are also specific functions to commit histograms and float histograms.
-func (a *headAppender) commitFloats(b *appendBatch, acc *appenderCommitContext) {
+func (a *headAppenderBase) commitFloats(b *appendBatch, acc *appenderCommitContext) {
var ok, chunkCreated bool
var series *memSeries
@@ -1466,7 +1471,7 @@ func (a *headAppender) commitFloats(b *appendBatch, acc *appenderCommitContext)
}
// For details on the commitHistograms function, see the commitFloats docs.
-func (a *headAppender) commitHistograms(b *appendBatch, acc *appenderCommitContext) {
+func (a *headAppenderBase) commitHistograms(b *appendBatch, acc *appenderCommitContext) {
var ok, chunkCreated bool
var series *memSeries
@@ -1575,7 +1580,7 @@ func (a *headAppender) commitHistograms(b *appendBatch, acc *appenderCommitConte
}
// For details on the commitFloatHistograms function, see the commitFloats docs.
-func (a *headAppender) commitFloatHistograms(b *appendBatch, acc *appenderCommitContext) {
+func (a *headAppenderBase) commitFloatHistograms(b *appendBatch, acc *appenderCommitContext) {
var ok, chunkCreated bool
var series *memSeries
@@ -1697,7 +1702,7 @@ func commitMetadata(b *appendBatch) {
}
}
-func (a *headAppender) unmarkCreatedSeriesAsPendingCommit() {
+func (a *headAppenderBase) unmarkCreatedSeriesAsPendingCommit() {
for _, s := range a.series {
s.Lock()
s.pendingCommit = false
@@ -1707,7 +1712,7 @@ func (a *headAppender) unmarkCreatedSeriesAsPendingCommit() {
// Commit writes to the WAL and adds the data to the Head.
// TODO(codesome): Refactor this method to reduce indentation and make it more readable.
-func (a *headAppender) Commit() (err error) {
+func (a *headAppenderBase) Commit() (err error) {
if a.closed {
return ErrAppenderClosed
}
@@ -2238,7 +2243,7 @@ func handleChunkWriteError(err error) {
}
// Rollback removes the samples and exemplars from headAppender and writes any series to WAL.
-func (a *headAppender) Rollback() (err error) {
+func (a *headAppenderBase) Rollback() (err error) {
if a.closed {
return ErrAppenderClosed
}
diff --git a/vendor/github.com/prometheus/prometheus/tsdb/head_append_v2.go b/vendor/github.com/prometheus/prometheus/tsdb/head_append_v2.go
new file mode 100644
index 00000000000..241fb42e975
--- /dev/null
+++ b/vendor/github.com/prometheus/prometheus/tsdb/head_append_v2.go
@@ -0,0 +1,398 @@
+// 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 tsdb
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "math"
+
+ "github.com/prometheus/prometheus/model/exemplar"
+ "github.com/prometheus/prometheus/model/histogram"
+ "github.com/prometheus/prometheus/model/labels"
+ "github.com/prometheus/prometheus/model/value"
+ "github.com/prometheus/prometheus/storage"
+ "github.com/prometheus/prometheus/tsdb/chunks"
+ "github.com/prometheus/prometheus/tsdb/record"
+)
+
+// initAppenderV2 is a helper to initialize the time bounds of the head
+// upon the first sample it receives.
+type initAppenderV2 struct {
+ app storage.AppenderV2
+ head *Head
+}
+
+var _ storage.GetRef = &initAppenderV2{}
+
+func (a *initAppenderV2) Append(ref storage.SeriesRef, ls labels.Labels, st, t int64, v float64, h *histogram.Histogram, fh *histogram.FloatHistogram, opts storage.AOptions) (storage.SeriesRef, error) {
+ if a.app == nil {
+ a.head.initTime(t)
+ a.app = a.head.appenderV2()
+ }
+ return a.app.Append(ref, ls, st, t, v, h, fh, opts)
+}
+
+func (a *initAppenderV2) GetRef(lset labels.Labels, hash uint64) (storage.SeriesRef, labels.Labels) {
+ if g, ok := a.app.(storage.GetRef); ok {
+ return g.GetRef(lset, hash)
+ }
+ return 0, labels.EmptyLabels()
+}
+
+func (a *initAppenderV2) Commit() error {
+ if a.app == nil {
+ a.head.metrics.activeAppenders.Dec()
+ return nil
+ }
+ return a.app.Commit()
+}
+
+func (a *initAppenderV2) Rollback() error {
+ if a.app == nil {
+ a.head.metrics.activeAppenders.Dec()
+ return nil
+ }
+ return a.app.Rollback()
+}
+
+// AppenderV2 returns a new AppenderV2 on the database.
+func (h *Head) AppenderV2(context.Context) storage.AppenderV2 {
+ h.metrics.activeAppenders.Inc()
+
+ // The head cache might not have a starting point yet. The init appender
+ // picks up the first appended timestamp as the base.
+ if !h.initialized() {
+ return &initAppenderV2{
+ head: h,
+ }
+ }
+ return h.appenderV2()
+}
+
+func (h *Head) appenderV2() *headAppenderV2 {
+ minValidTime := h.appendableMinValidTime()
+ appendID, cleanupAppendIDsBelow := h.iso.newAppendID(minValidTime) // Every appender gets an ID that is cleared upon commit/rollback.
+ return &headAppenderV2{
+ headAppenderBase: headAppenderBase{
+ head: h,
+ minValidTime: minValidTime,
+ mint: math.MaxInt64,
+ maxt: math.MinInt64,
+ headMaxt: h.MaxTime(),
+ oooTimeWindow: h.opts.OutOfOrderTimeWindow.Load(),
+ seriesRefs: h.getRefSeriesBuffer(),
+ series: h.getSeriesBuffer(),
+ typesInBatch: h.getTypeMap(),
+ appendID: appendID,
+ cleanupAppendIDsBelow: cleanupAppendIDsBelow,
+ },
+ }
+}
+
+type headAppenderV2 struct {
+ headAppenderBase
+}
+
+func (a *headAppenderV2) Append(ref storage.SeriesRef, ls labels.Labels, st, t int64, v float64, h *histogram.Histogram, fh *histogram.FloatHistogram, opts storage.AOptions) (storage.SeriesRef, error) {
+ var (
+ // Avoid shadowing err variables for reliability.
+ valErr, appErr, partialErr error
+ sampleMetricType = sampleMetricTypeFloat
+ isStale bool
+ )
+ // Fail fast on incorrect histograms.
+
+ switch {
+ case fh != nil:
+ sampleMetricType = sampleMetricTypeHistogram
+ valErr = fh.Validate()
+ case h != nil:
+ sampleMetricType = sampleMetricTypeHistogram
+ valErr = h.Validate()
+ }
+ if valErr != nil {
+ return 0, valErr
+ }
+
+ // Fail fast if OOO is disabled and the sample is out of bounds.
+ // Otherwise, a full check will be done later to decide if the sample is in-order or out-of-order.
+ if a.oooTimeWindow == 0 && t < a.minValidTime {
+ a.head.metrics.outOfBoundSamples.WithLabelValues(sampleMetricType).Inc()
+ return 0, storage.ErrOutOfBounds
+ }
+
+ s := a.head.series.getByID(chunks.HeadSeriesRef(ref))
+ if s == nil {
+ var err error
+ s, _, err = a.getOrCreate(ls)
+ if err != nil {
+ return 0, err
+ }
+ }
+
+ // TODO(bwplotka): Handle ST natively (as per PROM-60).
+ if a.head.opts.EnableSTAsZeroSample && st != 0 {
+ a.bestEffortAppendSTZeroSample(s, ls, st, t, h, fh)
+ }
+
+ switch {
+ case fh != nil:
+ isStale = value.IsStaleNaN(fh.Sum)
+ appErr = a.appendFloatHistogram(s, t, fh, opts.RejectOutOfOrder)
+ case h != nil:
+ isStale = value.IsStaleNaN(h.Sum)
+ appErr = a.appendHistogram(s, t, h, opts.RejectOutOfOrder)
+ default:
+ isStale = value.IsStaleNaN(v)
+ if isStale {
+ // If we have added a sample before with this same appender, we
+ // can check the previously used type and turn a stale float
+ // sample into a stale histogram sample or stale float histogram
+ // sample as appropriate. This prevents an unnecessary creation
+ // of a new batch. However, since other appenders might append
+ // to the same series concurrently, this is not perfect but just
+ // an optimization for the more likely case.
+ switch a.typesInBatch[s.ref] {
+ case stHistogram, stCustomBucketHistogram:
+ return a.Append(storage.SeriesRef(s.ref), ls, st, t, 0, &histogram.Histogram{Sum: v}, nil, storage.AOptions{
+ RejectOutOfOrder: opts.RejectOutOfOrder,
+ })
+ case stFloatHistogram, stCustomBucketFloatHistogram:
+ return a.Append(storage.SeriesRef(s.ref), ls, st, t, 0, nil, &histogram.FloatHistogram{Sum: v}, storage.AOptions{
+ RejectOutOfOrder: opts.RejectOutOfOrder,
+ })
+ }
+ // Note that a series reference not yet in the map will come out
+ // as stNone, but since we do not handle that case separately,
+ // we do not need to check for the difference between "unknown
+ // series" and "known series with stNone".
+ }
+ appErr = a.appendFloat(s, t, v, opts.RejectOutOfOrder)
+ }
+ // Handle append error, if any.
+ if appErr != nil {
+ switch {
+ case errors.Is(appErr, storage.ErrOutOfOrderSample):
+ a.head.metrics.outOfOrderSamples.WithLabelValues(sampleMetricType).Inc()
+ case errors.Is(appErr, storage.ErrTooOldSample):
+ a.head.metrics.tooOldSamples.WithLabelValues(sampleMetricType).Inc()
+ }
+ return 0, appErr
+ }
+
+ if t < a.mint {
+ a.mint = t
+ }
+ if t > a.maxt {
+ a.maxt = t
+ }
+
+ if isStale {
+ // For stale values we never attempt to process metadata/exemplars, claim the success.
+ return storage.SeriesRef(s.ref), nil
+ }
+
+ // Append exemplars if any and if storage was configured for it.
+ if len(opts.Exemplars) > 0 && a.head.opts.EnableExemplarStorage && a.head.opts.MaxExemplars.Load() > 0 {
+ // Currently only exemplars can return partial errors.
+ partialErr = a.appendExemplars(s, opts.Exemplars)
+ }
+
+ // TODO(bwplotka): Move/reuse metadata tests from scrape, once scrape adopts AppenderV2.
+ // Currently tsdb package does not test metadata.
+ if a.head.opts.EnableMetadataWALRecords && !opts.Metadata.IsEmpty() {
+ s.Lock()
+ metaChanged := s.meta == nil || !s.meta.Equals(opts.Metadata)
+ s.Unlock()
+ if metaChanged {
+ b := a.getCurrentBatch(stNone, s.ref)
+ b.metadata = append(b.metadata, record.RefMetadata{
+ Ref: s.ref,
+ Type: record.GetMetricType(opts.Metadata.Type),
+ Unit: opts.Metadata.Unit,
+ Help: opts.Metadata.Help,
+ })
+ b.metadataSeries = append(b.metadataSeries, s)
+ }
+ }
+ return storage.SeriesRef(s.ref), partialErr
+}
+
+func (a *headAppenderV2) appendFloat(s *memSeries, t int64, v float64, fastRejectOOO bool) error {
+ s.Lock()
+ // TODO(codesome): If we definitely know at this point that the sample is ooo, then optimise
+ // to skip that sample from the WAL and write only in the WBL.
+ isOOO, delta, err := s.appendable(t, v, a.headMaxt, a.minValidTime, a.oooTimeWindow)
+ if isOOO && fastRejectOOO {
+ s.Unlock()
+ return storage.ErrOutOfOrderSample
+ }
+ if err == nil {
+ s.pendingCommit = true
+ }
+ s.Unlock()
+ if delta > 0 {
+ a.head.metrics.oooHistogram.Observe(float64(delta) / 1000)
+ }
+ if err != nil {
+ return err
+ }
+
+ b := a.getCurrentBatch(stFloat, s.ref)
+ b.floats = append(b.floats, record.RefSample{Ref: s.ref, T: t, V: v})
+ b.floatSeries = append(b.floatSeries, s)
+ return nil
+}
+
+func (a *headAppenderV2) appendHistogram(s *memSeries, t int64, h *histogram.Histogram, fastRejectOOO bool) error {
+ s.Lock()
+ // TODO(codesome): If we definitely know at this point that the sample is ooo, then optimise
+ // to skip that sample from the WAL and write only in the WBL.
+ isOOO, delta, err := s.appendableHistogram(t, h, a.headMaxt, a.minValidTime, a.oooTimeWindow)
+ if isOOO && fastRejectOOO {
+ s.Unlock()
+ return storage.ErrOutOfOrderSample
+ }
+ if err == nil {
+ s.pendingCommit = true
+ }
+ s.Unlock()
+ if delta > 0 {
+ a.head.metrics.oooHistogram.Observe(float64(delta) / 1000)
+ }
+ if err != nil {
+ return err
+ }
+ st := stHistogram
+ if h.UsesCustomBuckets() {
+ st = stCustomBucketHistogram
+ }
+ b := a.getCurrentBatch(st, s.ref)
+ b.histograms = append(b.histograms, record.RefHistogramSample{Ref: s.ref, T: t, H: h})
+ b.histogramSeries = append(b.histogramSeries, s)
+ return nil
+}
+
+func (a *headAppenderV2) appendFloatHistogram(s *memSeries, t int64, fh *histogram.FloatHistogram, fastRejectOOO bool) error {
+ s.Lock()
+ // TODO(codesome): If we definitely know at this point that the sample is ooo, then optimise
+ // to skip that sample from the WAL and write only in the WBL.
+ isOOO, delta, err := s.appendableFloatHistogram(t, fh, a.headMaxt, a.minValidTime, a.oooTimeWindow)
+ if isOOO && fastRejectOOO {
+ s.Unlock()
+ return storage.ErrOutOfOrderSample
+ }
+ if err == nil {
+ s.pendingCommit = true
+ }
+ s.Unlock()
+ if delta > 0 {
+ a.head.metrics.oooHistogram.Observe(float64(delta) / 1000)
+ }
+ if err != nil {
+ return err
+ }
+ st := stFloatHistogram
+ if fh.UsesCustomBuckets() {
+ st = stCustomBucketFloatHistogram
+ }
+ b := a.getCurrentBatch(st, s.ref)
+ b.floatHistograms = append(b.floatHistograms, record.RefFloatHistogramSample{Ref: s.ref, T: t, FH: fh})
+ b.floatHistogramSeries = append(b.floatHistogramSeries, s)
+ return nil
+}
+
+func (a *headAppenderV2) appendExemplars(s *memSeries, exemplar []exemplar.Exemplar) error {
+ var errs []error
+ for _, e := range exemplar {
+ // Ensure no empty labels have gotten through.
+ e.Labels = e.Labels.WithoutEmpty()
+ if err := a.head.exemplars.ValidateExemplar(s.labels(), e); err != nil {
+ if !errors.Is(err, storage.ErrDuplicateExemplar) && !errors.Is(err, storage.ErrExemplarsDisabled) {
+ // Except duplicates, return partial errors.
+ errs = append(errs, err)
+ continue
+ }
+ if !errors.Is(err, storage.ErrOutOfOrderExemplar) {
+ a.head.logger.Debug("Error while adding an exemplar on AppendSample", "exemplars", fmt.Sprintf("%+v", e), "err", err)
+ }
+ continue
+ }
+ b := a.getCurrentBatch(stNone, s.ref)
+ b.exemplars = append(b.exemplars, exemplarWithSeriesRef{storage.SeriesRef(s.ref), e})
+ }
+ if len(errs) > 0 {
+ return &storage.AppendPartialError{ExemplarErrors: errs}
+ }
+ return nil
+}
+
+// NOTE(bwplotka): This feature might be deprecated and removed once PROM-60
+// is implemented.
+//
+// ST is an experimental feature, we don't fail the append on errors, just debug log.
+func (a *headAppenderV2) bestEffortAppendSTZeroSample(s *memSeries, ls labels.Labels, st, t int64, h *histogram.Histogram, fh *histogram.FloatHistogram) {
+ // NOTE: Use lset instead of s.lset to avoid locking memSeries. Using s.ref is acceptable without locking.
+ if st >= t {
+ a.head.logger.Debug("Error when appending ST", "series", ls.String(), "st", st, "t", t, "err", storage.ErrSTNewerThanSample)
+ return
+ }
+ if st < a.minValidTime {
+ a.head.logger.Debug("Error when appending ST", "series", ls.String(), "st", st, "t", t, "err", storage.ErrOutOfBounds)
+ return
+ }
+
+ var err error
+ switch {
+ case fh != nil:
+ zeroFloatHistogram := &histogram.FloatHistogram{
+ // The STZeroSample represents a counter reset by definition.
+ CounterResetHint: histogram.CounterReset,
+ // Replicate other fields to avoid needless chunk creation.
+ Schema: fh.Schema,
+ ZeroThreshold: fh.ZeroThreshold,
+ CustomValues: fh.CustomValues,
+ }
+ err = a.appendFloatHistogram(s, st, zeroFloatHistogram, true)
+ case h != nil:
+ zeroHistogram := &histogram.Histogram{
+ // The STZeroSample represents a counter reset by definition.
+ CounterResetHint: histogram.CounterReset,
+ // Replicate other fields to avoid needless chunk creation.
+ Schema: h.Schema,
+ ZeroThreshold: h.ZeroThreshold,
+ CustomValues: h.CustomValues,
+ }
+ err = a.appendHistogram(s, st, zeroHistogram, true)
+ default:
+ err = a.appendFloat(s, st, 0, true)
+ }
+
+ if err != nil {
+ if errors.Is(err, storage.ErrOutOfOrderSample) {
+ // OOO errors are common and expected (cumulative). Explicitly ignored.
+ return
+ }
+ a.head.logger.Debug("Error when appending ST", "series", s.lset.String(), "st", st, "t", t, "err", err)
+ return
+ }
+
+ if st > a.maxt {
+ a.maxt = st
+ }
+}
+
+var _ storage.GetRef = &headAppenderV2{}
diff --git a/vendor/github.com/prometheus/prometheus/tsdb/head_read.go b/vendor/github.com/prometheus/prometheus/tsdb/head_read.go
index 8485d654357..f2681accc04 100644
--- a/vendor/github.com/prometheus/prometheus/tsdb/head_read.go
+++ b/vendor/github.com/prometheus/prometheus/tsdb/head_read.go
@@ -261,21 +261,6 @@ func unpackHeadChunkRef(ref chunks.ChunkRef) (seriesID chunks.HeadSeriesRef, chu
return sid, (cid & (oooChunkIDMask - 1)), (cid & oooChunkIDMask) != 0
}
-// LabelValueFor returns label value for the given label name in the series referred to by ID.
-func (h *headIndexReader) LabelValueFor(_ context.Context, id storage.SeriesRef, label string) (string, error) {
- memSeries := h.head.series.getByID(chunks.HeadSeriesRef(id))
- if memSeries == nil {
- return "", storage.ErrNotFound
- }
-
- value := memSeries.labels().Get(label)
- if value == "" {
- return "", storage.ErrNotFound
- }
-
- return value, nil
-}
-
// LabelNamesFor returns all the label names for the series referred to by the postings.
// The names returned are sorted.
func (h *headIndexReader) LabelNamesFor(ctx context.Context, series index.Postings) ([]string, error) {
diff --git a/vendor/github.com/prometheus/prometheus/tsdb/index/index.go b/vendor/github.com/prometheus/prometheus/tsdb/index/index.go
index 28eacd7c007..253a5158159 100644
--- a/vendor/github.com/prometheus/prometheus/tsdb/index/index.go
+++ b/vendor/github.com/prometheus/prometheus/tsdb/index/index.go
@@ -1447,32 +1447,6 @@ func (r *Reader) LabelNamesFor(ctx context.Context, postings Postings) ([]string
return names, nil
}
-// LabelValueFor returns label value for the given label name in the series referred to by ID.
-func (r *Reader) LabelValueFor(ctx context.Context, id storage.SeriesRef, label string) (string, error) {
- offset := id
- // In version 2 series IDs are no longer exact references but series are 16-byte padded
- // and the ID is the multiple of 16 of the actual position.
- if r.version != FormatV1 {
- offset = id * seriesByteAlign
- }
- d := encoding.NewDecbufUvarintAt(r.b, int(offset), castagnoliTable)
- buf := d.Get()
- if d.Err() != nil {
- return "", fmt.Errorf("label values for: %w", d.Err())
- }
-
- value, err := r.dec.LabelValueFor(ctx, buf, label)
- if err != nil {
- return "", storage.ErrNotFound
- }
-
- if value == "" {
- return "", storage.ErrNotFound
- }
-
- return value, nil
-}
-
// Series reads the series with the given ID and writes its labels and chunks into builder and chks.
func (r *Reader) Series(id storage.SeriesRef, builder *labels.ScratchBuilder, chks *[]chunks.Meta) error {
offset := id
@@ -1809,37 +1783,6 @@ func (*Decoder) LabelNamesOffsetsFor(b []byte) ([]uint32, error) {
return offsets, d.Err()
}
-// LabelValueFor decodes a label for a given series.
-func (dec *Decoder) LabelValueFor(ctx context.Context, b []byte, label string) (string, error) {
- d := encoding.Decbuf{B: b}
- k := d.Uvarint()
-
- for range k {
- lno := uint32(d.Uvarint())
- lvo := uint32(d.Uvarint())
-
- if d.Err() != nil {
- return "", fmt.Errorf("read series label offsets: %w", d.Err())
- }
-
- ln, err := dec.LookupSymbol(ctx, lno)
- if err != nil {
- return "", fmt.Errorf("lookup label name: %w", err)
- }
-
- if ln == label {
- lv, err := dec.LookupSymbol(ctx, lvo)
- if err != nil {
- return "", fmt.Errorf("lookup label value: %w", err)
- }
-
- return lv, nil
- }
- }
-
- return "", d.Err()
-}
-
// Series decodes a series entry from the given byte slice into builder and chks.
// Previous contents of builder can be overwritten - make sure you copy before retaining.
// Skips reading chunks metadata if chks is nil.
diff --git a/vendor/github.com/prometheus/prometheus/tsdb/index/postings.go b/vendor/github.com/prometheus/prometheus/tsdb/index/postings.go
index d5a17c3daac..0185f58819f 100644
--- a/vendor/github.com/prometheus/prometheus/tsdb/index/postings.go
+++ b/vendor/github.com/prometheus/prometheus/tsdb/index/postings.go
@@ -391,7 +391,7 @@ func (p *MemPostings) Iter(f func(labels.Label, Postings) error) error {
for n, e := range p.m {
for v, p := range e {
- if err := f(labels.Label{Name: n, Value: v}, newListPostings(p...)); err != nil {
+ if err := f(labels.Label{Name: n, Value: v}, NewListPostings(p)); err != nil {
return err
}
}
@@ -478,8 +478,8 @@ func (p *MemPostings) PostingsForLabelMatching(ctx context.Context, name string,
}
// Now `vals` only contains the values that matched, get their postings.
- its := make([]*ListPostings, 0, len(vals))
- lps := make([]ListPostings, len(vals))
+ its := make([]*listPostings, 0, len(vals))
+ lps := make([]listPostings, len(vals))
p.mtx.RLock()
e := p.m[name]
for i, v := range vals {
@@ -488,7 +488,7 @@ func (p *MemPostings) PostingsForLabelMatching(ctx context.Context, name string,
// If we didn't let the mutex go, we'd have these postings here, but they would be pointing nowhere
// because there would be a `MemPostings.Delete()` call waiting for the lock to delete these labels,
// because the series were deleted already.
- lps[i] = ListPostings{list: refs}
+ lps[i] = listPostings{list: refs}
its = append(its, &lps[i])
}
}
@@ -500,13 +500,13 @@ func (p *MemPostings) PostingsForLabelMatching(ctx context.Context, name string,
// Postings returns a postings iterator for the given label values.
func (p *MemPostings) Postings(ctx context.Context, name string, values ...string) Postings {
- res := make([]*ListPostings, 0, len(values))
- lps := make([]ListPostings, len(values))
+ res := make([]*listPostings, 0, len(values))
+ lps := make([]listPostings, len(values))
p.mtx.RLock()
postingsMapForName := p.m[name]
for i, value := range values {
if lp := postingsMapForName[value]; lp != nil {
- lps[i] = ListPostings{list: lp}
+ lps[i] = listPostings{list: lp}
res = append(res, &lps[i])
}
}
@@ -518,12 +518,12 @@ func (p *MemPostings) PostingsForAllLabelValues(ctx context.Context, name string
p.mtx.RLock()
e := p.m[name]
- its := make([]*ListPostings, 0, len(e))
- lps := make([]ListPostings, len(e))
+ its := make([]*listPostings, 0, len(e))
+ lps := make([]listPostings, len(e))
i := 0
for _, refs := range e {
if len(refs) > 0 {
- lps[i] = ListPostings{list: refs}
+ lps[i] = listPostings{list: refs}
its = append(its, &lps[i])
}
i++
@@ -542,7 +542,7 @@ func ExpandPostings(p Postings) (res []storage.SeriesRef, err error) {
return res, p.Err()
}
-// Postings provides iterative access over a postings list.
+// Postings provides iterative access over an ordered list of SeriesRef.
type Postings interface {
// Next advances the iterator and returns true if another value was found.
Next() bool
@@ -641,15 +641,28 @@ func (it *intersectPostings) Seek(target storage.SeriesRef) bool {
}
func (it *intersectPostings) Next() bool {
- target := it.current
- for _, p := range it.postings {
+ // Move forward the first Postings and take its value as the target to match.
+ if !it.postings[0].Next() {
+ return false
+ }
+ target := it.postings[0].At()
+ allEqual := true
+ for _, p := range it.postings[1:] { // Now move forward all the other ones and check if they match.
if !p.Next() {
return false
}
- if p.At() > target {
- target = p.At()
+ at := p.At()
+ if at > target { // This one is past the target, so pick up a new target to Seek at the end.
+ target = at
+ allEqual = false
+ } else if at < target { // This one needs to Seek to the target, but carry on with other postings in case they have an even higher target.
+ allEqual = false
}
}
+ if allEqual {
+ it.current = target
+ return true
+ }
return it.Seek(target)
}
@@ -814,25 +827,23 @@ func (rp *removedPostings) Err() error {
return rp.remove.Err()
}
-// ListPostings implements the Postings interface over a plain list.
-type ListPostings struct {
+// listPostings implements the Postings interface over a plain list.
+type listPostings struct {
list []storage.SeriesRef
cur storage.SeriesRef
}
+// NewListPostings creates a Postings from the supplied SeriesRefs, which must be in order.
+// The list slice passed in is retained.
func NewListPostings(list []storage.SeriesRef) Postings {
- return newListPostings(list...)
-}
-
-func newListPostings(list ...storage.SeriesRef) *ListPostings {
- return &ListPostings{list: list}
+ return &listPostings{list: list}
}
-func (it *ListPostings) At() storage.SeriesRef {
+func (it *listPostings) At() storage.SeriesRef {
return it.cur
}
-func (it *ListPostings) Next() bool {
+func (it *listPostings) Next() bool {
if len(it.list) > 0 {
it.cur = it.list[0]
it.list = it.list[1:]
@@ -842,7 +853,7 @@ func (it *ListPostings) Next() bool {
return false
}
-func (it *ListPostings) Seek(x storage.SeriesRef) bool {
+func (it *listPostings) Seek(x storage.SeriesRef) bool {
// If the current value satisfies, then return.
if it.cur >= x {
return true
@@ -851,23 +862,25 @@ func (it *ListPostings) Seek(x storage.SeriesRef) bool {
return false
}
- // Do binary search between current position and end.
- i, _ := slices.BinarySearch(it.list, x)
- if i < len(it.list) {
- it.cur = it.list[i]
- it.list = it.list[i+1:]
- return true
+ i := 0 // Check the next item in the list, otherwise binary search between current position and end.
+ if it.list[0] < x {
+ i, _ = slices.BinarySearch(it.list, x)
+ if i >= len(it.list) { // Off the end - terminate the iterator.
+ it.list = nil
+ return false
+ }
}
- it.list = nil
- return false
+ it.cur = it.list[i]
+ it.list = it.list[i+1:]
+ return true
}
-func (*ListPostings) Err() error {
+func (*listPostings) Err() error {
return nil
}
// Len returns the remaining number of postings in the list.
-func (it *ListPostings) Len() int {
+func (it *listPostings) Len() int {
return len(it.list)
}
diff --git a/vendor/github.com/prometheus/prometheus/tsdb/ooo_head_read.go b/vendor/github.com/prometheus/prometheus/tsdb/ooo_head_read.go
index af8f9b1f83f..4cecb9fd6c4 100644
--- a/vendor/github.com/prometheus/prometheus/tsdb/ooo_head_read.go
+++ b/vendor/github.com/prometheus/prometheus/tsdb/ooo_head_read.go
@@ -500,10 +500,6 @@ func (*OOOCompactionHeadIndexReader) LabelNames(context.Context, ...*labels.Matc
return nil, errors.New("not implemented")
}
-func (*OOOCompactionHeadIndexReader) LabelValueFor(context.Context, storage.SeriesRef, string) (string, error) {
- return "", errors.New("not implemented")
-}
-
func (*OOOCompactionHeadIndexReader) LabelNamesFor(context.Context, index.Postings) ([]string, error) {
return nil, errors.New("not implemented")
}
diff --git a/vendor/github.com/prometheus/prometheus/tsdb/record/record.go b/vendor/github.com/prometheus/prometheus/tsdb/record/record.go
index 561810a3a54..5791f60df40 100644
--- a/vendor/github.com/prometheus/prometheus/tsdb/record/record.go
+++ b/vendor/github.com/prometheus/prometheus/tsdb/record/record.go
@@ -475,7 +475,9 @@ func (d *Decoder) HistogramSamples(rec []byte, histograms []RefHistogramSample)
// This is a very slow path, but it should only happen if the
// record is from a newer Prometheus version that supports higher
// resolution.
- rh.H.ReduceResolution(histogram.ExponentialSchemaMax)
+ if err := rh.H.ReduceResolution(histogram.ExponentialSchemaMax); err != nil {
+ return nil, fmt.Errorf("error reducing resolution of histogram #%d: %w", len(histograms)+1, err)
+ }
}
histograms = append(histograms, rh)
@@ -579,7 +581,9 @@ func (d *Decoder) FloatHistogramSamples(rec []byte, histograms []RefFloatHistogr
// This is a very slow path, but it should only happen if the
// record is from a newer Prometheus version that supports higher
// resolution.
- rh.FH.ReduceResolution(histogram.ExponentialSchemaMax)
+ if err := rh.FH.ReduceResolution(histogram.ExponentialSchemaMax); err != nil {
+ return nil, fmt.Errorf("error reducing resolution of histogram #%d: %w", len(histograms)+1, err)
+ }
}
histograms = append(histograms, rh)
diff --git a/vendor/github.com/prometheus/prometheus/tsdb/testutil.go b/vendor/github.com/prometheus/prometheus/tsdb/testutil.go
index 4d413322c8a..d41591750b6 100644
--- a/vendor/github.com/prometheus/prometheus/tsdb/testutil.go
+++ b/vendor/github.com/prometheus/prometheus/tsdb/testutil.go
@@ -44,14 +44,14 @@ type testValue struct {
type sampleTypeScenario struct {
sampleType string
- appendFunc func(appender storage.Appender, lbls labels.Labels, ts, value int64) (storage.SeriesRef, sample, error)
+ appendFunc func(appender storage.LimitedAppenderV1, lbls labels.Labels, ts, value int64) (storage.SeriesRef, sample, error)
sampleFunc func(ts, value int64) sample
}
var sampleTypeScenarios = map[string]sampleTypeScenario{
float: {
sampleType: sampleMetricTypeFloat,
- appendFunc: func(appender storage.Appender, lbls labels.Labels, ts, value int64) (storage.SeriesRef, sample, error) {
+ appendFunc: func(appender storage.LimitedAppenderV1, lbls labels.Labels, ts, value int64) (storage.SeriesRef, sample, error) {
s := sample{t: ts, f: float64(value)}
ref, err := appender.Append(0, lbls, ts, s.f)
return ref, s, err
@@ -62,7 +62,7 @@ var sampleTypeScenarios = map[string]sampleTypeScenario{
},
intHistogram: {
sampleType: sampleMetricTypeHistogram,
- appendFunc: func(appender storage.Appender, lbls labels.Labels, ts, value int64) (storage.SeriesRef, sample, error) {
+ appendFunc: func(appender storage.LimitedAppenderV1, lbls labels.Labels, ts, value int64) (storage.SeriesRef, sample, error) {
s := sample{t: ts, h: tsdbutil.GenerateTestHistogram(value)}
ref, err := appender.AppendHistogram(0, lbls, ts, s.h, nil)
return ref, s, err
@@ -73,7 +73,7 @@ var sampleTypeScenarios = map[string]sampleTypeScenario{
},
floatHistogram: {
sampleType: sampleMetricTypeHistogram,
- appendFunc: func(appender storage.Appender, lbls labels.Labels, ts, value int64) (storage.SeriesRef, sample, error) {
+ appendFunc: func(appender storage.LimitedAppenderV1, lbls labels.Labels, ts, value int64) (storage.SeriesRef, sample, error) {
s := sample{t: ts, fh: tsdbutil.GenerateTestFloatHistogram(value)}
ref, err := appender.AppendHistogram(0, lbls, ts, nil, s.fh)
return ref, s, err
@@ -84,7 +84,7 @@ var sampleTypeScenarios = map[string]sampleTypeScenario{
},
customBucketsIntHistogram: {
sampleType: sampleMetricTypeHistogram,
- appendFunc: func(appender storage.Appender, lbls labels.Labels, ts, value int64) (storage.SeriesRef, sample, error) {
+ appendFunc: func(appender storage.LimitedAppenderV1, lbls labels.Labels, ts, value int64) (storage.SeriesRef, sample, error) {
s := sample{t: ts, h: tsdbutil.GenerateTestCustomBucketsHistogram(value)}
ref, err := appender.AppendHistogram(0, lbls, ts, s.h, nil)
return ref, s, err
@@ -95,7 +95,7 @@ var sampleTypeScenarios = map[string]sampleTypeScenario{
},
customBucketsFloatHistogram: {
sampleType: sampleMetricTypeHistogram,
- appendFunc: func(appender storage.Appender, lbls labels.Labels, ts, value int64) (storage.SeriesRef, sample, error) {
+ appendFunc: func(appender storage.LimitedAppenderV1, lbls labels.Labels, ts, value int64) (storage.SeriesRef, sample, error) {
s := sample{t: ts, fh: tsdbutil.GenerateTestCustomBucketsFloatHistogram(value)}
ref, err := appender.AppendHistogram(0, lbls, ts, nil, s.fh)
return ref, s, err
@@ -106,7 +106,7 @@ var sampleTypeScenarios = map[string]sampleTypeScenario{
},
gaugeIntHistogram: {
sampleType: sampleMetricTypeHistogram,
- appendFunc: func(appender storage.Appender, lbls labels.Labels, ts, value int64) (storage.SeriesRef, sample, error) {
+ appendFunc: func(appender storage.LimitedAppenderV1, lbls labels.Labels, ts, value int64) (storage.SeriesRef, sample, error) {
s := sample{t: ts, h: tsdbutil.GenerateTestGaugeHistogram(value)}
ref, err := appender.AppendHistogram(0, lbls, ts, s.h, nil)
return ref, s, err
@@ -117,7 +117,7 @@ var sampleTypeScenarios = map[string]sampleTypeScenario{
},
gaugeFloatHistogram: {
sampleType: sampleMetricTypeHistogram,
- appendFunc: func(appender storage.Appender, lbls labels.Labels, ts, value int64) (storage.SeriesRef, sample, error) {
+ appendFunc: func(appender storage.LimitedAppenderV1, lbls labels.Labels, ts, value int64) (storage.SeriesRef, sample, error) {
s := sample{t: ts, fh: tsdbutil.GenerateTestGaugeFloatHistogram(value)}
ref, err := appender.AppendHistogram(0, lbls, ts, nil, s.fh)
return ref, s, err
diff --git a/vendor/github.com/prometheus/prometheus/util/features/features.go b/vendor/github.com/prometheus/prometheus/util/features/features.go
new file mode 100644
index 00000000000..d52384dbd88
--- /dev/null
+++ b/vendor/github.com/prometheus/prometheus/util/features/features.go
@@ -0,0 +1,127 @@
+// 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 features
+
+import (
+ "maps"
+ "sync"
+)
+
+// Category constants define the standard feature flag categories used in Prometheus.
+const (
+ API = "api"
+ OTLPReceiver = "otlp_receiver"
+ Prometheus = "prometheus"
+ PromQL = "promql"
+ PromQLFunctions = "promql_functions"
+ PromQLOperators = "promql_operators"
+ Rules = "rules"
+ Scrape = "scrape"
+ ServiceDiscoveryProviders = "service_discovery_providers"
+ TemplatingFunctions = "templating_functions"
+ TSDB = "tsdb"
+ UI = "ui"
+)
+
+// Collector defines the interface for collecting and managing feature flags.
+// It provides methods to enable, disable, and retrieve feature states.
+type Collector interface {
+ // Enable marks a feature as enabled in the registry.
+ // The category and name should use snake_case naming convention.
+ Enable(category, name string)
+
+ // Disable marks a feature as disabled in the registry.
+ // The category and name should use snake_case naming convention.
+ Disable(category, name string)
+
+ // Set sets a feature to the specified enabled state.
+ // The category and name should use snake_case naming convention.
+ Set(category, name string, enabled bool)
+
+ // Get returns a copy of all registered features organized by category.
+ // Returns a map where the keys are category names and values are maps
+ // of feature names to their enabled status.
+ Get() map[string]map[string]bool
+}
+
+// registry is the private implementation of the Collector interface.
+// It stores feature information organized by category.
+type registry struct {
+ mu sync.RWMutex
+ features map[string]map[string]bool
+}
+
+// DefaultRegistry is the package-level registry used by Prometheus.
+var DefaultRegistry = NewRegistry()
+
+// NewRegistry creates a new feature registry.
+func NewRegistry() Collector {
+ return ®istry{
+ features: make(map[string]map[string]bool),
+ }
+}
+
+// Enable marks a feature as enabled in the registry.
+func (r *registry) Enable(category, name string) {
+ r.Set(category, name, true)
+}
+
+// Disable marks a feature as disabled in the registry.
+func (r *registry) Disable(category, name string) {
+ r.Set(category, name, false)
+}
+
+// Set sets a feature to the specified enabled state.
+func (r *registry) Set(category, name string, enabled bool) {
+ r.mu.Lock()
+ defer r.mu.Unlock()
+
+ if r.features[category] == nil {
+ r.features[category] = make(map[string]bool)
+ }
+ r.features[category][name] = enabled
+}
+
+// Get returns a copy of all registered features organized by category.
+func (r *registry) Get() map[string]map[string]bool {
+ r.mu.RLock()
+ defer r.mu.RUnlock()
+
+ result := make(map[string]map[string]bool, len(r.features))
+ for category, features := range r.features {
+ result[category] = make(map[string]bool, len(features))
+ maps.Copy(result[category], features)
+ }
+ return result
+}
+
+// Enable marks a feature as enabled in the default registry.
+func Enable(category, name string) {
+ DefaultRegistry.Enable(category, name)
+}
+
+// Disable marks a feature as disabled in the default registry.
+func Disable(category, name string) {
+ DefaultRegistry.Disable(category, name)
+}
+
+// Set sets a feature to the specified enabled state in the default registry.
+func Set(category, name string, enabled bool) {
+ DefaultRegistry.Set(category, name, enabled)
+}
+
+// Get returns all features from the default registry.
+func Get() map[string]map[string]bool {
+ return DefaultRegistry.Get()
+}
diff --git a/vendor/github.com/prometheus/prometheus/web/api/v1/api.go b/vendor/github.com/prometheus/prometheus/web/api/v1/api.go
index baddedd4951..2a6036ba0b8 100644
--- a/vendor/github.com/prometheus/prometheus/web/api/v1/api.go
+++ b/vendor/github.com/prometheus/prometheus/web/api/v1/api.go
@@ -56,6 +56,7 @@ import (
"github.com/prometheus/prometheus/tsdb"
"github.com/prometheus/prometheus/tsdb/index"
"github.com/prometheus/prometheus/util/annotations"
+ "github.com/prometheus/prometheus/util/features"
"github.com/prometheus/prometheus/util/httputil"
"github.com/prometheus/prometheus/util/notifications"
"github.com/prometheus/prometheus/util/stats"
@@ -255,6 +256,8 @@ type API struct {
otlpWriteHandler http.Handler
codecs []Codec
+
+ featureRegistry features.Collector
}
// NewAPI returns an initialized API type.
@@ -290,11 +293,12 @@ func NewAPI(
rwEnabled bool,
acceptRemoteWriteProtoMsgs remoteapi.MessageTypes,
otlpEnabled, otlpDeltaToCumulative, otlpNativeDeltaIngestion bool,
- ctZeroIngestionEnabled bool,
+ stZeroIngestionEnabled bool,
lookbackDelta time.Duration,
enableTypeAndUnitLabels bool,
appendMetadata bool,
overrideErrorCode OverrideErrorCode,
+ featureRegistry features.Collector,
) *API {
a := &API{
QueryEngine: qe,
@@ -324,6 +328,7 @@ func NewAPI(
notificationsGetter: notificationsGetter,
notificationsSub: notificationsSub,
overrideErrorCode: overrideErrorCode,
+ featureRegistry: featureRegistry,
remoteReadHandler: remote.NewReadHandler(logger, registerer, q, configFunc, remoteReadSampleLimit, remoteReadConcurrencyLimit, remoteReadMaxBytesInFrame),
}
@@ -339,15 +344,16 @@ func NewAPI(
}
if rwEnabled {
- a.remoteWriteHandler = remote.NewWriteHandler(logger, registerer, ap, acceptRemoteWriteProtoMsgs, ctZeroIngestionEnabled, enableTypeAndUnitLabels, appendMetadata)
+ a.remoteWriteHandler = remote.NewWriteHandler(logger, registerer, ap, acceptRemoteWriteProtoMsgs, stZeroIngestionEnabled, enableTypeAndUnitLabels, appendMetadata)
}
if otlpEnabled {
a.otlpWriteHandler = remote.NewOTLPWriteHandler(logger, registerer, ap, configFunc, remote.OTLPOptions{
ConvertDelta: otlpDeltaToCumulative,
NativeDelta: otlpNativeDeltaIngestion,
LookbackDelta: lookbackDelta,
- IngestCTZeroSample: ctZeroIngestionEnabled,
+ IngestSTZeroSample: stZeroIngestionEnabled,
EnableTypeAndUnitLabels: enableTypeAndUnitLabels,
+ AppendMetadata: appendMetadata,
})
}
@@ -444,6 +450,7 @@ func (api *API) Register(r *route.Router) {
r.Get("/status/flags", wrap(api.serveFlags))
r.Get("/status/tsdb", wrapAgent(api.serveTSDBStatus))
r.Get("/status/tsdb/blocks", wrapAgent(api.serveTSDBBlocks))
+ r.Get("/features", wrap(api.features))
r.Get("/status/walreplay", api.serveWALReplayStatus)
r.Get("/notifications", api.notifications)
r.Get("/notifications/live", api.notificationsSSE)
@@ -1788,6 +1795,29 @@ func (api *API) serveFlags(*http.Request) apiFuncResult {
return apiFuncResult{api.flagsMap, nil, nil, nil}
}
+// featuresData wraps feature flags data to provide custom JSON marshaling without HTML escaping.
+// featuresData does not contain user-provided input, and it is more convenient to have unescaped
+// representation of PromQL operators like >=.
+type featuresData struct {
+ data map[string]map[string]bool
+}
+
+func (f featuresData) MarshalJSON() ([]byte, error) {
+ json := jsoniter.Config{
+ EscapeHTML: false,
+ SortMapKeys: true,
+ ValidateJsonRawMessage: true,
+ }.Froze()
+ return json.Marshal(f.data)
+}
+
+func (api *API) features(*http.Request) apiFuncResult {
+ if api.featureRegistry == nil {
+ return apiFuncResult{nil, &apiError{errorInternal, errors.New("feature registry not configured")}, nil, nil}
+ }
+ return apiFuncResult{featuresData{data: api.featureRegistry.Get()}, nil, nil, nil}
+}
+
// TSDBStat holds the information about individual cardinality.
type TSDBStat struct {
Name string `json:"name"`
@@ -1836,12 +1866,16 @@ func (api *API) serveTSDBBlocks(*http.Request) apiFuncResult {
}
func (api *API) serveTSDBStatus(r *http.Request) apiFuncResult {
+ const maxTSDBLimit = 10000
limit := 10
if s := r.FormValue("limit"); s != "" {
var err error
if limit, err = strconv.Atoi(s); err != nil || limit < 1 {
return apiFuncResult{nil, &apiError{errorBadData, errors.New("limit must be a positive number")}, nil, nil}
}
+ if limit > maxTSDBLimit {
+ return apiFuncResult{nil, &apiError{errorBadData, fmt.Errorf("limit must not exceed %d", maxTSDBLimit)}, nil, nil}
+ }
}
s, err := api.db.Stats(labels.MetricName, limit)
if err != nil {
diff --git a/vendor/go.opentelemetry.io/collector/component/component.go b/vendor/go.opentelemetry.io/collector/component/component.go
index 5a32c5041d7..1da518ac335 100644
--- a/vendor/go.opentelemetry.io/collector/component/component.go
+++ b/vendor/go.opentelemetry.io/collector/component/component.go
@@ -46,7 +46,7 @@ type Component interface {
// If there are any background operations running by the component they must be aborted before
// this function returns. Remember that if you started any long-running background operations from
// the Start() method, those operations must be also cancelled. If there are any buffers in the
- // component, they should be cleared and the data sent immediately to the next component.
+ // component, they should be flushed with the data being sent immediately to the next component.
//
// The component's lifecycle is completed once the Shutdown() method returns. No other
// methods of the component are called after that. If necessary a new component with
diff --git a/vendor/go.opentelemetry.io/collector/component/host.go b/vendor/go.opentelemetry.io/collector/component/host.go
index dc8210ffbe3..f425b76bb67 100644
--- a/vendor/go.opentelemetry.io/collector/component/host.go
+++ b/vendor/go.opentelemetry.io/collector/component/host.go
@@ -17,5 +17,7 @@ type Host interface {
//
// GetExtensions can be called by the component anytime after Component.Start() begins and
// until Component.Shutdown() ends.
+ //
+ // The returned map should only be nil if the host does not support extensions at all.
GetExtensions() map[ID]Component
}
diff --git a/vendor/go.opentelemetry.io/collector/confmap/internal/decoder.go b/vendor/go.opentelemetry.io/collector/confmap/internal/decoder.go
index 17f172a2ec3..7009c9f410b 100644
--- a/vendor/go.opentelemetry.io/collector/confmap/internal/decoder.go
+++ b/vendor/go.opentelemetry.io/collector/confmap/internal/decoder.go
@@ -7,7 +7,6 @@ import (
"encoding"
"errors"
"fmt"
- "maps"
"reflect"
"slices"
"strings"
@@ -57,7 +56,7 @@ func Decode(input, result any, settings UnmarshalOptions, skipTopLevelUnmarshale
unmarshalerHookFunc(result, skipTopLevelUnmarshaler),
// after the main unmarshaler hook is called,
// we unmarshal the embedded structs if present to merge with the result:
- unmarshalerEmbeddedStructsHookFunc(),
+ unmarshalerEmbeddedStructsHookFunc(settings),
zeroSliceAndMapHookFunc(),
),
}
@@ -168,7 +167,7 @@ func mapKeyStringToMapKeyTextUnmarshalerHookFunc() mapstructure.DecodeHookFuncTy
}
// Create a map with key value of to's key to bool.
- fieldNameSet := reflect.MakeMap(reflect.MapOf(to.Key(), reflect.TypeOf(true)))
+ fieldNameSet := reflect.MakeMap(reflect.MapOf(to.Key(), reflect.TypeFor[bool]()))
for k := range data.(map[string]any) {
// Create a new value of the to's key type.
tKey := reflect.New(to.Key())
@@ -189,7 +188,7 @@ func mapKeyStringToMapKeyTextUnmarshalerHookFunc() mapstructure.DecodeHookFuncTy
// unmarshalerEmbeddedStructsHookFunc provides a mechanism for embedded structs to define their own unmarshal logic,
// by implementing the Unmarshaler interface.
-func unmarshalerEmbeddedStructsHookFunc() mapstructure.DecodeHookFuncValue {
+func unmarshalerEmbeddedStructsHookFunc(settings UnmarshalOptions) mapstructure.DecodeHookFuncValue {
return safeWrapDecodeHookFunc(func(from, to reflect.Value) (any, error) {
if to.Type().Kind() != reflect.Struct {
return from.Interface(), nil
@@ -198,32 +197,72 @@ func unmarshalerEmbeddedStructsHookFunc() mapstructure.DecodeHookFuncValue {
if !ok {
return from.Interface(), nil
}
+
+ // First call Unmarshaler on squashed embedded fields, if necessary.
+ var squashedUnmarshalers []int
for i := 0; i < to.Type().NumField(); i++ {
- // embedded structs passed in via `squash` cannot be pointers. We just check if they are structs:
f := to.Type().Field(i)
- if f.IsExported() && slices.Contains(strings.Split(f.Tag.Get(MapstructureTag), ","), "squash") {
- if unmarshaler, ok := to.Field(i).Addr().Interface().(Unmarshaler); ok {
- c := NewFromStringMap(fromAsMap)
- c.skipTopLevelUnmarshaler = true
- if err := unmarshaler.Unmarshal(c); err != nil {
- return nil, err
- }
- // the struct we receive from this unmarshaling only contains fields related to the embedded struct.
- // we merge this partially unmarshaled struct with the rest of the result.
- // note we already unmarshaled the main struct earlier, and therefore merge with it.
- conf := New()
- if err := conf.Marshal(unmarshaler); err != nil {
- return nil, err
- }
- resultMap := conf.ToStringMap()
- if fromAsMap == nil && len(resultMap) > 0 {
- fromAsMap = make(map[string]any, len(resultMap))
- }
- maps.Copy(fromAsMap, resultMap)
- }
+ if !f.IsExported() {
+ continue
+ }
+ tagParts := strings.Split(f.Tag.Get(MapstructureTag), ",")
+ if !slices.Contains(tagParts[1:], "squash") {
+ continue
+ }
+ unmarshaler, ok := to.Field(i).Addr().Interface().(Unmarshaler)
+ if !ok {
+ continue
}
+ c := NewFromStringMap(fromAsMap)
+ c.skipTopLevelUnmarshaler = true
+ if err := unmarshaler.Unmarshal(c); err != nil {
+ return nil, err
+ }
+ squashedUnmarshalers = append(squashedUnmarshalers, i)
+ }
+
+ // No squashed unmarshalers, we can let mapstructure do its job.
+ if len(squashedUnmarshalers) == 0 {
+ return fromAsMap, nil
}
- return fromAsMap, nil
+
+ // We need to unmarshal into all other fields without overwriting the output of the Unmarshal calls.
+ // To do that, create a custom "partial" struct containing only the non-squashed fields.
+ var fields []reflect.StructField
+ var fieldValues []reflect.Value
+ for i := 0; i < to.Type().NumField(); i++ {
+ f := to.Type().Field(i)
+ if !f.IsExported() {
+ continue
+ }
+ if slices.Contains(squashedUnmarshalers, i) {
+ continue
+ }
+ fields = append(fields, f)
+ fieldValues = append(fieldValues, to.Field(i))
+ }
+ restType := reflect.StructOf(fields)
+ restValue := reflect.New(restType)
+
+ // Copy initial values into partial struct.
+ for i, fieldValue := range fieldValues {
+ restValue.Elem().Field(i).Set(fieldValue)
+ }
+
+ // Decode into the partial struct.
+ // This performs a recursive call into this hook, which will be handled by the "no squashed unmarshalers" case above.
+ // We need to set `IgnoreUnused` to avoid errors from the map containing fields only present in the full struct.
+ settings.IgnoreUnused = true
+ if err := Decode(fromAsMap, restValue.Interface(), settings, true); err != nil {
+ return nil, err
+ }
+
+ // Copy decoding results back to the original struct.
+ for i, fieldValue := range fieldValues {
+ fieldValue.Set(restValue.Elem().Field(i))
+ }
+
+ return to, nil
})
}
diff --git a/vendor/go.opentelemetry.io/collector/confmap/xconfmap/config.go b/vendor/go.opentelemetry.io/collector/confmap/xconfmap/config.go
index ba90b54bc28..113d0a3fce1 100644
--- a/vendor/go.opentelemetry.io/collector/confmap/xconfmap/config.go
+++ b/vendor/go.opentelemetry.io/collector/confmap/xconfmap/config.go
@@ -15,7 +15,7 @@ import (
// As interface types are only used for static typing, a common idiom to find the reflection Type
// for an interface type Foo is to use a *Foo value.
-var configValidatorType = reflect.TypeOf((*Validator)(nil)).Elem()
+var configValidatorType = reflect.TypeFor[Validator]()
// Validator defines an optional interface for configurations to implement to do validation.
type Validator interface {
diff --git a/vendor/go.opentelemetry.io/collector/pdata/internal/generated_proto_location.go b/vendor/go.opentelemetry.io/collector/pdata/internal/generated_proto_location.go
index 7b16e381ff5..bceb7556171 100644
--- a/vendor/go.opentelemetry.io/collector/pdata/internal/generated_proto_location.go
+++ b/vendor/go.opentelemetry.io/collector/pdata/internal/generated_proto_location.go
@@ -18,7 +18,7 @@ import (
type Location struct {
MappingIndex int32
Address uint64
- Line []*Line
+ Lines []*Line
AttributeIndices []int32
}
@@ -47,8 +47,8 @@ func DeleteLocation(orig *Location, nullable bool) {
return
}
- for i := range orig.Line {
- DeleteLine(orig.Line[i], true)
+ for i := range orig.Lines {
+ DeleteLine(orig.Lines[i], true)
}
orig.Reset()
@@ -74,7 +74,7 @@ func CopyLocation(dest, src *Location) *Location {
dest.Address = src.Address
- dest.Line = CopyLinePtrSlice(dest.Line, src.Line)
+ dest.Lines = CopyLinePtrSlice(dest.Lines, src.Lines)
dest.AttributeIndices = append(dest.AttributeIndices[:0], src.AttributeIndices...)
@@ -144,13 +144,13 @@ func (orig *Location) MarshalJSON(dest *json.Stream) {
dest.WriteObjectField("address")
dest.WriteUint64(orig.Address)
}
- if len(orig.Line) > 0 {
- dest.WriteObjectField("line")
+ if len(orig.Lines) > 0 {
+ dest.WriteObjectField("lines")
dest.WriteArrayStart()
- orig.Line[0].MarshalJSON(dest)
- for i := 1; i < len(orig.Line); i++ {
+ orig.Lines[0].MarshalJSON(dest)
+ for i := 1; i < len(orig.Lines); i++ {
dest.WriteMore()
- orig.Line[i].MarshalJSON(dest)
+ orig.Lines[i].MarshalJSON(dest)
}
dest.WriteArrayEnd()
}
@@ -175,10 +175,10 @@ func (orig *Location) UnmarshalJSON(iter *json.Iterator) {
orig.MappingIndex = iter.ReadInt32()
case "address":
orig.Address = iter.ReadUint64()
- case "line":
+ case "lines":
for iter.ReadArray() {
- orig.Line = append(orig.Line, NewLine())
- orig.Line[len(orig.Line)-1].UnmarshalJSON(iter)
+ orig.Lines = append(orig.Lines, NewLine())
+ orig.Lines[len(orig.Lines)-1].UnmarshalJSON(iter)
}
case "attributeIndices", "attribute_indices":
@@ -202,8 +202,8 @@ func (orig *Location) SizeProto() int {
if orig.Address != 0 {
n += 1 + proto.Sov(uint64(orig.Address))
}
- for i := range orig.Line {
- l = orig.Line[i].SizeProto()
+ for i := range orig.Lines {
+ l = orig.Lines[i].SizeProto()
n += 1 + proto.Sov(uint64(l)) + l
}
if len(orig.AttributeIndices) > 0 {
@@ -230,8 +230,8 @@ func (orig *Location) MarshalProto(buf []byte) int {
pos--
buf[pos] = 0x10
}
- for i := len(orig.Line) - 1; i >= 0; i-- {
- l = orig.Line[i].MarshalProto(buf[:pos])
+ for i := len(orig.Lines) - 1; i >= 0; i-- {
+ l = orig.Lines[i].MarshalProto(buf[:pos])
pos -= l
pos = proto.EncodeVarint(buf, pos, uint64(l))
pos--
@@ -291,7 +291,7 @@ func (orig *Location) UnmarshalProto(buf []byte) error {
case 3:
if wireType != proto.WireTypeLen {
- return fmt.Errorf("proto: wrong wireType = %d for field Line", wireType)
+ return fmt.Errorf("proto: wrong wireType = %d for field Lines", wireType)
}
var length int
length, pos, err = proto.ConsumeLen(buf, pos)
@@ -299,8 +299,8 @@ func (orig *Location) UnmarshalProto(buf []byte) error {
return err
}
startPos := pos - length
- orig.Line = append(orig.Line, NewLine())
- err = orig.Line[len(orig.Line)-1].UnmarshalProto(buf[startPos:pos])
+ orig.Lines = append(orig.Lines, NewLine())
+ err = orig.Lines[len(orig.Lines)-1].UnmarshalProto(buf[startPos:pos])
if err != nil {
return err
}
@@ -348,7 +348,7 @@ func GenTestLocation() *Location {
orig := NewLocation()
orig.MappingIndex = int32(13)
orig.Address = uint64(13)
- orig.Line = []*Line{{}, GenTestLine()}
+ orig.Lines = []*Line{{}, GenTestLine()}
orig.AttributeIndices = []int32{int32(0), int32(13)}
return orig
}
diff --git a/vendor/go.opentelemetry.io/collector/pdata/internal/generated_proto_profile.go b/vendor/go.opentelemetry.io/collector/pdata/internal/generated_proto_profile.go
index eb1e0b9f922..c3a6460d20a 100644
--- a/vendor/go.opentelemetry.io/collector/pdata/internal/generated_proto_profile.go
+++ b/vendor/go.opentelemetry.io/collector/pdata/internal/generated_proto_profile.go
@@ -19,12 +19,11 @@ import (
type Profile struct {
SampleType ValueType
- Sample []*Sample
+ Samples []*Sample
TimeUnixNano uint64
DurationNano uint64
PeriodType ValueType
Period int64
- CommentStrindices []int32
ProfileId ProfileID
DroppedAttributesCount uint32
OriginalPayloadFormat string
@@ -58,8 +57,8 @@ func DeleteProfile(orig *Profile, nullable bool) {
}
DeleteValueType(&orig.SampleType, false)
- for i := range orig.Sample {
- DeleteSample(orig.Sample[i], true)
+ for i := range orig.Samples {
+ DeleteSample(orig.Samples[i], true)
}
DeleteValueType(&orig.PeriodType, false)
DeleteProfileID(&orig.ProfileId, false)
@@ -85,7 +84,7 @@ func CopyProfile(dest, src *Profile) *Profile {
}
CopyValueType(&dest.SampleType, &src.SampleType)
- dest.Sample = CopySamplePtrSlice(dest.Sample, src.Sample)
+ dest.Samples = CopySamplePtrSlice(dest.Samples, src.Samples)
dest.TimeUnixNano = src.TimeUnixNano
@@ -95,7 +94,6 @@ func CopyProfile(dest, src *Profile) *Profile {
dest.Period = src.Period
- dest.CommentStrindices = append(dest.CommentStrindices[:0], src.CommentStrindices...)
CopyProfileID(&dest.ProfileId, &src.ProfileId)
dest.DroppedAttributesCount = src.DroppedAttributesCount
@@ -166,13 +164,13 @@ func (orig *Profile) MarshalJSON(dest *json.Stream) {
dest.WriteObjectStart()
dest.WriteObjectField("sampleType")
orig.SampleType.MarshalJSON(dest)
- if len(orig.Sample) > 0 {
- dest.WriteObjectField("sample")
+ if len(orig.Samples) > 0 {
+ dest.WriteObjectField("samples")
dest.WriteArrayStart()
- orig.Sample[0].MarshalJSON(dest)
- for i := 1; i < len(orig.Sample); i++ {
+ orig.Samples[0].MarshalJSON(dest)
+ for i := 1; i < len(orig.Samples); i++ {
dest.WriteMore()
- orig.Sample[i].MarshalJSON(dest)
+ orig.Samples[i].MarshalJSON(dest)
}
dest.WriteArrayEnd()
}
@@ -190,16 +188,6 @@ func (orig *Profile) MarshalJSON(dest *json.Stream) {
dest.WriteObjectField("period")
dest.WriteInt64(orig.Period)
}
- if len(orig.CommentStrindices) > 0 {
- dest.WriteObjectField("commentStrindices")
- dest.WriteArrayStart()
- dest.WriteInt32(orig.CommentStrindices[0])
- for i := 1; i < len(orig.CommentStrindices); i++ {
- dest.WriteMore()
- dest.WriteInt32(orig.CommentStrindices[i])
- }
- dest.WriteArrayEnd()
- }
if !orig.ProfileId.IsEmpty() {
dest.WriteObjectField("profileId")
orig.ProfileId.MarshalJSON(dest)
@@ -237,10 +225,10 @@ func (orig *Profile) UnmarshalJSON(iter *json.Iterator) {
case "sampleType", "sample_type":
orig.SampleType.UnmarshalJSON(iter)
- case "sample":
+ case "samples":
for iter.ReadArray() {
- orig.Sample = append(orig.Sample, NewSample())
- orig.Sample[len(orig.Sample)-1].UnmarshalJSON(iter)
+ orig.Samples = append(orig.Samples, NewSample())
+ orig.Samples[len(orig.Samples)-1].UnmarshalJSON(iter)
}
case "timeUnixNano", "time_unix_nano":
@@ -252,11 +240,6 @@ func (orig *Profile) UnmarshalJSON(iter *json.Iterator) {
orig.PeriodType.UnmarshalJSON(iter)
case "period":
orig.Period = iter.ReadInt64()
- case "commentStrindices", "comment_strindices":
- for iter.ReadArray() {
- orig.CommentStrindices = append(orig.CommentStrindices, iter.ReadInt32())
- }
-
case "profileId", "profile_id":
orig.ProfileId.UnmarshalJSON(iter)
@@ -283,8 +266,8 @@ func (orig *Profile) SizeProto() int {
_ = l
l = orig.SampleType.SizeProto()
n += 1 + proto.Sov(uint64(l)) + l
- for i := range orig.Sample {
- l = orig.Sample[i].SizeProto()
+ for i := range orig.Samples {
+ l = orig.Samples[i].SizeProto()
n += 1 + proto.Sov(uint64(l)) + l
}
if orig.TimeUnixNano != 0 {
@@ -298,13 +281,6 @@ func (orig *Profile) SizeProto() int {
if orig.Period != 0 {
n += 1 + proto.Sov(uint64(orig.Period))
}
- if len(orig.CommentStrindices) > 0 {
- l = 0
- for _, e := range orig.CommentStrindices {
- l += proto.Sov(uint64(e))
- }
- n += 1 + proto.Sov(uint64(l)) + l
- }
l = orig.ProfileId.SizeProto()
n += 1 + proto.Sov(uint64(l)) + l
if orig.DroppedAttributesCount != 0 {
@@ -338,8 +314,8 @@ func (orig *Profile) MarshalProto(buf []byte) int {
pos--
buf[pos] = 0xa
- for i := len(orig.Sample) - 1; i >= 0; i-- {
- l = orig.Sample[i].MarshalProto(buf[:pos])
+ for i := len(orig.Samples) - 1; i >= 0; i-- {
+ l = orig.Samples[i].MarshalProto(buf[:pos])
pos -= l
pos = proto.EncodeVarint(buf, pos, uint64(l))
pos--
@@ -367,26 +343,16 @@ func (orig *Profile) MarshalProto(buf []byte) int {
pos--
buf[pos] = 0x30
}
- l = len(orig.CommentStrindices)
- if l > 0 {
- endPos := pos
- for i := l - 1; i >= 0; i-- {
- pos = proto.EncodeVarint(buf, pos, uint64(orig.CommentStrindices[i]))
- }
- pos = proto.EncodeVarint(buf, pos, uint64(endPos-pos))
- pos--
- buf[pos] = 0x3a
- }
l = orig.ProfileId.MarshalProto(buf[:pos])
pos -= l
pos = proto.EncodeVarint(buf, pos, uint64(l))
pos--
- buf[pos] = 0x42
+ buf[pos] = 0x3a
if orig.DroppedAttributesCount != 0 {
pos = proto.EncodeVarint(buf, pos, uint64(orig.DroppedAttributesCount))
pos--
- buf[pos] = 0x48
+ buf[pos] = 0x40
}
l = len(orig.OriginalPayloadFormat)
if l > 0 {
@@ -394,7 +360,7 @@ func (orig *Profile) MarshalProto(buf []byte) int {
copy(buf[pos:], orig.OriginalPayloadFormat)
pos = proto.EncodeVarint(buf, pos, uint64(l))
pos--
- buf[pos] = 0x52
+ buf[pos] = 0x4a
}
l = len(orig.OriginalPayload)
if l > 0 {
@@ -402,7 +368,7 @@ func (orig *Profile) MarshalProto(buf []byte) int {
copy(buf[pos:], orig.OriginalPayload)
pos = proto.EncodeVarint(buf, pos, uint64(l))
pos--
- buf[pos] = 0x5a
+ buf[pos] = 0x52
}
l = len(orig.AttributeIndices)
if l > 0 {
@@ -412,7 +378,7 @@ func (orig *Profile) MarshalProto(buf []byte) int {
}
pos = proto.EncodeVarint(buf, pos, uint64(endPos-pos))
pos--
- buf[pos] = 0x62
+ buf[pos] = 0x5a
}
return len(buf) - pos
}
@@ -450,7 +416,7 @@ func (orig *Profile) UnmarshalProto(buf []byte) error {
case 2:
if wireType != proto.WireTypeLen {
- return fmt.Errorf("proto: wrong wireType = %d for field Sample", wireType)
+ return fmt.Errorf("proto: wrong wireType = %d for field Samples", wireType)
}
var length int
length, pos, err = proto.ConsumeLen(buf, pos)
@@ -458,8 +424,8 @@ func (orig *Profile) UnmarshalProto(buf []byte) error {
return err
}
startPos := pos - length
- orig.Sample = append(orig.Sample, NewSample())
- err = orig.Sample[len(orig.Sample)-1].UnmarshalProto(buf[startPos:pos])
+ orig.Samples = append(orig.Samples, NewSample())
+ err = orig.Samples[len(orig.Samples)-1].UnmarshalProto(buf[startPos:pos])
if err != nil {
return err
}
@@ -515,38 +481,8 @@ func (orig *Profile) UnmarshalProto(buf []byte) error {
}
orig.Period = int64(num)
- case 7:
- switch wireType {
- case proto.WireTypeLen:
- var length int
- length, pos, err = proto.ConsumeLen(buf, pos)
- if err != nil {
- return err
- }
- startPos := pos - length
- var num uint64
- for startPos < pos {
- num, startPos, err = proto.ConsumeVarint(buf[:pos], startPos)
- if err != nil {
- return err
- }
- orig.CommentStrindices = append(orig.CommentStrindices, int32(num))
- }
- if startPos != pos {
- return fmt.Errorf("proto: invalid field len = %d for field CommentStrindices", pos-startPos)
- }
- case proto.WireTypeVarint:
- var num uint64
- num, pos, err = proto.ConsumeVarint(buf, pos)
- if err != nil {
- return err
- }
- orig.CommentStrindices = append(orig.CommentStrindices, int32(num))
- default:
- return fmt.Errorf("proto: wrong wireType = %d for field CommentStrindices", wireType)
- }
- case 8:
+ case 7:
if wireType != proto.WireTypeLen {
return fmt.Errorf("proto: wrong wireType = %d for field ProfileId", wireType)
}
@@ -562,7 +498,7 @@ func (orig *Profile) UnmarshalProto(buf []byte) error {
return err
}
- case 9:
+ case 8:
if wireType != proto.WireTypeVarint {
return fmt.Errorf("proto: wrong wireType = %d for field DroppedAttributesCount", wireType)
}
@@ -574,7 +510,7 @@ func (orig *Profile) UnmarshalProto(buf []byte) error {
orig.DroppedAttributesCount = uint32(num)
- case 10:
+ case 9:
if wireType != proto.WireTypeLen {
return fmt.Errorf("proto: wrong wireType = %d for field OriginalPayloadFormat", wireType)
}
@@ -586,7 +522,7 @@ func (orig *Profile) UnmarshalProto(buf []byte) error {
startPos := pos - length
orig.OriginalPayloadFormat = string(buf[startPos:pos])
- case 11:
+ case 10:
if wireType != proto.WireTypeLen {
return fmt.Errorf("proto: wrong wireType = %d for field OriginalPayload", wireType)
}
@@ -600,7 +536,7 @@ func (orig *Profile) UnmarshalProto(buf []byte) error {
orig.OriginalPayload = make([]byte, length)
copy(orig.OriginalPayload, buf[startPos:pos])
}
- case 12:
+ case 11:
switch wireType {
case proto.WireTypeLen:
var length int
@@ -643,12 +579,11 @@ func (orig *Profile) UnmarshalProto(buf []byte) error {
func GenTestProfile() *Profile {
orig := NewProfile()
orig.SampleType = *GenTestValueType()
- orig.Sample = []*Sample{{}, GenTestSample()}
+ orig.Samples = []*Sample{{}, GenTestSample()}
orig.TimeUnixNano = uint64(13)
orig.DurationNano = uint64(13)
orig.PeriodType = *GenTestValueType()
orig.Period = int64(13)
- orig.CommentStrindices = []int32{int32(0), int32(13)}
orig.ProfileId = *GenTestProfileID()
orig.DroppedAttributesCount = uint32(13)
orig.OriginalPayloadFormat = "test_originalpayloadformat"
diff --git a/vendor/go.opentelemetry.io/collector/pdata/internal/generated_proto_valuetype.go b/vendor/go.opentelemetry.io/collector/pdata/internal/generated_proto_valuetype.go
index a070ab3e70f..7742846f24a 100644
--- a/vendor/go.opentelemetry.io/collector/pdata/internal/generated_proto_valuetype.go
+++ b/vendor/go.opentelemetry.io/collector/pdata/internal/generated_proto_valuetype.go
@@ -14,11 +14,10 @@ import (
"go.opentelemetry.io/collector/pdata/internal/proto"
)
-// ValueType describes the type and units of a value, with an optional aggregation temporality.
+// ValueType describes the type and units of a value.
type ValueType struct {
- TypeStrindex int32
- UnitStrindex int32
- AggregationTemporality AggregationTemporality
+ TypeStrindex int32
+ UnitStrindex int32
}
var (
@@ -69,8 +68,6 @@ func CopyValueType(dest, src *ValueType) *ValueType {
dest.UnitStrindex = src.UnitStrindex
- dest.AggregationTemporality = src.AggregationTemporality
-
return dest
}
@@ -137,11 +134,6 @@ func (orig *ValueType) MarshalJSON(dest *json.Stream) {
dest.WriteObjectField("unitStrindex")
dest.WriteInt32(orig.UnitStrindex)
}
-
- if int32(orig.AggregationTemporality) != 0 {
- dest.WriteObjectField("aggregationTemporality")
- dest.WriteInt32(int32(orig.AggregationTemporality))
- }
dest.WriteObjectEnd()
}
@@ -153,8 +145,6 @@ func (orig *ValueType) UnmarshalJSON(iter *json.Iterator) {
orig.TypeStrindex = iter.ReadInt32()
case "unitStrindex", "unit_strindex":
orig.UnitStrindex = iter.ReadInt32()
- case "aggregationTemporality", "aggregation_temporality":
- orig.AggregationTemporality = AggregationTemporality(iter.ReadEnumValue(AggregationTemporality_value))
default:
iter.Skip()
}
@@ -171,9 +161,6 @@ func (orig *ValueType) SizeProto() int {
if orig.UnitStrindex != 0 {
n += 1 + proto.Sov(uint64(orig.UnitStrindex))
}
- if orig.AggregationTemporality != 0 {
- n += 1 + proto.Sov(uint64(orig.AggregationTemporality))
- }
return n
}
@@ -191,11 +178,6 @@ func (orig *ValueType) MarshalProto(buf []byte) int {
pos--
buf[pos] = 0x10
}
- if orig.AggregationTemporality != 0 {
- pos = proto.EncodeVarint(buf, pos, uint64(orig.AggregationTemporality))
- pos--
- buf[pos] = 0x18
- }
return len(buf) - pos
}
@@ -237,18 +219,6 @@ func (orig *ValueType) UnmarshalProto(buf []byte) error {
}
orig.UnitStrindex = int32(num)
-
- case 3:
- if wireType != proto.WireTypeVarint {
- return fmt.Errorf("proto: wrong wireType = %d for field AggregationTemporality", wireType)
- }
- var num uint64
- num, pos, err = proto.ConsumeVarint(buf, pos)
- if err != nil {
- return err
- }
-
- orig.AggregationTemporality = AggregationTemporality(num)
default:
pos, err = proto.ConsumeUnknown(buf, pos, wireType)
if err != nil {
@@ -263,7 +233,6 @@ func GenTestValueType() *ValueType {
orig := NewValueType()
orig.TypeStrindex = int32(13)
orig.UnitStrindex = int32(13)
- orig.AggregationTemporality = AggregationTemporality(13)
return orig
}
diff --git a/vendor/go.opentelemetry.io/collector/pdata/internal/proto/marshal.go b/vendor/go.opentelemetry.io/collector/pdata/internal/proto/marshal.go
index 1655533a2ce..b4d494e6e54 100644
--- a/vendor/go.opentelemetry.io/collector/pdata/internal/proto/marshal.go
+++ b/vendor/go.opentelemetry.io/collector/pdata/internal/proto/marshal.go
@@ -8,7 +8,6 @@ func EncodeVarint(buf []byte, offset int, v uint64) int {
offset -= Sov(v)
base := offset
for v >= 1<<7 {
- //nolint:gosec
buf[offset] = uint8(v&0x7f | 0x80)
v >>= 7
offset++
diff --git a/vendor/go.opentelemetry.io/collector/pdata/internal/proto/size.go b/vendor/go.opentelemetry.io/collector/pdata/internal/proto/size.go
index 6f48443bf9d..bfc52147599 100644
--- a/vendor/go.opentelemetry.io/collector/pdata/internal/proto/size.go
+++ b/vendor/go.opentelemetry.io/collector/pdata/internal/proto/size.go
@@ -12,6 +12,5 @@ func Sov(x uint64) (n int) {
}
func Soz(x uint64) (n int) {
- //nolint:gosec
return Sov((x << 1) ^ uint64((int64(x) >> 63)))
}
diff --git a/vendor/go.opentelemetry.io/collector/pdata/internal/proto/unmarshal.go b/vendor/go.opentelemetry.io/collector/pdata/internal/proto/unmarshal.go
index 6ecf5ca3a3a..b6dc0c79495 100644
--- a/vendor/go.opentelemetry.io/collector/pdata/internal/proto/unmarshal.go
+++ b/vendor/go.opentelemetry.io/collector/pdata/internal/proto/unmarshal.go
@@ -89,7 +89,6 @@ func ConsumeLen(buf []byte, pos int) (int, int, error) {
if err != nil {
return 0, 0, err
}
- //nolint:gosec
length := int(num)
if length < 0 {
return 0, 0, ErrInvalidLength
@@ -116,9 +115,7 @@ func ConsumeTag(buf []byte, pos int) (int32, WireType, int, error) {
if err != nil {
return 0, 0, 0, err
}
- //nolint:gosec
fieldNum := int32(tag >> 3)
- //nolint:gosec
wireType := int8(tag & 0x7)
if fieldNum <= 0 {
return 0, 0, 0, fmt.Errorf("proto: Link: illegal field=%d (tag=%d, pos=%d)", fieldNum, tag, pos)
diff --git a/vendor/go.opentelemetry.io/collector/pdata/pcommon/timestamp.go b/vendor/go.opentelemetry.io/collector/pdata/pcommon/timestamp.go
index 037213a0caf..5fd1758b1be 100644
--- a/vendor/go.opentelemetry.io/collector/pdata/pcommon/timestamp.go
+++ b/vendor/go.opentelemetry.io/collector/pdata/pcommon/timestamp.go
@@ -13,13 +13,11 @@ type Timestamp uint64
// NewTimestampFromTime constructs a new Timestamp from the provided time.Time.
func NewTimestampFromTime(t time.Time) Timestamp {
- //nolint:gosec
return Timestamp(uint64(t.UnixNano()))
}
// AsTime converts this to a time.Time.
func (ts Timestamp) AsTime() time.Time {
- //nolint:gosec
return time.Unix(0, int64(ts)).UTC()
}
diff --git a/vendor/go.opentelemetry.io/collector/pdata/pcommon/value.go b/vendor/go.opentelemetry.io/collector/pdata/pcommon/value.go
index f74fa79467b..3c39e704ed9 100644
--- a/vendor/go.opentelemetry.io/collector/pdata/pcommon/value.go
+++ b/vendor/go.opentelemetry.io/collector/pdata/pcommon/value.go
@@ -166,7 +166,6 @@ func (v Value) FromRaw(iv any) error {
case int64:
v.SetInt(tv)
case uint:
- //nolint:gosec
v.SetInt(int64(tv))
case uint8:
v.SetInt(int64(tv))
@@ -175,7 +174,6 @@ func (v Value) FromRaw(iv any) error {
case uint32:
v.SetInt(int64(tv))
case uint64:
- //nolint:gosec
v.SetInt(int64(tv))
case float32:
v.SetDouble(float64(tv))
diff --git a/vendor/go.uber.org/zap/.golangci.yml b/vendor/go.uber.org/zap/.golangci.yml
index 2346df13517..74faaa71d8c 100644
--- a/vendor/go.uber.org/zap/.golangci.yml
+++ b/vendor/go.uber.org/zap/.golangci.yml
@@ -25,7 +25,7 @@ linters-settings:
govet:
# These govet checks are disabled by default, but they're useful.
enable:
- - niliness
+ - nilness
- reflectvaluecompare
- sortslice
- unusedwrite
diff --git a/vendor/go.uber.org/zap/CHANGELOG.md b/vendor/go.uber.org/zap/CHANGELOG.md
index 6d6cd5f4d70..86e7e6f9820 100644
--- a/vendor/go.uber.org/zap/CHANGELOG.md
+++ b/vendor/go.uber.org/zap/CHANGELOG.md
@@ -3,6 +3,16 @@ All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## 1.27.1 (19 Nov 2025)
+Enhancements:
+* [#1501][]: prevent `Object` from panicking on nils
+* [#1511][]: Fix a race condition in `WithLazy`.
+
+Thanks to @rabbbit, @alshopov, @jquirke, @arukiidou for their contributions to this release.
+
+[#1501]: https://github.com/uber-go/zap/pull/1501
+[#1511]: https://github.com/uber-go/zap/pull/1511
+
## 1.27.0 (20 Feb 2024)
Enhancements:
* [#1378][]: Add `WithLazy` method for `SugaredLogger`.
diff --git a/vendor/go.uber.org/zap/CODE_OF_CONDUCT.md b/vendor/go.uber.org/zap/CODE_OF_CONDUCT.md
index e327d9aa5cd..bc988b72ed0 100644
--- a/vendor/go.uber.org/zap/CODE_OF_CONDUCT.md
+++ b/vendor/go.uber.org/zap/CODE_OF_CONDUCT.md
@@ -71,5 +71,5 @@ This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 1.4, available at
[http://contributor-covenant.org/version/1/4][version].
-[homepage]: http://contributor-covenant.org
-[version]: http://contributor-covenant.org/version/1/4/
+[homepage]: https://contributor-covenant.org
+[version]: https://contributor-covenant.org/version/1/4/
diff --git a/vendor/go.uber.org/zap/LICENSE b/vendor/go.uber.org/zap/LICENSE
index 6652bed45f4..3883b9a7eab 100644
--- a/vendor/go.uber.org/zap/LICENSE
+++ b/vendor/go.uber.org/zap/LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2016-2017 Uber Technologies, Inc.
+Copyright (c) 2016-2024 Uber Technologies, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/vendor/go.uber.org/zap/Makefile b/vendor/go.uber.org/zap/Makefile
index eb1cee53bd5..f9db385b34c 100644
--- a/vendor/go.uber.org/zap/Makefile
+++ b/vendor/go.uber.org/zap/Makefile
@@ -24,7 +24,7 @@ golangci-lint:
@$(foreach mod,$(MODULE_DIRS), \
(cd $(mod) && \
echo "[lint] golangci-lint: $(mod)" && \
- golangci-lint run --path-prefix $(mod)) &&) true
+ golangci-lint run --path-prefix $(mod) ./...) &&) true
.PHONY: tidy
tidy:
diff --git a/vendor/go.uber.org/zap/field.go b/vendor/go.uber.org/zap/field.go
index 6743930b823..1884afabcd3 100644
--- a/vendor/go.uber.org/zap/field.go
+++ b/vendor/go.uber.org/zap/field.go
@@ -398,6 +398,9 @@ func Durationp(key string, val *time.Duration) Field {
// struct-like user-defined types to the logging context. The struct's
// MarshalLogObject method is called lazily.
func Object(key string, val zapcore.ObjectMarshaler) Field {
+ if val == nil {
+ return nilField(key)
+ }
return Field{Key: key, Type: zapcore.ObjectMarshalerType, Interface: val}
}
@@ -431,6 +434,13 @@ func (d dictObject) MarshalLogObject(enc zapcore.ObjectEncoder) error {
return nil
}
+// DictObject constructs a [zapcore.ObjectMarshaler] with the given list of fields.
+// The resulting object marshaler can be used as input to [Object], [Objects], or
+// any other functions that expect an object marshaler.
+func DictObject(val ...Field) zapcore.ObjectMarshaler {
+ return dictObject(val)
+}
+
// We discovered an issue where zap.Any can cause a performance degradation
// when used in new goroutines.
//
diff --git a/vendor/go.uber.org/zap/http_handler.go b/vendor/go.uber.org/zap/http_handler.go
index 2be8f651500..1cae2c164b5 100644
--- a/vendor/go.uber.org/zap/http_handler.go
+++ b/vendor/go.uber.org/zap/http_handler.go
@@ -71,7 +71,7 @@ import (
func (lvl AtomicLevel) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if err := lvl.serveHTTP(w, r); err != nil {
w.WriteHeader(http.StatusInternalServerError)
- fmt.Fprintf(w, "internal error: %v", err)
+ _, _ = fmt.Fprintf(w, "internal error: %v", err)
}
}
diff --git a/vendor/go.uber.org/zap/logger.go b/vendor/go.uber.org/zap/logger.go
index c4d30032394..2d0ef141bcf 100644
--- a/vendor/go.uber.org/zap/logger.go
+++ b/vendor/go.uber.org/zap/logger.go
@@ -381,7 +381,11 @@ func (log *Logger) check(lvl zapcore.Level, msg string) *zapcore.CheckedEntry {
if stack.Count() == 0 {
if log.addCaller {
- fmt.Fprintf(log.errorOutput, "%v Logger.check error: failed to get caller\n", ent.Time.UTC())
+ _, _ = fmt.Fprintf(
+ log.errorOutput,
+ "%v Logger.check error: failed to get caller\n",
+ ent.Time.UTC(),
+ )
_ = log.errorOutput.Sync()
}
return ce
diff --git a/vendor/go.uber.org/zap/options.go b/vendor/go.uber.org/zap/options.go
index 43d357ac902..04a3c1e6357 100644
--- a/vendor/go.uber.org/zap/options.go
+++ b/vendor/go.uber.org/zap/options.go
@@ -125,7 +125,11 @@ func IncreaseLevel(lvl zapcore.LevelEnabler) Option {
return optionFunc(func(log *Logger) {
core, err := zapcore.NewIncreaseLevelCore(log.core, lvl)
if err != nil {
- fmt.Fprintf(log.errorOutput, "failed to IncreaseLevel: %v\n", err)
+ _, _ = fmt.Fprintf(
+ log.errorOutput,
+ "failed to IncreaseLevel: %v\n",
+ err,
+ )
} else {
log.core = core
}
diff --git a/vendor/go.uber.org/zap/sink.go b/vendor/go.uber.org/zap/sink.go
index 499772a00dc..92202280f13 100644
--- a/vendor/go.uber.org/zap/sink.go
+++ b/vendor/go.uber.org/zap/sink.go
@@ -71,7 +71,7 @@ func newSinkRegistry() *sinkRegistry {
return sr
}
-// RegisterScheme registers the given factory for the specific scheme.
+// RegisterSink registers the given factory for the specific scheme.
func (sr *sinkRegistry) RegisterSink(scheme string, factory func(*url.URL) (Sink, error)) error {
sr.mu.Lock()
defer sr.mu.Unlock()
diff --git a/vendor/go.uber.org/zap/zapcore/buffered_write_syncer.go b/vendor/go.uber.org/zap/zapcore/buffered_write_syncer.go
index a40e93b3ec8..4b426a56487 100644
--- a/vendor/go.uber.org/zap/zapcore/buffered_write_syncer.go
+++ b/vendor/go.uber.org/zap/zapcore/buffered_write_syncer.go
@@ -188,32 +188,33 @@ func (s *BufferedWriteSyncer) flushLoop() {
// Stop closes the buffer, cleans up background goroutines, and flushes
// remaining unwritten data.
func (s *BufferedWriteSyncer) Stop() (err error) {
- var stopped bool
-
// Critical section.
- func() {
+ stopped := func() bool {
s.mu.Lock()
defer s.mu.Unlock()
if !s.initialized {
- return
+ return false
}
- stopped = s.stopped
- if stopped {
- return
+ if s.stopped {
+ return false
}
s.stopped = true
s.ticker.Stop()
close(s.stop) // tell flushLoop to stop
- <-s.done // and wait until it has
+ return true
}()
- // Don't call Sync on consecutive Stops.
+ // Not initialized, or already stopped, no need for any cleanup.
if !stopped {
- err = s.Sync()
+ return
}
- return err
+ // Wait for flushLoop to end outside of the lock, as it may need the lock to complete.
+ // See https://github.com/uber-go/zap/issues/1428 for details.
+ <-s.done
+
+ return s.Sync()
}
diff --git a/vendor/go.uber.org/zap/zapcore/console_encoder.go b/vendor/go.uber.org/zap/zapcore/console_encoder.go
index cc2b4e07b93..98eea5154d4 100644
--- a/vendor/go.uber.org/zap/zapcore/console_encoder.go
+++ b/vendor/go.uber.org/zap/zapcore/console_encoder.go
@@ -105,7 +105,7 @@ func (c consoleEncoder) EncodeEntry(ent Entry, fields []Field) (*buffer.Buffer,
if i > 0 {
line.AppendString(c.ConsoleSeparator)
}
- fmt.Fprint(line, arr.elems[i])
+ _, _ = fmt.Fprint(line, arr.elems[i])
}
putSliceEncoder(arr)
diff --git a/vendor/go.uber.org/zap/zapcore/entry.go b/vendor/go.uber.org/zap/zapcore/entry.go
index 459a5d7ce3c..841752f2ed6 100644
--- a/vendor/go.uber.org/zap/zapcore/entry.go
+++ b/vendor/go.uber.org/zap/zapcore/entry.go
@@ -241,7 +241,12 @@ func (ce *CheckedEntry) Write(fields ...Field) {
// If the entry is dirty, log an internal error; because the
// CheckedEntry is being used after it was returned to the pool,
// the message may be an amalgamation from multiple call sites.
- fmt.Fprintf(ce.ErrorOutput, "%v Unsafe CheckedEntry re-use near Entry %+v.\n", ce.Time, ce.Entry)
+ _, _ = fmt.Fprintf(
+ ce.ErrorOutput,
+ "%v Unsafe CheckedEntry re-use near Entry %+v.\n",
+ ce.Time,
+ ce.Entry,
+ )
_ = ce.ErrorOutput.Sync() // ignore error
}
return
@@ -253,7 +258,12 @@ func (ce *CheckedEntry) Write(fields ...Field) {
err = multierr.Append(err, ce.cores[i].Write(ce.Entry, fields))
}
if err != nil && ce.ErrorOutput != nil {
- fmt.Fprintf(ce.ErrorOutput, "%v write error: %v\n", ce.Time, err)
+ _, _ = fmt.Fprintf(
+ ce.ErrorOutput,
+ "%v write error: %v\n",
+ ce.Time,
+ err,
+ )
_ = ce.ErrorOutput.Sync() // ignore error
}
diff --git a/vendor/go.uber.org/zap/zapcore/lazy_with.go b/vendor/go.uber.org/zap/zapcore/lazy_with.go
index 05288d6a88e..500809de08b 100644
--- a/vendor/go.uber.org/zap/zapcore/lazy_with.go
+++ b/vendor/go.uber.org/zap/zapcore/lazy_with.go
@@ -23,7 +23,8 @@ package zapcore
import "sync"
type lazyWithCore struct {
- Core
+ core Core
+ originalCore Core
sync.Once
fields []Field
}
@@ -32,23 +33,45 @@ type lazyWithCore struct {
// the logger is written to (or is further chained in a lon-lazy manner).
func NewLazyWith(core Core, fields []Field) Core {
return &lazyWithCore{
- Core: core,
- fields: fields,
+ core: nil, // core is allocated once `initOnce` is called.
+ originalCore: core,
+ fields: fields,
}
}
func (d *lazyWithCore) initOnce() {
d.Once.Do(func() {
- d.Core = d.Core.With(d.fields)
+ d.core = d.originalCore.With(d.fields)
})
}
func (d *lazyWithCore) With(fields []Field) Core {
d.initOnce()
- return d.Core.With(fields)
+ return d.core.With(fields)
}
func (d *lazyWithCore) Check(e Entry, ce *CheckedEntry) *CheckedEntry {
+ // This is safe because `lazyWithCore` doesn't change the level.
+ // So we can delagate the level check, any not `initOnce`
+ // just for the check.
+ if !d.originalCore.Enabled(e.Level) {
+ return ce
+ }
+ d.initOnce()
+ return d.core.Check(e, ce)
+}
+
+func (d *lazyWithCore) Enabled(level Level) bool {
+ // Like above, this is safe because `lazyWithCore` doesn't change the level.
+ return d.originalCore.Enabled(level)
+}
+
+func (d *lazyWithCore) Write(e Entry, fields []Field) error {
+ d.initOnce()
+ return d.core.Write(e, fields)
+}
+
+func (d *lazyWithCore) Sync() error {
d.initOnce()
- return d.Core.Check(e, ce)
+ return d.core.Sync()
}
diff --git a/vendor/go.uber.org/zap/zapcore/level.go b/vendor/go.uber.org/zap/zapcore/level.go
index e01a2413166..f3e166d67b0 100644
--- a/vendor/go.uber.org/zap/zapcore/level.go
+++ b/vendor/go.uber.org/zap/zapcore/level.go
@@ -179,19 +179,19 @@ func (l *Level) UnmarshalText(text []byte) error {
func (l *Level) unmarshalText(text []byte) bool {
switch string(text) {
- case "debug", "DEBUG":
+ case "debug":
*l = DebugLevel
- case "info", "INFO", "": // make the zero value useful
+ case "info", "": // make the zero value useful
*l = InfoLevel
- case "warn", "WARN":
+ case "warn", "warning":
*l = WarnLevel
- case "error", "ERROR":
+ case "error":
*l = ErrorLevel
- case "dpanic", "DPANIC":
+ case "dpanic":
*l = DPanicLevel
- case "panic", "PANIC":
+ case "panic":
*l = PanicLevel
- case "fatal", "FATAL":
+ case "fatal":
*l = FatalLevel
default:
return false
diff --git a/vendor/google.golang.org/api/iamcredentials/v1/iamcredentials-api.json b/vendor/google.golang.org/api/iamcredentials/v1/iamcredentials-api.json
index 7d8b3f719f1..3694e4aebb3 100644
--- a/vendor/google.golang.org/api/iamcredentials/v1/iamcredentials-api.json
+++ b/vendor/google.golang.org/api/iamcredentials/v1/iamcredentials-api.json
@@ -340,7 +340,7 @@
}
}
},
- "revision": "20250911",
+ "revision": "20251022",
"rootUrl": "https://iamcredentials.googleapis.com/",
"schemas": {
"GenerateAccessTokenRequest": {
diff --git a/vendor/google.golang.org/api/internal/version.go b/vendor/google.golang.org/api/internal/version.go
index 6646bbcbfc0..6368b1d5cc6 100644
--- a/vendor/google.golang.org/api/internal/version.go
+++ b/vendor/google.golang.org/api/internal/version.go
@@ -5,4 +5,4 @@
package internal
// Version is the current tagged release of the library.
-const Version = "0.252.0"
+const Version = "0.257.0"
diff --git a/vendor/modules.txt b/vendor/modules.txt
index af452941bcd..a831bf05f2a 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -49,7 +49,7 @@ cloud.google.com/go/storage/experimental
cloud.google.com/go/storage/internal
cloud.google.com/go/storage/internal/apiv2
cloud.google.com/go/storage/internal/apiv2/storagepb
-# github.com/Azure/azure-sdk-for-go/sdk/azcore v1.19.1
+# github.com/Azure/azure-sdk-for-go/sdk/azcore v1.20.0
## explicit; go 1.23.0
github.com/Azure/azure-sdk-for-go/sdk/azcore
github.com/Azure/azure-sdk-for-go/sdk/azcore/arm/internal/resource
@@ -71,7 +71,7 @@ github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime
github.com/Azure/azure-sdk-for-go/sdk/azcore/streaming
github.com/Azure/azure-sdk-for-go/sdk/azcore/to
github.com/Azure/azure-sdk-for-go/sdk/azcore/tracing
-# github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.12.0
+# github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1
## explicit; go 1.23.0
github.com/Azure/azure-sdk-for-go/sdk/azidentity
github.com/Azure/azure-sdk-for-go/sdk/azidentity/internal
@@ -99,7 +99,7 @@ github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/internal/shared
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/pageblob
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/sas
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/service
-# github.com/AzureAD/microsoft-authentication-library-for-go v1.5.0
+# github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0
## explicit; go 1.18
github.com/AzureAD/microsoft-authentication-library-for-go/apps/cache
github.com/AzureAD/microsoft-authentication-library-for-go/apps/confidential
@@ -611,8 +611,8 @@ github.com/google/go-cmp/cmp/internal/diff
github.com/google/go-cmp/cmp/internal/flags
github.com/google/go-cmp/cmp/internal/function
github.com/google/go-cmp/cmp/internal/value
-# github.com/google/pprof v0.0.0-20250923004556-9e5a51aed1e8
-## explicit; go 1.24.0
+# github.com/google/pprof v0.0.0-20251213031049-b05bdaca462f
+## explicit; go 1.24.9
github.com/google/pprof/profile
# github.com/google/s2a-go v0.1.9
## explicit; go 1.20
@@ -640,7 +640,7 @@ github.com/google/s2a-go/stream
# github.com/google/uuid v1.6.0
## explicit
github.com/google/uuid
-# github.com/googleapis/enterprise-certificate-proxy v0.3.6
+# github.com/googleapis/enterprise-certificate-proxy v0.3.7
## explicit; go 1.23.0
github.com/googleapis/enterprise-certificate-proxy/client
github.com/googleapis/enterprise-certificate-proxy/client/util
@@ -675,8 +675,8 @@ github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/logging
github.com/grpc-ecosystem/grpc-gateway/v2/internal/httprule
github.com/grpc-ecosystem/grpc-gateway/v2/runtime
github.com/grpc-ecosystem/grpc-gateway/v2/utilities
-# github.com/hashicorp/consul/api v1.32.0
-## explicit; go 1.22.12
+# github.com/hashicorp/consul/api v1.32.1
+## explicit; go 1.23.8
github.com/hashicorp/consul/api
# github.com/hashicorp/errwrap v1.1.0
## explicit
@@ -706,7 +706,7 @@ github.com/hashicorp/go-rootcerts
# github.com/hashicorp/go-sockaddr v1.0.7
## explicit; go 1.19
github.com/hashicorp/go-sockaddr
-# github.com/hashicorp/go-version v1.7.0
+# github.com/hashicorp/go-version v1.8.0
## explicit
github.com/hashicorp/go-version
# github.com/hashicorp/golang-lru v0.6.0
@@ -807,8 +807,8 @@ github.com/mdlayher/vsock
# github.com/metalmatze/signal v0.0.0-20210307161603-1c9aa721a97a
## explicit; go 1.14
github.com/metalmatze/signal/server/signalhttp
-# github.com/miekg/dns v1.1.68
-## explicit; go 1.23.0
+# github.com/miekg/dns v1.1.69
+## explicit; go 1.24.0
github.com/miekg/dns
# github.com/minio/crc64nvme v1.1.1
## explicit; go 1.22
@@ -874,13 +874,13 @@ github.com/oklog/ulid
# github.com/oklog/ulid/v2 v2.1.1
## explicit; go 1.15
github.com/oklog/ulid/v2
-# github.com/open-telemetry/opentelemetry-collector-contrib/internal/exp/metrics v0.139.0
+# github.com/open-telemetry/opentelemetry-collector-contrib/internal/exp/metrics v0.142.0
## explicit; go 1.24.0
github.com/open-telemetry/opentelemetry-collector-contrib/internal/exp/metrics/identity
-# github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil v0.139.0
+# github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil v0.142.0
## explicit; go 1.24.0
github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil
-# github.com/open-telemetry/opentelemetry-collector-contrib/processor/deltatocumulativeprocessor v0.139.0
+# github.com/open-telemetry/opentelemetry-collector-contrib/processor/deltatocumulativeprocessor v0.142.0
## explicit; go 1.24.0
github.com/open-telemetry/opentelemetry-collector-contrib/processor/deltatocumulativeprocessor
github.com/open-telemetry/opentelemetry-collector-contrib/processor/deltatocumulativeprocessor/internal/data
@@ -1083,8 +1083,8 @@ github.com/prometheus/otlptranslator
github.com/prometheus/procfs
github.com/prometheus/procfs/internal/fs
github.com/prometheus/procfs/internal/util
-# github.com/prometheus/prometheus v0.308.1
-## explicit; go 1.24.0
+# github.com/prometheus/prometheus v0.309.1
+## explicit; go 1.24.9
github.com/prometheus/prometheus/config
github.com/prometheus/prometheus/discovery
github.com/prometheus/prometheus/discovery/dns
@@ -1133,6 +1133,7 @@ github.com/prometheus/prometheus/util/almost
github.com/prometheus/prometheus/util/annotations
github.com/prometheus/prometheus/util/compression
github.com/prometheus/prometheus/util/convertnhcb
+github.com/prometheus/prometheus/util/features
github.com/prometheus/prometheus/util/gate
github.com/prometheus/prometheus/util/httputil
github.com/prometheus/prometheus/util/jsonutil
@@ -1416,26 +1417,26 @@ go.opencensus.io/trace/tracestate
## explicit; go 1.24.0
go.opentelemetry.io/auto/sdk
go.opentelemetry.io/auto/sdk/internal/telemetry
-# go.opentelemetry.io/collector/component v1.45.0
+# go.opentelemetry.io/collector/component v1.48.0
## explicit; go 1.24.0
go.opentelemetry.io/collector/component
-# go.opentelemetry.io/collector/confmap v1.45.0
+# go.opentelemetry.io/collector/confmap v1.48.0
## explicit; go 1.24.0
go.opentelemetry.io/collector/confmap
go.opentelemetry.io/collector/confmap/internal
go.opentelemetry.io/collector/confmap/internal/mapstructure
go.opentelemetry.io/collector/confmap/internal/third_party/composehook
-# go.opentelemetry.io/collector/confmap/xconfmap v0.139.0
+# go.opentelemetry.io/collector/confmap/xconfmap v0.142.0
## explicit; go 1.24.0
go.opentelemetry.io/collector/confmap/xconfmap
-# go.opentelemetry.io/collector/consumer v1.45.0
+# go.opentelemetry.io/collector/consumer v1.48.0
## explicit; go 1.24.0
go.opentelemetry.io/collector/consumer
go.opentelemetry.io/collector/consumer/internal
-# go.opentelemetry.io/collector/featuregate v1.45.0
+# go.opentelemetry.io/collector/featuregate v1.48.0
## explicit; go 1.24.0
go.opentelemetry.io/collector/featuregate
-# go.opentelemetry.io/collector/pdata v1.45.0
+# go.opentelemetry.io/collector/pdata v1.48.0
## explicit; go 1.24.0
go.opentelemetry.io/collector/pdata/internal
go.opentelemetry.io/collector/pdata/internal/json
@@ -1447,11 +1448,11 @@ go.opentelemetry.io/collector/pdata/plog
go.opentelemetry.io/collector/pdata/pmetric
go.opentelemetry.io/collector/pdata/pmetric/pmetricotlp
go.opentelemetry.io/collector/pdata/ptrace
-# go.opentelemetry.io/collector/pipeline v1.45.0
+# go.opentelemetry.io/collector/pipeline v1.48.0
## explicit; go 1.24.0
go.opentelemetry.io/collector/pipeline
go.opentelemetry.io/collector/pipeline/internal/globalsignal
-# go.opentelemetry.io/collector/processor v1.45.0
+# go.opentelemetry.io/collector/processor v1.48.0
## explicit; go 1.24.0
go.opentelemetry.io/collector/processor
go.opentelemetry.io/collector/processor/internal
@@ -1591,7 +1592,7 @@ go.uber.org/goleak/internal/stack
# go.uber.org/multierr v1.11.0
## explicit; go 1.19
go.uber.org/multierr
-# go.uber.org/zap v1.27.0
+# go.uber.org/zap v1.27.1
## explicit; go 1.19
go.uber.org/zap
go.uber.org/zap/buffer
@@ -1723,7 +1724,7 @@ golang.org/x/tools/internal/versions
gonum.org/v1/gonum/floats
gonum.org/v1/gonum/floats/scalar
gonum.org/v1/gonum/internal/asm/f64
-# google.golang.org/api v0.252.0
+# google.golang.org/api v0.257.0
## explicit; go 1.24.0
google.golang.org/api/googleapi
google.golang.org/api/googleapi/transport
@@ -1975,11 +1976,11 @@ gopkg.in/yaml.v2
# gopkg.in/yaml.v3 v3.0.1
## explicit
gopkg.in/yaml.v3
-# k8s.io/apimachinery v0.34.1
+# k8s.io/apimachinery v0.34.3
## explicit; go 1.24.0
k8s.io/apimachinery/pkg/util/runtime
k8s.io/apimachinery/pkg/util/sets
-# k8s.io/client-go v0.34.1
+# k8s.io/client-go v0.34.3
## explicit; go 1.24.0
k8s.io/client-go/tools/metrics
k8s.io/client-go/util/workqueue