diff --git a/go.mod b/go.mod index 57470ac4711..6c827856806 100644 --- a/go.mod +++ b/go.mod @@ -91,7 +91,7 @@ require ( go.opentelemetry.io/otel/trace v1.34.0 go.uber.org/multierr v1.11.0 golang.org/x/term v0.29.0 - google.golang.org/api v0.218.0 + google.golang.org/api v0.219.0 google.golang.org/protobuf v1.36.5 sigs.k8s.io/kustomize/kyaml v0.18.1 ) @@ -133,12 +133,12 @@ require ( github.com/minio/crc64nvme v1.0.1 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect - github.com/open-telemetry/opentelemetry-collector-contrib/internal/exp/metrics v0.116.0 // indirect - github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil v0.116.0 // indirect - github.com/open-telemetry/opentelemetry-collector-contrib/processor/deltatocumulativeprocessor v0.116.0 // indirect + github.com/open-telemetry/opentelemetry-collector-contrib/internal/exp/metrics v0.118.0 // indirect + github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil v0.118.0 // indirect + github.com/open-telemetry/opentelemetry-collector-contrib/processor/deltatocumulativeprocessor v0.118.0 // indirect github.com/pires/go-proxyproto v0.7.0 // indirect github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect - github.com/prometheus/sigv4 v0.1.1 // indirect + github.com/prometheus/sigv4 v0.1.2 // indirect github.com/ryanuber/go-glob v1.0.0 // indirect github.com/shopspring/decimal v1.2.0 // indirect github.com/spf13/cast v1.5.0 // indirect @@ -298,7 +298,7 @@ require ( sigs.k8s.io/yaml v1.4.0 // indirect ) -replace github.com/prometheus/prometheus => github.com/grafana/mimir-prometheus v0.0.0-20250306234455-f6f6f2cceada +replace github.com/prometheus/prometheus => github.com/grafana/mimir-prometheus v0.0.0-20250307031102-9ba23e267c3d // Replace memberlist with our fork which includes some fixes that haven't been // merged upstream yet: diff --git a/go.sum b/go.sum index a0b20952eaf..e41e021c544 100644 --- a/go.sum +++ b/go.sum @@ -957,14 +957,14 @@ github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczC github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= -github.com/digitalocean/godo v1.132.0 h1:n0x6+ZkwbyQBtIU1wwBhv26EINqHg0wWQiBXlwYg/HQ= -github.com/digitalocean/godo v1.132.0/go.mod h1:PU8JB6I1XYkQIdHFop8lLAY9ojp6M0XcU0TWaQSxbrc= +github.com/digitalocean/godo v1.136.0 h1:DTxugljFJSMBPfEGq4KeXpnKeAHicggNqogcrw/YdZw= +github.com/digitalocean/godo v1.136.0/go.mod h1:PU8JB6I1XYkQIdHFop8lLAY9ojp6M0XcU0TWaQSxbrc= github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0= github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/dlclark/regexp2 v1.11.4 h1:rPYF9/LECdNymJufQKmri9gV604RvvABwgOA8un7yAo= github.com/dlclark/regexp2 v1.11.4/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= -github.com/docker/docker v27.4.1+incompatible h1:ZJvcY7gfwHn1JF48PfbyXg7Jyt9ZCWDW+GGXOIxEwp4= -github.com/docker/docker v27.4.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v27.5.1+incompatible h1:4PYU5dnBYqRQi0294d1FBECqT9ECWeQAIfE8q4YnPY8= +github.com/docker/docker v27.5.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.4.1-0.20210727194412-58542c764a11 h1:IPrmumsT9t5BS7XcPhgsCTlkWbYg80SEXUzDpReaU6Y= github.com/docker/go-connections v0.4.1-0.20210727194412-58542c764a11/go.mod h1:a6bNUGTbQBsY6VRHTr4h/rkOXjl244DyRD0tx3fgq4Q= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= @@ -1284,8 +1284,8 @@ github.com/grafana/gomemcache v0.0.0-20250228145437-da7b95fd2ac1 h1:vR5nELq+KtGO github.com/grafana/gomemcache v0.0.0-20250228145437-da7b95fd2ac1/go.mod h1:j/s0jkda4UXTemDs7Pgw/vMT06alWc42CHisvYac0qw= github.com/grafana/memberlist v0.3.1-0.20220714140823-09ffed8adbbe h1:yIXAAbLswn7VNWBIvM71O2QsgfgW9fRXZNR0DXe6pDU= github.com/grafana/memberlist v0.3.1-0.20220714140823-09ffed8adbbe/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= -github.com/grafana/mimir-prometheus v0.0.0-20250306234455-f6f6f2cceada h1:8MLoP1fblwE72Bk4G66nmhXwoHDcpHQcfjrC+kLoXAg= -github.com/grafana/mimir-prometheus v0.0.0-20250306234455-f6f6f2cceada/go.mod h1:jC5V3PuoN3nxpvsvZipB+iOf6H/Np1uW+e3r9TTxJMA= +github.com/grafana/mimir-prometheus v0.0.0-20250307031102-9ba23e267c3d h1:62ia0+nFFVwSKKpza8GZhK1dzZN8wDbt3uzaaXAicJQ= +github.com/grafana/mimir-prometheus v0.0.0-20250307031102-9ba23e267c3d/go.mod h1:9Hf/cSfGXNKRswdYnj10nTOt5LUmazcJO2tVsg02+Jo= github.com/grafana/opentracing-contrib-go-stdlib v0.0.0-20230509071955-f410e79da956 h1:em1oddjXL8c1tL0iFdtVtPloq2hRPen2MJQKoAWpxu0= github.com/grafana/opentracing-contrib-go-stdlib v0.0.0-20230509071955-f410e79da956/go.mod h1:qtI1ogk+2JhVPIXVc6q+NHziSmy2W5GbdQZFUHADCBU= github.com/grafana/prometheus-alertmanager v0.25.1-0.20250211112812-e32be5e2a455 h1:yidC1xzk4fedLZ/iXEqSJopkw3jPZPwoMqqzue4eFEA= @@ -1376,8 +1376,8 @@ github.com/hashicorp/vault/api/auth/kubernetes v0.8.0 h1:6jPcORq7OHwf+MCbaaUmiBv github.com/hashicorp/vault/api/auth/kubernetes v0.8.0/go.mod h1:nfl5sRUUork0ZSfV3xf+pgAFQSD5kSkL0k9axg523DM= github.com/hashicorp/vault/api/auth/userpass v0.8.0 h1:JFFzMld+VO/S1v8HQNJzcy+3o+xfx/iH49dsiQ1G5jk= github.com/hashicorp/vault/api/auth/userpass v0.8.0/go.mod h1:+XbsSnbbyo+yjySfKcIsyl28kO4C/c4Czo7og0XCtUo= -github.com/hetznercloud/hcloud-go/v2 v2.18.0 h1:BemrVGeWI8Kn/pvaC1jBsHZxQMnRqOydS7Ju4BERB4Q= -github.com/hetznercloud/hcloud-go/v2 v2.18.0/go.mod h1:r5RTzv+qi8IbLcDIskTzxkFIji7Ovc8yNgepQR9M+UA= +github.com/hetznercloud/hcloud-go/v2 v2.19.0 h1:crqbWMywudvlPLLczFf2hBpTPIATjrWMmwfiKSTpUt0= +github.com/hetznercloud/hcloud-go/v2 v2.19.0/go.mod h1:r5RTzv+qi8IbLcDIskTzxkFIji7Ovc8yNgepQR9M+UA= 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/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= @@ -1463,8 +1463,8 @@ github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0 github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= -github.com/linode/linodego v1.46.0 h1:+uOG4SD2MIrhbrLrvOD5HrbdLN3D19Wgn3MgdUNQjeU= -github.com/linode/linodego v1.46.0/go.mod h1:vyklQRzZUWhFVBZdYx4dcYJU/gG9yKB9VUcUs6ub0Lk= +github.com/linode/linodego v1.47.0 h1:6MFNCyzWbr8Rhl4r7d5DwZLwxvFIsM4ARH6W0KS/R0U= +github.com/linode/linodego v1.47.0/go.mod h1:vyklQRzZUWhFVBZdYx4dcYJU/gG9yKB9VUcUs6ub0Lk= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= github.com/lyft/protoc-gen-star v0.6.0/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA= @@ -1566,14 +1566,14 @@ 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.24.0 h1:+0glovB9Jd6z3VR+ScSwQqXVTIfJcGA9UBM8yzQxhqg= github.com/onsi/gomega v1.24.0/go.mod h1:Z/NWtiqwBrwUt4/2loMmHL63EDLnYHmVbuBpDr2vQAg= -github.com/open-telemetry/opentelemetry-collector-contrib/internal/exp/metrics v0.116.0 h1:Kxk5Ral+Dc6VB9UmTketVjs+rbMZP8JxQ4SXDx4RivQ= -github.com/open-telemetry/opentelemetry-collector-contrib/internal/exp/metrics v0.116.0/go.mod h1:ctT6oQmGmWGGGgUIKyx2fDwqz77N9+04gqKkDyAzKCg= -github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatatest v0.116.0 h1:RlEK9MbxWyBHbLel8EJ1L7DbYVLai9dZL6Ljl2cBgyA= -github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatatest v0.116.0/go.mod h1:AVUEyIjPb+0ARr7mhIkZkdNg3fd0ZcRhzAi53oZhl1Q= -github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil v0.116.0 h1:jwnZYRBuPJnsKXE5H6ZvTEm91bXW5VP8+tLewzl54eg= -github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil v0.116.0/go.mod h1:NT3Ag+DdnIAZQfD7l7OHwlYqnaAJ19SoPZ0nhD9yx4s= -github.com/open-telemetry/opentelemetry-collector-contrib/processor/deltatocumulativeprocessor v0.116.0 h1:ZBmLuipJv7BT9fho/2yAFsS8AtMsCOCe4ON8oqkX3n8= -github.com/open-telemetry/opentelemetry-collector-contrib/processor/deltatocumulativeprocessor v0.116.0/go.mod h1:f0GdYWGxUunyRZ088gHnoX78pc/gZc3dQlRtidiGXzg= +github.com/open-telemetry/opentelemetry-collector-contrib/internal/exp/metrics v0.118.0 h1:PbknCwTbeTz8GNSfN4fOIp50YCDO19s1IAp6PGFcdpA= +github.com/open-telemetry/opentelemetry-collector-contrib/internal/exp/metrics v0.118.0/go.mod h1:cOY+YDFtxJH3eQzJDObvWFFSIvD2AstG5MZ9t8wqusQ= +github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatatest v0.118.0 h1:DSoYrOjLv23HXpx72hl61br4ZZTj6dqtwZSGoypKWIA= +github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatatest v0.118.0/go.mod h1:nR+r7aAbsktscJk4fGmzljblbZBMaiZcIWeKbXV+HmY= +github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil v0.118.0 h1:aUTSkzJExtrlHN32g8hX/cRNEo2ZmucPg+vwPqOYvhg= +github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil v0.118.0/go.mod h1:a3sewj4nEozMwcNwZTHPzddS+1BnA6BaAkO/CRIGHVU= +github.com/open-telemetry/opentelemetry-collector-contrib/processor/deltatocumulativeprocessor v0.118.0 h1:RZszYLp7sVMOD1rppjY+fP2PQh5qNAh5U6RoQNvd4Rg= +github.com/open-telemetry/opentelemetry-collector-contrib/processor/deltatocumulativeprocessor v0.118.0/go.mod h1:5i928mwS+Ojv41l3/IxcyK1SCy6WnpL3wjLWKDb4YKQ= 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-rc2 h1:2zx/Stx4Wc5pIPDvIxHXvXtQFW/7XWJGmnM7r3wg034= @@ -1651,8 +1651,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.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= -github.com/prometheus/sigv4 v0.1.1 h1:UJxjOqVcXctZlwDjpUpZ2OiMWJdFijgSofwLzO1Xk0Q= -github.com/prometheus/sigv4 v0.1.1/go.mod h1:RAmWVKqx0bwi0Qm4lrKMXFM0nhpesBcenfCtz9qRyH8= +github.com/prometheus/sigv4 v0.1.2 h1:R7570f8AoM5YnTUPFm3mjZH5q2k4D+I/phCWvZ4PXG8= +github.com/prometheus/sigv4 v0.1.2/go.mod h1:GF9fwrvLgkQwDdQ5BXeV9XUSCH/IPNqzvAoaohfjqMU= github.com/rainycape/unidecode v0.0.0-20150907023854-cb7f23ec59be h1:ta7tUOvsPHVHGom5hKW5VXNc2xZIkfCKP8iaqOyYtUQ= github.com/rainycape/unidecode v0.0.0-20150907023854-cb7f23ec59be/go.mod h1:MIDFMn7db1kT65GmV94GzpX9Qdi7N/pQlwb+AN8wh+Q= github.com/redis/go-redis/v9 v9.7.0 h1:HhLSs+B6O021gwzl+locl0zEDnyNkxMtf/Z3NNBMa9E= @@ -1816,8 +1816,8 @@ go.opentelemetry.io/collector/component/componenttest v0.118.0 h1:knEHckoiL2fEWS go.opentelemetry.io/collector/component/componenttest v0.118.0/go.mod h1:aHc7t7zVwCpbhrWIWY+GMuaMxMCUP8C8P7pJOt8r/vU= go.opentelemetry.io/collector/config/configtelemetry v0.118.0 h1:UlN46EViG2X42odWtXgWaqY7Y01ZKpsnswSwXTWx5mM= go.opentelemetry.io/collector/config/configtelemetry v0.118.0/go.mod h1:SlBEwQg0qly75rXZ6W1Ig8jN25KBVBkFIIAUI1GiAAE= -go.opentelemetry.io/collector/confmap v1.22.0 h1:ZKQzRuj5lKu+seKArAAZ1yPRroDPricaIVIREm/jr3w= -go.opentelemetry.io/collector/confmap v1.22.0/go.mod h1:Rrhs+MWoaP6AswZp+ReQ2VO9dfOfcUjdjiSHBsG+nec= +go.opentelemetry.io/collector/confmap v1.24.0 h1:UUHVhkDCsVw14jPOarug9PDQE2vaB2ELPWMr7ARFBCA= +go.opentelemetry.io/collector/confmap v1.24.0/go.mod h1:Rrhs+MWoaP6AswZp+ReQ2VO9dfOfcUjdjiSHBsG+nec= go.opentelemetry.io/collector/consumer v1.24.0 h1:7DeyBm9qdr1EPuCfPjWyChPK16DbVc0wZeSa9LZprFU= go.opentelemetry.io/collector/consumer v1.24.0/go.mod h1:0G6jvZprIp4dpKMD1ZxCjriiP9GdFvFMObsQEtTk71s= go.opentelemetry.io/collector/consumer/consumertest v0.118.0 h1:8AAS9ejQapP1zqt0+cI6u+AUBheT3X0171N9WtXWsVY= @@ -2400,8 +2400,8 @@ google.golang.org/api v0.122.0/go.mod h1:gcitW0lvnyWjSp9nKxAbdHKIZ6vF4aajGueeslZ google.golang.org/api v0.124.0/go.mod h1:xu2HQurE5gi/3t1aFCvhPD781p0a3p11sdunTJ2BlP4= google.golang.org/api v0.125.0/go.mod h1:mBwVAtz+87bEN6CbA1GtZPDOqY2R5ONPqJeIlvyo4Aw= google.golang.org/api v0.126.0/go.mod h1:mBwVAtz+87bEN6CbA1GtZPDOqY2R5ONPqJeIlvyo4Aw= -google.golang.org/api v0.218.0 h1:x6JCjEWeZ9PFCRe9z0FBrNwj7pB7DOAqT35N+IPnAUA= -google.golang.org/api v0.218.0/go.mod h1:5VGHBAkxrA/8EFjLVEYmMUJ8/8+gWWQ3s4cFH0FxG2M= +google.golang.org/api v0.219.0 h1:nnKIvxKs/06jWawp2liznTBnMRQBEPpGo7I+oEypTX0= +google.golang.org/api v0.219.0/go.mod h1:K6OmjGm+NtLrIkHxv1U3a0qIf/0JOvAHd5O/6AoyKYE= 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= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= diff --git a/pkg/streamingpromql/engine_test.go b/pkg/streamingpromql/engine_test.go index 6c2a6171b17..241fd13c8e1 100644 --- a/pkg/streamingpromql/engine_test.go +++ b/pkg/streamingpromql/engine_test.go @@ -394,7 +394,7 @@ func TestRangeVectorSelectors(t *testing.T) { expected *promql.Result ts time.Time }{ - "matches series with points in range": { + "floats: matches series with points in range": { expr: "some_metric[1m1s]", ts: baseT.Add(2 * time.Minute), expected: &promql.Result{ @@ -416,21 +416,21 @@ func TestRangeVectorSelectors(t *testing.T) { }, }, }, - "matches no series": { + "floats: matches no series": { expr: "some_nonexistent_metric[1m]", ts: baseT, expected: &promql.Result{ Value: promql.Matrix{}, }, }, - "no samples in range": { + "floats: no samples in range": { expr: "some_metric[1m]", ts: baseT.Add(20 * time.Minute), expected: &promql.Result{ Value: promql.Matrix{}, }, }, - "does not return points outside range if last selected point does not align to end of range": { + "floats: does not return points outside range if last selected point does not align to end of range": { expr: "some_metric_with_gaps[1m1s]", ts: baseT.Add(2 * time.Minute), expected: &promql.Result{ @@ -444,7 +444,7 @@ func TestRangeVectorSelectors(t *testing.T) { }, }, }, - "metric with stale marker": { + "floats: metric with stale marker": { expr: "some_metric_with_stale_marker[3m1s]", ts: baseT.Add(3 * time.Minute), expected: &promql.Result{ @@ -460,7 +460,14 @@ func TestRangeVectorSelectors(t *testing.T) { }, }, }, - "histogram: matches series with points in range": { + "floats: 0 length range": { + expr: "some_metric[0]", + ts: baseT.Add(2 * time.Minute), + expected: &promql.Result{ + Value: promql.Matrix{}, + }, + }, + "histograms: matches series with points in range": { expr: "incr_histogram[1m1s]", ts: baseT.Add(2 * time.Minute), expected: &promql.Result{ @@ -546,14 +553,14 @@ func TestRangeVectorSelectors(t *testing.T) { }, }, }, - "histogram: no samples in range": { + "histograms: no samples in range": { expr: "incr_histogram[1m]", ts: baseT.Add(20 * time.Minute), expected: &promql.Result{ Value: promql.Matrix{}, }, }, - "histogram: does not return points outside range if last selected point does not align to end of range": { + "histograms: does not return points outside range if last selected point does not align to end of range": { expr: "histogram_with_gaps[1m1s]", ts: baseT.Add(2 * time.Minute), expected: &promql.Result{ @@ -583,7 +590,7 @@ func TestRangeVectorSelectors(t *testing.T) { }, }, }, - "histogram: metric with stale marker": { + "histograms: metric with stale marker": { expr: "histogram_with_stale_marker[3m1s]", ts: baseT.Add(3 * time.Minute), expected: &promql.Result{ @@ -647,6 +654,13 @@ func TestRangeVectorSelectors(t *testing.T) { }, }, }, + "histograms: 0 length range": { + expr: "incr_histogram[0]", + ts: baseT.Add(2 * time.Minute), + expected: &promql.Result{ + Value: promql.Matrix{}, + }, + }, "mixed series with histograms and floats": { expr: "mixed_metric[4m]", ts: baseT.Add(4 * time.Minute), @@ -1083,6 +1097,13 @@ func TestSubqueries(t *testing.T) { }, Start: time.Unix(35, 0), }, + { + Query: "metric[0:5s]", + Result: promql.Result{ + Value: promql.Matrix{}, + }, + Start: time.Unix(10, 0), + }, { // Normal selector. Query: `http_requests{group=~"pro.*",instance="0"}[30s:10s]`, Result: promql.Result{ diff --git a/pkg/streamingpromql/functions.go b/pkg/streamingpromql/functions.go index ed413bcdb69..4630cdae0d3 100644 --- a/pkg/streamingpromql/functions.go +++ b/pkg/streamingpromql/functions.go @@ -541,8 +541,9 @@ func SortOperatorFactory(descending bool) InstantVectorFunctionOperatorFactory { } if timeRange.StepCount != 1 { - // If this is a range query, sort / sort_desc have no effect, so we might as well just skip straight to the inner operator. - return inner, nil + // If this is a range query, sort / sort_desc does not reorder series, but does drop all histograms like it would for an instant query. + f := functions.FunctionOverInstantVectorDefinition{SeriesDataFunc: functions.DropHistograms} + return functions.NewFunctionOverInstantVector(inner, nil, memoryConsumptionTracker, f, expressionPosition, timeRange), nil } return functions.NewSort(inner, descending, memoryConsumptionTracker, expressionPosition), nil diff --git a/pkg/streamingpromql/operators/functions/common.go b/pkg/streamingpromql/operators/functions/common.go index 191b8e597dc..954c0d1b640 100644 --- a/pkg/streamingpromql/operators/functions/common.go +++ b/pkg/streamingpromql/operators/functions/common.go @@ -48,6 +48,12 @@ func FloatTransformationDropHistogramsFunc(transform func(f float64) float64) In } } +func DropHistograms(seriesData types.InstantVectorSeriesData, _ []types.ScalarData, _ types.QueryTimeRange, memoryConsumptionTracker *limiting.MemoryConsumptionTracker) (types.InstantVectorSeriesData, error) { + types.HPointSlicePool.Put(seriesData.Histograms, memoryConsumptionTracker) + seriesData.Histograms = nil + return seriesData, nil +} + func PassthroughData(seriesData types.InstantVectorSeriesData, _ []types.ScalarData, _ types.QueryTimeRange, _ *limiting.MemoryConsumptionTracker) (types.InstantVectorSeriesData, error) { return seriesData, nil } diff --git a/pkg/streamingpromql/operators/functions/label.go b/pkg/streamingpromql/operators/functions/label.go index 460f204af4c..1ea9281bacd 100644 --- a/pkg/streamingpromql/operators/functions/label.go +++ b/pkg/streamingpromql/operators/functions/label.go @@ -66,8 +66,7 @@ func LabelReplaceFactory(dstLabelOp, replacementOp, srcLabelOp, regexOp types.St return nil, fmt.Errorf("invalid regular expression in label_replace(): %s", regexStr) } dst := dstLabelOp.GetValue() - // TODO(jhesketh): Use UTF-8 validation (model.LabelName(src).IsValid()) when https://github.com/prometheus/prometheus/pull/15974 is vendored in. - if !model.LabelNameRE.MatchString(dst) { + if !model.LabelName(dst).IsValid() { return nil, fmt.Errorf("invalid destination label name in label_replace(): %s", dst) } repl := replacementOp.GetValue() diff --git a/pkg/streamingpromql/operators/functions/sort.go b/pkg/streamingpromql/operators/functions/sort.go index 6b4a188eba6..e5cad64fe91 100644 --- a/pkg/streamingpromql/operators/functions/sort.go +++ b/pkg/streamingpromql/operators/functions/sort.go @@ -55,7 +55,11 @@ func (s *Sort) SeriesMetadata(ctx context.Context) ([]types.SeriesMetadata, erro return nil, err } - pointCount := len(d.Floats) + len(d.Histograms) + // sort() and sort_desc() ignore histograms. + types.HPointSlicePool.Put(d.Histograms, s.MemoryConsumptionTracker) + d.Histograms = nil + + pointCount := len(d.Floats) if pointCount > 1 { return nil, fmt.Errorf("expected series %v to have at most one point, but it had %v", allSeries[idx], pointCount) @@ -136,10 +140,6 @@ func getValueForSorting(allData []types.InstantVectorSeriesData, seriesIdx int) return series.Floats[0].F } - if len(series.Histograms) == 1 { - return series.Histograms[0].H.Sum - } - return 0 // The value we use for empty series doesn't matter, as long as we're consistent: we'll still return an empty set of data in NextSeries(). } diff --git a/pkg/streamingpromql/testdata/ours-only/functions.test b/pkg/streamingpromql/testdata/ours-only/functions.test index b4fbc1ed323..dcadd2059ed 100644 --- a/pkg/streamingpromql/testdata/ours-only/functions.test +++ b/pkg/streamingpromql/testdata/ours-only/functions.test @@ -5,15 +5,3 @@ load 6m # Currently prometheus does not merge series: https://github.com/prometheus/prometheus/issues/15114 eval range from 0 to 6m step 6m label_replace(series, "idx", "replaced", "idx", ".*") series{label="a", idx="replaced"} 2 4 - -clear - -# label_join() tests -load 5m - dup{label="a", this="a"} 1.0 - dup{label="b", this="a"} 1.0 - -# Prometheus fails this with enableDelayedNameRemoval disabled -# Can be tested against both once https://github.com/prometheus/prometheus/pull/15975 is merged -eval_fail range from 0 to 10m step 5m label_join(dup, "label", "", "this") - expected_fail_message vector cannot contain metrics with the same labelset diff --git a/pkg/streamingpromql/testdata/ours/functions.test b/pkg/streamingpromql/testdata/ours/functions.test index 9364efceb5a..3646a57838d 100644 --- a/pkg/streamingpromql/testdata/ours/functions.test +++ b/pkg/streamingpromql/testdata/ours/functions.test @@ -935,26 +935,14 @@ load 1m test_metric{case="histogram with -Inf"} {{count:0 sum:-Inf}} {{count:0 sum:-Inf}} {{count:0 sum:-Inf}} {{count:0 sum:-Inf}} {{count:0 sum:-Inf}} # Sorting of identical values is not stable, so we exclude those from these test cases and check them below. -eval_ordered instant at 1m sort(test_metric{case!~".*(NaN|Inf)"}) - test_metric{case="histogram 2"} {{count:20 sum:5}} - test_metric{case="float 1"} 10 - test_metric{case="histogram 1"} {{count:0 sum:12}} - test_metric{case="float 2"} 15 - -eval_ordered instant at 1m sort_desc(test_metric{case!~".*(NaN|Inf)"}) - test_metric{case="float 2"} 15 - test_metric{case="histogram 1"} {{count:0 sum:12}} - test_metric{case="float 1"} 10 - test_metric{case="histogram 2"} {{count:20 sum:5}} - -eval_ordered instant at 1m sort(test_metric{case=~"float.*"}) +eval_ordered instant at 1m sort(test_metric) test_metric{case="float with -Inf"} -Inf test_metric{case="float 1"} 10 test_metric{case="float 2"} 15 test_metric{case="float with +Inf"} +Inf test_metric{case="float with NaN"} NaN -eval_ordered instant at 1m sort_desc(test_metric{case=~"float.*"}) +eval_ordered instant at 1m sort_desc(test_metric) test_metric{case="float with +Inf"} +Inf test_metric{case="float 2"} 15 test_metric{case="float 1"} 10 @@ -962,36 +950,23 @@ eval_ordered instant at 1m sort_desc(test_metric{case=~"float.*"}) test_metric{case="float with NaN"} NaN eval_ordered instant at 1m sort(test_metric{case=~"histogram.*"}) - test_metric{case="histogram with -Inf"} {{count:0 sum:-Inf}} - test_metric{case="histogram 2"} {{count:20 sum:5}} - test_metric{case="histogram 1"} {{count:0 sum:12}} - test_metric{case="histogram with +Inf"} {{count:0 sum:Inf}} - test_metric{case="histogram with NaN"} {{count:0 sum:NaN}} + # Returns no results: sort() ignores histograms. eval_ordered instant at 1m sort_desc(test_metric{case=~"histogram.*"}) - test_metric{case="histogram with +Inf"} {{count:0 sum:Inf}} - test_metric{case="histogram 1"} {{count:0 sum:12}} - test_metric{case="histogram 2"} {{count:20 sum:5}} - test_metric{case="histogram with -Inf"} {{count:0 sum:-Inf}} - test_metric{case="histogram with NaN"} {{count:0 sum:NaN}} + # Returns no results: sort_desc() ignores histograms. # Test the case where some series have no sample at all. eval_ordered instant at 1m sort(test_metric{case=~"float.*"} > 11) test_metric{case="float 2"} 15 test_metric{case="float with +Inf"} +Inf -# sort / sort_desc do nothing for range queries. +# sort / sort_desc do nothing except drop histograms for range queries. eval range from 0 to 4m step 1m sort(test_metric) test_metric{case="float 1"} 0+10x4 test_metric{case="float 2"} 0+15x4 test_metric{case="float with NaN"} NaN NaN NaN NaN NaN test_metric{case="float with +Inf"} +Inf +Inf +Inf +Inf +Inf test_metric{case="float with -Inf"} -Inf -Inf -Inf -Inf -Inf - test_metric{case="histogram 1"} {{count:0 sum:12}} {{count:0 sum:12}} {{count:0 sum:12}} {{count:0 sum:12}} {{count:0 sum:12}} - test_metric{case="histogram 2"} {{count:20 sum:5}} {{count:20 sum:5}} {{count:20 sum:5}} {{count:20 sum:5}} {{count:20 sum:5}} - test_metric{case="histogram with NaN"} {{count:0 sum:NaN}} {{count:0 sum:NaN}} {{count:0 sum:NaN}} {{count:0 sum:NaN}} {{count:0 sum:NaN}} - test_metric{case="histogram with +Inf"} {{count:0 sum:Inf}} {{count:0 sum:Inf}} {{count:0 sum:Inf}} {{count:0 sum:Inf}} {{count:0 sum:Inf}} - test_metric{case="histogram with -Inf"} {{count:0 sum:-Inf}} {{count:0 sum:-Inf}} {{count:0 sum:-Inf}} {{count:0 sum:-Inf}} {{count:0 sum:-Inf}} eval range from 0 to 4m step 1m sort_desc(test_metric) test_metric{case="float 1"} 0+10x4 @@ -999,11 +974,6 @@ eval range from 0 to 4m step 1m sort_desc(test_metric) test_metric{case="float with NaN"} NaN NaN NaN NaN NaN test_metric{case="float with +Inf"} +Inf +Inf +Inf +Inf +Inf test_metric{case="float with -Inf"} -Inf -Inf -Inf -Inf -Inf - test_metric{case="histogram 1"} {{count:0 sum:12}} {{count:0 sum:12}} {{count:0 sum:12}} {{count:0 sum:12}} {{count:0 sum:12}} - test_metric{case="histogram 2"} {{count:20 sum:5}} {{count:20 sum:5}} {{count:20 sum:5}} {{count:20 sum:5}} {{count:20 sum:5}} - test_metric{case="histogram with NaN"} {{count:0 sum:NaN}} {{count:0 sum:NaN}} {{count:0 sum:NaN}} {{count:0 sum:NaN}} {{count:0 sum:NaN}} - test_metric{case="histogram with +Inf"} {{count:0 sum:Inf}} {{count:0 sum:Inf}} {{count:0 sum:Inf}} {{count:0 sum:Inf}} {{count:0 sum:Inf}} - test_metric{case="histogram with -Inf"} {{count:0 sum:-Inf}} {{count:0 sum:-Inf}} {{count:0 sum:-Inf}} {{count:0 sum:-Inf}} {{count:0 sum:-Inf}} clear @@ -1148,4 +1118,4 @@ eval range from 0s to 66m step 6m absent(histo{case="a"}) eval range from 0s to 66m step 6m absent(histo{case=~"(0|a)"}) {} 1 -clear \ No newline at end of file +clear diff --git a/pkg/streamingpromql/testdata/upstream/functions.test b/pkg/streamingpromql/testdata/upstream/functions.test index 0db3f6957bb..123d65b3985 100644 --- a/pkg/streamingpromql/testdata/upstream/functions.test +++ b/pkg/streamingpromql/testdata/upstream/functions.test @@ -223,6 +223,13 @@ clear load 5m http_requests_total{path="/foo"} 0+10x10 http_requests_total{path="/bar"} 0+10x5 0+10x5 + http_requests_histogram{path="/a"} {{sum:2 count:2}}+{{sum:3 count:3}}x5 + http_requests_histogram{path="/b"} 0 0 {{sum:1 count:1}} {{sum:4 count:4}} + http_requests_histogram{path="/c"} 0 0 {{sum:1 count:1}} {{sum:4 count:4 counter_reset_hint:gauge}} + http_requests_histogram{path="/d"} 0 0 {{sum:1 count:1 counter_reset_hint:gauge}} {{sum:4 count:4}} + http_requests_histogram{path="/e"} 0 1 2 {{sum:4 count:4}} + http_requests_histogram{path="/f"} 0 0 {{sum:1 count:1}} {{schema:-53 sum:3 count:3 custom_values:[5 10] buckets:[3]}} + http_requests_histogram{path="/g"} 0 0 {{schema:-53 sum:3 count:3 custom_values:[1] buckets:[3]}} {{schema:-53 sum:3 count:3 custom_values:[5 10] buckets:[3]}} eval instant at 50m irate(http_requests_total[50m]) {path="/foo"} .03333333333333333333 @@ -233,6 +240,28 @@ eval instant at 30m irate(http_requests_total[50m]) {path="/foo"} .03333333333333333333 {path="/bar"} 0 +eval instant at 20m irate(http_requests_histogram{path="/a"}[20m]) + {path="/a"} {{sum:0.01 count:0.01 counter_reset_hint:gauge}} + +eval instant at 20m irate(http_requests_histogram{path="/b"}[20m]) + {path="/b"} {{sum:0.01 count:0.01 counter_reset_hint:gauge}} + +eval instant at 20m irate(http_requests_histogram{path="/b"}[6m]) + +eval_warn instant at 20m irate(http_requests_histogram{path="/c"}[20m]) + {path="/c"} {{sum:0.01 count:0.01 counter_reset_hint:gauge}} + +eval_warn instant at 20m irate(http_requests_histogram{path="/d"}[20m]) + {path="/d"} {{sum:0.01 count:0.01 counter_reset_hint:gauge}} + +eval_warn instant at 20m irate(http_requests_histogram{path="/e"}[20m]) + +eval instant at 20m irate(http_requests_histogram{path="/f"}[20m]) + {path="/f"} {{schema:-53 sum:0.01 count:0.01 custom_values:[5 10] buckets:[0.01]}} + +eval instant at 20m irate(http_requests_histogram{path="/g"}[20m]) + {path="/g"} {{schema:-53 sum:0.01 count:0.01 custom_values:[5 10] buckets:[0.01]}} + clear # Tests for delta(). @@ -264,11 +293,38 @@ clear load 5m http_requests{path="/foo"} 0 50 100 150 http_requests{path="/bar"} 0 50 100 50 + http_requests_histogram{path="/a"} {{sum:2 count:2 counter_reset_hint:gauge}}+{{sum:1 count:3 counter_reset_hint:gauge}}x5 + http_requests_histogram{path="/b"} 0 0 {{sum:1 count:1 counter_reset_hint:gauge}} {{sum:2 count:2 counter_reset_hint:gauge}} + http_requests_histogram{path="/c"} 0 0 {{sum:1 count:1}} {{sum:2 count:2 counter_reset_hint:gauge}} + http_requests_histogram{path="/d"} 0 0 {{sum:1 count:1 counter_reset_hint:gauge}} {{sum:2 count:2}} + http_requests_histogram{path="/e"} 0 1 2 {{sum:1 count:2 counter_reset_hint:gauge}} + http_requests_histogram{path="/f"} 0 0 {{sum:1 count:1 counter_reset_hint:gauge}} {{schema:-53 sum:1 count:1 custom_values:[5 10] buckets:[1] counter_reset_hint:gauge}} + http_requests_histogram{path="/g"} 0 0 {{schema:-53 sum:1 count:1 custom_values:[1] buckets:[2] counter_reset_hint:gauge}} {{schema:-53 sum:1 count:1 custom_values:[5 10] buckets:[1] counter_reset_hint:gauge}} eval instant at 20m idelta(http_requests[20m]) {path="/foo"} 50 {path="/bar"} -50 +eval instant at 20m idelta(http_requests_histogram{path="/a"}[20m]) + {path="/a"} {{sum:1 count:3 counter_reset_hint:gauge}} + +eval instant at 20m idelta(http_requests_histogram{path="/b"}[20m]) + {path="/b"} {{sum:1 count:1 counter_reset_hint:gauge}} + +eval instant at 20m idelta(http_requests_histogram{path="/b"}[6m]) + +eval_warn instant at 20m idelta(http_requests_histogram{path="/c"}[20m]) + {path="/c"} {{sum:1 count:1 counter_reset_hint:gauge}} + +eval_warn instant at 20m idelta(http_requests_histogram{path="/d"}[20m]) + {path="/d"} {{sum:1 count:1 counter_reset_hint:gauge}} + +eval_warn instant at 20m idelta(http_requests_histogram{path="/e"}[20m]) + +eval_warn instant at 20m idelta(http_requests_histogram{path="/f"}[20m]) + +eval_warn instant at 20m idelta(http_requests_histogram{path="/g"}[20m]) + clear # Tests for deriv() and predict_linear(). @@ -415,7 +471,7 @@ eval instant at 0m label_replace(testmetric, "dst", "", "dst", ".*") eval_fail instant at 0m label_replace(testmetric, "dst", "value-$1", "src", "(.*") # label_replace fails when the destination label name is not a valid Prometheus label name. -eval_fail instant at 0m label_replace(testmetric, "invalid-label-name", "", "src", "(.*)") +eval_fail instant at 0m label_replace(testmetric, "\xff", "", "src", "(.*)") # label_replace fails when there would be duplicated identical output label sets. eval_fail instant at 0m label_replace(testmetric, "src", "", "", "") @@ -448,6 +504,8 @@ eval instant at 20s timestamp(metric) load 5m testmetric{src="a",src1="b",src2="c",dst="original-destination-value"} 0 testmetric{src="d",src1="e",src2="f",dst="original-destination-value"} 1 + dup{label="a", this="a"} 1.0 + dup{label="b", this="a"} 1.0 # label_join joins all src values in order. eval instant at 0m label_join(testmetric, "dst", "-", "src", "src1", "src2") @@ -479,6 +537,9 @@ eval instant at 0m label_join(testmetric1, "dst", ", ", "src", "src1", "src2") testmetric1{src="foo",src1="bar",src2="foobar",dst="foo, bar, foobar"} 0 testmetric1{src="fizz",src1="buzz",src2="fizzbuzz",dst="fizz, buzz, fizzbuzz"} 1 +eval_fail instant at 0m label_join(dup, "label", "", "this") + expected_fail_message vector cannot contain metrics with the same labelset + clear # Tests for vector. @@ -586,6 +647,7 @@ load 5m http_requests{job="app-server", instance="1", group="production"} 0+60x10 http_requests{job="app-server", instance="0", group="canary"} 0+70x10 http_requests{job="app-server", instance="1", group="canary"} 0+80x10 + http_requests{job="app-server", instance="2", group="canary"} {{schema:0 sum:1 count:1}}x15 eval_ordered instant at 50m sort(http_requests) http_requests{group="production", instance="0", job="api-server"} 100 @@ -1641,3 +1703,22 @@ load 1m eval range from 0 to 5m step 1m round(mixed_metric) {} _ 1 2 3 + +# Test scalar() with histograms. +load 1m + metric{type="float", l="x"} 1 + metric{type="float", l="y"} 2 + metric{type="histogram", l="x"} {{schema:0 sum:1 count:1}} + metric{type="histogram", l="x"} {{schema:0 sum:1 count:1}} + +# Two floats in the vector. +eval instant at 0m scalar(metric) + NaN + +# No floats in the vector. +eval instant at 0m scalar({type="histogram"}) + NaN + +# One float in the vector. +eval instant at 0m scalar({l="x"}) + 1 diff --git a/pkg/streamingpromql/testdata/upstream/native_histograms.test b/pkg/streamingpromql/testdata/upstream/native_histograms.test index 5cb8ade772e..f42ee7cbea2 100644 --- a/pkg/streamingpromql/testdata/upstream/native_histograms.test +++ b/pkg/streamingpromql/testdata/upstream/native_histograms.test @@ -1018,7 +1018,7 @@ eval instant at 5m sum(custom_buckets_histogram) clear -# Test 'this native histogram metric is not a gauge' warning for rate +# Test 'this native histogram metric is not a counter' warning for rate load 30s some_metric {{schema:0 sum:1 count:1 buckets:[1] counter_reset_hint:gauge}} {{schema:0 sum:2 count:2 buckets:[2] counter_reset_hint:gauge}} {{schema:0 sum:3 count:3 buckets:[3] counter_reset_hint:gauge}} @@ -1027,7 +1027,7 @@ eval_warn instant at 30s rate(some_metric[1m]) {} {{count:0.03333333333333333 sum:0.03333333333333333 buckets:[0.03333333333333333]}} # Test the case where we have more than two points for rate -eval_warn instant at 1m rate(some_metric[1m]) +eval_warn instant at 1m rate(some_metric[1m30s]) {} {{count:0.03333333333333333 sum:0.03333333333333333 buckets:[0.03333333333333333]}} clear @@ -1037,20 +1037,20 @@ load 30s some_metric {{schema:0 sum:1 count:1 buckets:[1]}} {{schema:-53 sum:1 count:1 custom_values:[5 10] buckets:[1]}} {{schema:0 sum:5 count:4 buckets:[1 2 1]}} {{schema:-53 sum:1 count:1 custom_values:[5 10] buckets:[1]}} # Start and end with exponential, with custom in the middle. -eval_warn instant at 1m rate(some_metric[1m]) +eval_warn instant at 1m rate(some_metric[1m30s]) # Should produce no results. # Start and end with custom, with exponential in the middle. -eval_warn instant at 1m30s rate(some_metric[1m]) +eval_warn instant at 1m30s rate(some_metric[1m30s]) # Should produce no results. -# Start with custom, end with exponential. -eval_warn instant at 1m rate(some_metric[1m]) - # Should produce no results. +# Start with custom, end with exponential. Return the exponential histogram divided by 30. +eval instant at 1m rate(some_metric[1m]) + {} {{schema:0 sum:0.16666666666666666 count:0.13333333333333333 buckets:[0.03333333333333333 0.06666666666666667 0.03333333333333333]}} -# Start with exponential, end with custom. -eval_warn instant at 30s rate(some_metric[1m]) - # Should produce no results. +# Start with exponential, end with custom. Return the custom buckets histogram divided by 30. +eval instant at 30s rate(some_metric[1m]) + {} {{schema:-53 sum:0.03333333333333333 count:0.03333333333333333 custom_values:[5 10] buckets:[0.03333333333333333]}} clear @@ -1187,7 +1187,10 @@ eval_info range from 0 to 6m step 6m metric2 > metric2 clear load 6m - nhcb_metric {{schema:-53 sum:1 count:1 custom_values:[2] buckets:[1]}} {{schema:-53 sum:1 count:1 custom_values:[5 10] buckets:[1]}} {{schema:-53 sum:1 count:1 custom_values:[5 10] buckets:[1]}} + nhcb_metric {{schema:-53 sum:1 count:1 custom_values:[2] buckets:[1]}} {{schema:-53 sum:1 count:1 custom_values:[2] buckets:[1]}} {{schema:-53 sum:1 count:1 custom_values:[5 10] buckets:[1]}} {{schema:-53 sum:1 count:1 custom_values:[5 10] buckets:[1]}} + +# If evaluating at 12m, the first two NHCBs have the same custom values +# while the 3rd one has different ones. eval_warn instant at 12m sum_over_time(nhcb_metric[13m]) @@ -1214,6 +1217,38 @@ eval_warn instant at 12m rate(nhcb_metric[13m]) eval instant at 12m resets(nhcb_metric[13m]) {} 1 +# Now doing the same again, but at 18m, where the first NHCB has +# different custom_values compared to the other two. This now +# works with no warning for increase() and rate(). No change +# otherwise. + +eval_warn instant at 18m sum_over_time(nhcb_metric[13m]) + +eval_warn instant at 18m avg_over_time(nhcb_metric[13m]) + +eval instant at 18m last_over_time(nhcb_metric[13m]) +nhcb_metric{} {{schema:-53 sum:1 count:1 custom_values:[5 10] buckets:[1]}} + +eval instant at 18m count_over_time(nhcb_metric[13m]) +{} 3 + +eval instant at 18m present_over_time(nhcb_metric[13m]) +{} 1 + +eval instant at 18m changes(nhcb_metric[13m]) +{} 1 + +eval_warn instant at 18m delta(nhcb_metric[13m]) + +eval instant at 18m increase(nhcb_metric[13m]) +{} {{schema:-53 count:1.0833333333333333 sum:1.0833333333333333 custom_values:[5 10] buckets:[1.0833333333333333]}} + +eval instant at 18m rate(nhcb_metric[13m]) +{} {{schema:-53 count:0.0013888888888888887 sum:0.0013888888888888887 custom_values:[5 10] buckets:[0.0013888888888888887]}} + +eval instant at 18m resets(nhcb_metric[13m]) +{} 1 + clear load 1m diff --git a/vendor/github.com/open-telemetry/opentelemetry-collector-contrib/internal/exp/metrics/identity/metric.go b/vendor/github.com/open-telemetry/opentelemetry-collector-contrib/internal/exp/metrics/identity/metric.go index ceaea90b8e0..7068bfbdbef 100644 --- a/vendor/github.com/open-telemetry/opentelemetry-collector-contrib/internal/exp/metrics/identity/metric.go +++ b/vendor/github.com/open-telemetry/opentelemetry-collector-contrib/internal/exp/metrics/identity/metric.go @@ -4,14 +4,13 @@ package identity // import "github.com/open-telemetry/opentelemetry-collector-contrib/internal/exp/metrics/identity" import ( + "fmt" "hash" "go.opentelemetry.io/collector/pdata/pcommon" "go.opentelemetry.io/collector/pdata/pmetric" ) -type metric = Metric - type Metric struct { scope @@ -23,21 +22,21 @@ type Metric struct { temporality pmetric.AggregationTemporality } -func (i Metric) Hash() hash.Hash64 { - sum := i.scope.Hash() - sum.Write([]byte(i.name)) - sum.Write([]byte(i.unit)) +func (m Metric) Hash() hash.Hash64 { + sum := m.scope.Hash() + sum.Write([]byte(m.name)) + sum.Write([]byte(m.unit)) var mono byte - if i.monotonic { + if m.monotonic { mono = 1 } - sum.Write([]byte{byte(i.ty), mono, byte(i.temporality)}) + sum.Write([]byte{byte(m.ty), mono, byte(m.temporality)}) return sum } -func (i Metric) Scope() Scope { - return i.scope +func (m Metric) Scope() Scope { + return m.scope } func OfMetric(scope Scope, m pmetric.Metric) Metric { @@ -66,6 +65,10 @@ func OfMetric(scope Scope, m pmetric.Metric) Metric { return id } +func (m Metric) String() string { + return fmt.Sprintf("metric/%x", m.Hash().Sum64()) +} + func OfResourceMetric(res pcommon.Resource, scope pcommon.InstrumentationScope, metric pmetric.Metric) Metric { return OfMetric(OfScope(OfResource(res), scope), metric) } diff --git a/vendor/github.com/open-telemetry/opentelemetry-collector-contrib/internal/exp/metrics/identity/resource.go b/vendor/github.com/open-telemetry/opentelemetry-collector-contrib/internal/exp/metrics/identity/resource.go index 990fb71e64e..7114c45facc 100644 --- a/vendor/github.com/open-telemetry/opentelemetry-collector-contrib/internal/exp/metrics/identity/resource.go +++ b/vendor/github.com/open-telemetry/opentelemetry-collector-contrib/internal/exp/metrics/identity/resource.go @@ -4,6 +4,7 @@ package identity // import "github.com/open-telemetry/opentelemetry-collector-contrib/internal/exp/metrics/identity" import ( + "fmt" "hash" "hash/fnv" @@ -24,6 +25,10 @@ func (r Resource) Hash() hash.Hash64 { return sum } +func (r Resource) String() string { + return fmt.Sprintf("resource/%x", r.Hash().Sum64()) +} + func OfResource(r pcommon.Resource) Resource { return Resource{ attrs: pdatautil.MapHash(r.Attributes()), diff --git a/vendor/github.com/open-telemetry/opentelemetry-collector-contrib/internal/exp/metrics/identity/scope.go b/vendor/github.com/open-telemetry/opentelemetry-collector-contrib/internal/exp/metrics/identity/scope.go index db516bc14c7..0ea0b0f1595 100644 --- a/vendor/github.com/open-telemetry/opentelemetry-collector-contrib/internal/exp/metrics/identity/scope.go +++ b/vendor/github.com/open-telemetry/opentelemetry-collector-contrib/internal/exp/metrics/identity/scope.go @@ -4,6 +4,7 @@ package identity // import "github.com/open-telemetry/opentelemetry-collector-contrib/internal/exp/metrics/identity" import ( + "fmt" "hash" "go.opentelemetry.io/collector/pdata/pcommon" @@ -33,6 +34,10 @@ func (s Scope) Resource() Resource { return s.resource } +func (s Scope) String() string { + return fmt.Sprintf("scope/%x", s.Hash().Sum64()) +} + func OfScope(res Resource, scope pcommon.InstrumentationScope) Scope { return Scope{ resource: res, diff --git a/vendor/github.com/open-telemetry/opentelemetry-collector-contrib/internal/exp/metrics/identity/stream.go b/vendor/github.com/open-telemetry/opentelemetry-collector-contrib/internal/exp/metrics/identity/stream.go index 19988f7730d..c29af83edfa 100644 --- a/vendor/github.com/open-telemetry/opentelemetry-collector-contrib/internal/exp/metrics/identity/stream.go +++ b/vendor/github.com/open-telemetry/opentelemetry-collector-contrib/internal/exp/metrics/identity/stream.go @@ -4,6 +4,7 @@ package identity // import "github.com/open-telemetry/opentelemetry-collector-contrib/internal/exp/metrics/identity" import ( + "fmt" "hash" "go.opentelemetry.io/collector/pdata/pcommon" @@ -12,18 +13,22 @@ import ( ) type Stream struct { - metric - attrs [16]byte + metric Metric + attrs [16]byte } -func (i Stream) Hash() hash.Hash64 { - sum := i.metric.Hash() - sum.Write(i.attrs[:]) +func (s Stream) Hash() hash.Hash64 { + sum := s.metric.Hash() + sum.Write(s.attrs[:]) return sum } -func (i Stream) Metric() Metric { - return i.metric +func (s Stream) Metric() Metric { + return s.metric +} + +func (s Stream) String() string { + return fmt.Sprintf("stream/%x", s.Hash().Sum64()) } func OfStream[DataPoint attrPoint](m Metric, dp DataPoint) Stream { diff --git a/vendor/github.com/open-telemetry/opentelemetry-collector-contrib/internal/exp/metrics/identity/strings.go b/vendor/github.com/open-telemetry/opentelemetry-collector-contrib/internal/exp/metrics/identity/strings.go deleted file mode 100644 index 7339f95a578..00000000000 --- a/vendor/github.com/open-telemetry/opentelemetry-collector-contrib/internal/exp/metrics/identity/strings.go +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -package identity // import "github.com/open-telemetry/opentelemetry-collector-contrib/internal/exp/metrics/identity" - -import ( - "fmt" -) - -func (r Resource) String() string { - return fmt.Sprintf("resource/%x", r.Hash().Sum64()) -} - -func (s Scope) String() string { - return fmt.Sprintf("scope/%x", s.Hash().Sum64()) -} - -func (m Metric) String() string { - return fmt.Sprintf("metric/%x", m.Hash().Sum64()) -} - -func (s Stream) String() string { - return fmt.Sprintf("stream/%x", s.Hash().Sum64()) -} diff --git a/vendor/github.com/open-telemetry/opentelemetry-collector-contrib/internal/exp/metrics/staleness/staleness.go b/vendor/github.com/open-telemetry/opentelemetry-collector-contrib/internal/exp/metrics/staleness/staleness.go deleted file mode 100644 index eb52e686182..00000000000 --- a/vendor/github.com/open-telemetry/opentelemetry-collector-contrib/internal/exp/metrics/staleness/staleness.go +++ /dev/null @@ -1,134 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -package staleness // import "github.com/open-telemetry/opentelemetry-collector-contrib/internal/exp/metrics/staleness" - -import ( - "time" - - "github.com/open-telemetry/opentelemetry-collector-contrib/internal/exp/metrics/identity" - "github.com/open-telemetry/opentelemetry-collector-contrib/internal/exp/metrics/streams" -) - -// We override how Now() is returned, so we can have deterministic tests -var NowFunc = time.Now - -var ( - _ streams.Map[any] = (*Staleness[any])(nil) - _ streams.Evictor = (*Staleness[any])(nil) -) - -// Staleness a a wrapper over a map that adds an additional "staleness" value to each entry. Users can -// call ExpireOldEntries() to automatically remove all entries from the map whole staleness value is -// older than the `max` -// -// NOTE: Staleness methods are *not* thread-safe. If the user needs to use Staleness in a multi-threaded -// environment, then it is the user's responsibility to properly serialize calls to Staleness methods -type Staleness[T any] struct { - Max time.Duration - - items streams.Map[T] - pq PriorityQueue -} - -func NewStaleness[T any](max time.Duration, items streams.Map[T]) *Staleness[T] { - return &Staleness[T]{ - Max: max, - - items: items, - pq: NewPriorityQueue(), - } -} - -// Load the value at key. If it does not exist, the boolean will be false and the value returned will be the zero value -func (s *Staleness[T]) Load(id identity.Stream) (T, bool) { - return s.items.Load(id) -} - -// Store the given key value pair in the map, and update the pair's staleness value to "now" -func (s *Staleness[T]) Store(id identity.Stream, v T) error { - s.pq.Update(id, NowFunc()) - return s.items.Store(id, v) -} - -func (s *Staleness[T]) Delete(id identity.Stream) { - s.items.Delete(id) -} - -// Items returns an iterator function that in future go version can be used with range -// See: https://go.dev/wiki/RangefuncExperiment -func (s *Staleness[T]) Items() func(yield func(identity.Stream, T) bool) bool { - return s.items.Items() -} - -// ExpireOldEntries will remove all entries whose staleness value is older than `now() - max` -// For example, if an entry has a staleness value of two hours ago, and max == 1 hour, then the entry would -// be removed. But if an entry had a stalness value of 30 minutes, then it *wouldn't* be removed. -func (s *Staleness[T]) ExpireOldEntries() { - now := NowFunc() - for { - if s.Len() == 0 { - return - } - _, ts := s.pq.Peek() - if now.Sub(ts) < s.Max { - break - } - id, _ := s.pq.Pop() - s.items.Delete(id) - } -} - -func (s *Staleness[T]) Len() int { - return s.items.Len() -} - -func (s *Staleness[T]) Next() time.Time { - _, ts := s.pq.Peek() - return ts -} - -func (s *Staleness[T]) Evict() (identity.Stream, bool) { - _, ts := s.pq.Peek() - if NowFunc().Sub(ts) < s.Max { - return identity.Stream{}, false - } - - id, _ := s.pq.Pop() - s.items.Delete(id) - return id, true -} - -func (s *Staleness[T]) Clear() { - s.items.Clear() -} - -type Tracker struct { - pq PriorityQueue -} - -func NewTracker() Tracker { - return Tracker{pq: NewPriorityQueue()} -} - -func (stale Tracker) Refresh(ts time.Time, ids ...identity.Stream) { - for _, id := range ids { - stale.pq.Update(id, ts) - } -} - -func (stale Tracker) Collect(max time.Duration) []identity.Stream { - now := NowFunc() - - var ids []identity.Stream - for stale.pq.Len() > 0 { - _, ts := stale.pq.Peek() - if now.Sub(ts) < max { - break - } - id, _ := stale.pq.Pop() - ids = append(ids, id) - } - - return ids -} diff --git a/vendor/github.com/open-telemetry/opentelemetry-collector-contrib/internal/exp/metrics/staleness/tracker.go b/vendor/github.com/open-telemetry/opentelemetry-collector-contrib/internal/exp/metrics/staleness/tracker.go new file mode 100644 index 00000000000..68676e19b0f --- /dev/null +++ b/vendor/github.com/open-telemetry/opentelemetry-collector-contrib/internal/exp/metrics/staleness/tracker.go @@ -0,0 +1,40 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package staleness // import "github.com/open-telemetry/opentelemetry-collector-contrib/internal/exp/metrics/staleness" + +import ( + "time" + + "github.com/open-telemetry/opentelemetry-collector-contrib/internal/exp/metrics/identity" +) + +type Tracker struct { + pq PriorityQueue +} + +func NewTracker() Tracker { + return Tracker{pq: NewPriorityQueue()} +} + +func (tr Tracker) Refresh(ts time.Time, ids ...identity.Stream) { + for _, id := range ids { + tr.pq.Update(id, ts) + } +} + +func (tr Tracker) Collect(maxDuration time.Duration) []identity.Stream { + now := time.Now() + + var ids []identity.Stream + for tr.pq.Len() > 0 { + _, ts := tr.pq.Peek() + if now.Sub(ts) < maxDuration { + break + } + id, _ := tr.pq.Pop() + ids = append(ids, id) + } + + return ids +} diff --git a/vendor/github.com/open-telemetry/opentelemetry-collector-contrib/internal/exp/metrics/streams/streams.go b/vendor/github.com/open-telemetry/opentelemetry-collector-contrib/internal/exp/metrics/streams/streams.go deleted file mode 100644 index 5f0d715b696..00000000000 --- a/vendor/github.com/open-telemetry/opentelemetry-collector-contrib/internal/exp/metrics/streams/streams.go +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -package streams // import "github.com/open-telemetry/opentelemetry-collector-contrib/internal/exp/metrics/streams" - -import ( - "go.opentelemetry.io/collector/pdata/pcommon" - - "github.com/open-telemetry/opentelemetry-collector-contrib/internal/exp/metrics/identity" -) - -// Sequence of streams that can be iterated upon -type Seq[T any] func(yield func(identity.Stream, T) bool) bool - -// Map defines a collection of items tracked by a stream-id and the operations -// on it -type Map[T any] interface { - Load(identity.Stream) (T, bool) - Store(identity.Stream, T) error - Delete(identity.Stream) - Items() func(yield func(identity.Stream, T) bool) bool - Len() int - Clear() -} - -var _ Map[any] = HashMap[any](nil) - -type HashMap[T any] map[identity.Stream]T - -func (m HashMap[T]) Load(id identity.Stream) (T, bool) { - v, ok := (map[identity.Stream]T)(m)[id] - return v, ok -} - -func (m HashMap[T]) Store(id identity.Stream, v T) error { - (map[identity.Stream]T)(m)[id] = v - return nil -} - -func (m HashMap[T]) Delete(id identity.Stream) { - delete((map[identity.Stream]T)(m), id) -} - -func (m HashMap[T]) Items() func(yield func(identity.Stream, T) bool) bool { - return func(yield func(identity.Stream, T) bool) bool { - for id, v := range (map[identity.Stream]T)(m) { - if !yield(id, v) { - break - } - } - return false - } -} - -func (m HashMap[T]) Len() int { - return len((map[identity.Stream]T)(m)) -} - -func (m HashMap[T]) Clear() { - clear(m) -} - -// Evictors remove the "least important" stream based on some strategy such as -// the oldest, least active, etc. -// -// Returns whether a stream was evicted and if so the now gone stream id -type Evictor interface { - Evict() (gone identity.Stream, ok bool) -} - -type DataPointSlice[DP DataPoint[DP]] interface { - Len() int - At(i int) DP - AppendEmpty() DP -} - -type DataPoint[Self any] interface { - Timestamp() pcommon.Timestamp - Attributes() pcommon.Map - CopyTo(dest Self) -} diff --git a/vendor/github.com/open-telemetry/opentelemetry-collector-contrib/processor/deltatocumulativeprocessor/internal/data/add.go b/vendor/github.com/open-telemetry/opentelemetry-collector-contrib/processor/deltatocumulativeprocessor/internal/data/add.go index 33c2f283c84..13b9a815110 100644 --- a/vendor/github.com/open-telemetry/opentelemetry-collector-contrib/processor/deltatocumulativeprocessor/internal/data/add.go +++ b/vendor/github.com/open-telemetry/opentelemetry-collector-contrib/processor/deltatocumulativeprocessor/internal/data/add.go @@ -12,6 +12,8 @@ import ( "github.com/open-telemetry/opentelemetry-collector-contrib/processor/deltatocumulativeprocessor/internal/putil/pslice" ) +var maxBuckets = 160 + func (dp Number) Add(in Number) Number { switch in.ValueType() { case pmetric.NumberDataPointValueTypeDouble: @@ -76,6 +78,21 @@ func (dp ExpHistogram) Add(in ExpHistogram) ExpHistogram { hi.SetScale(lo.Scale()) } + // Downscale if an expected number of buckets after the merge is too large. + from := expo.Scale(dp.Scale()) + to := max( + expo.Limit(maxBuckets, from, dp.Positive(), in.Positive()), + expo.Limit(maxBuckets, from, dp.Negative(), in.Negative()), + ) + if from != to { + expo.Downscale(dp.Positive(), from, to) + expo.Downscale(dp.Negative(), from, to) + expo.Downscale(in.Positive(), from, to) + expo.Downscale(in.Negative(), from, to) + dp.SetScale(int32(to)) + in.SetScale(int32(to)) + } + if dp.ZeroThreshold() != in.ZeroThreshold() { hi, lo := expo.HiLo(dp, in, H.ZeroThreshold) expo.WidenZero(lo.DataPoint, hi.ZeroThreshold()) diff --git a/vendor/github.com/open-telemetry/opentelemetry-collector-contrib/processor/deltatocumulativeprocessor/internal/data/expo/merge.go b/vendor/github.com/open-telemetry/opentelemetry-collector-contrib/processor/deltatocumulativeprocessor/internal/data/expo/merge.go index 150e29a6581..82536ea1fa7 100644 --- a/vendor/github.com/open-telemetry/opentelemetry-collector-contrib/processor/deltatocumulativeprocessor/internal/data/expo/merge.go +++ b/vendor/github.com/open-telemetry/opentelemetry-collector-contrib/processor/deltatocumulativeprocessor/internal/data/expo/merge.go @@ -23,6 +23,15 @@ func Merge(arel, brel Buckets) { lo := min(a.Lower(), b.Lower()) up := max(a.Upper(), b.Upper()) + // Skip leading and trailing zeros to reduce number of buckets. + // As we cap number of buckets this allows us to have higher scale. + for lo < up && a.Abs(lo) == 0 && b.Abs(lo) == 0 { + lo++ + } + for lo < up-1 && a.Abs(up-1) == 0 && b.Abs(up-1) == 0 { + up-- + } + size := up - lo counts := pcommon.NewUInt64Slice() diff --git a/vendor/github.com/open-telemetry/opentelemetry-collector-contrib/processor/deltatocumulativeprocessor/internal/data/expo/scale.go b/vendor/github.com/open-telemetry/opentelemetry-collector-contrib/processor/deltatocumulativeprocessor/internal/data/expo/scale.go index 5201806fb82..50fdef75c9f 100644 --- a/vendor/github.com/open-telemetry/opentelemetry-collector-contrib/processor/deltatocumulativeprocessor/internal/data/expo/scale.go +++ b/vendor/github.com/open-telemetry/opentelemetry-collector-contrib/processor/deltatocumulativeprocessor/internal/data/expo/scale.go @@ -6,6 +6,8 @@ package expo // import "github.com/open-telemetry/opentelemetry-collector-contri import ( "fmt" "math" + + "go.opentelemetry.io/collector/pdata/pmetric" ) type Scale int32 @@ -29,7 +31,7 @@ func (scale Scale) Idx(v float64) int { // This means a value min < v <= max belongs to this bucket. // // NOTE: this is different from Go slice intervals, which are [a,b) -func (scale Scale) Bounds(index int) (min, max float64) { +func (scale Scale) Bounds(index int) (minVal, maxVal float64) { // from: https://opentelemetry.io/docs/specs/otel/metrics/data-model/#all-scales-use-the-logarithm-function lower := func(index int) float64 { inverseFactor := math.Ldexp(math.Ln2, int(-scale)) @@ -47,7 +49,7 @@ func Downscale(bs Buckets, from, to Scale) { case from < to: // because even distribution within the buckets cannot be assumed, it is // not possible to correctly upscale (split) buckets. - // any attempt to do so would yield erronous data. + // any attempt to do so would yield erroneous data. panic(fmt.Sprintf("cannot upscale without introducing error (%d -> %d)", from, to)) } @@ -107,9 +109,35 @@ func Collapse(bs Buckets) { // zero the excess area. its not needed to represent the observation // anymore, but kept for two reasons: // 1. future observations may need it, no need to re-alloc then if kept - // 2. [pcommon.Uint64Slice] can not, in fact, be sliced, so getting rid + // 2. [pcommon.Uint64Slice] cannot, in fact, be sliced, so getting rid // of it would alloc ¯\_(ツ)_/¯ for i := size; i < counts.Len(); i++ { counts.SetAt(i, 0) } } + +// Limit returns a target Scale that when be downscaled to, +// the total bucket count after [Merge] never exceeds maxBuckets. +func Limit(maxBuckets int, scale Scale, arel, brel pmetric.ExponentialHistogramDataPointBuckets) Scale { + a, b := Abs(arel), Abs(brel) + + lo := min(a.Lower(), b.Lower()) + up := max(a.Upper(), b.Upper()) + + // Skip leading and trailing zeros. + for lo < up && a.Abs(lo) == 0 && b.Abs(lo) == 0 { + lo++ + } + for lo < up-1 && a.Abs(up-1) == 0 && b.Abs(up-1) == 0 { + up-- + } + + // Keep downscaling until the number of buckets is within the limit. + for up-lo > maxBuckets { + lo /= 2 + up /= 2 + scale-- + } + + return scale +} diff --git a/vendor/github.com/open-telemetry/opentelemetry-collector-contrib/processor/deltatocumulativeprocessor/internal/data/expo/zero.go b/vendor/github.com/open-telemetry/opentelemetry-collector-contrib/processor/deltatocumulativeprocessor/internal/data/expo/zero.go index 2d5401b39f5..969c5f2734a 100644 --- a/vendor/github.com/open-telemetry/opentelemetry-collector-contrib/processor/deltatocumulativeprocessor/internal/data/expo/zero.go +++ b/vendor/github.com/open-telemetry/opentelemetry-collector-contrib/processor/deltatocumulativeprocessor/internal/data/expo/zero.go @@ -37,8 +37,8 @@ func WidenZero(dp DataPoint, width float64) { widen(dp.Positive()) widen(dp.Negative()) - _, max := scale.Bounds(zero) - dp.SetZeroThreshold(max) + _, maxVal := scale.Bounds(zero) + dp.SetZeroThreshold(maxVal) } // Slice drops data outside the range from <= i < to from the bucket counts. It behaves the same as Go's [a:b] diff --git a/vendor/github.com/open-telemetry/opentelemetry-collector-contrib/processor/deltatocumulativeprocessor/internal/metadata/generated_telemetry.go b/vendor/github.com/open-telemetry/opentelemetry-collector-contrib/processor/deltatocumulativeprocessor/internal/metadata/generated_telemetry.go index 82a4476ba92..1b24ace4e6c 100644 --- a/vendor/github.com/open-telemetry/opentelemetry-collector-contrib/processor/deltatocumulativeprocessor/internal/metadata/generated_telemetry.go +++ b/vendor/github.com/open-telemetry/opentelemetry-collector-contrib/processor/deltatocumulativeprocessor/internal/metadata/generated_telemetry.go @@ -7,7 +7,7 @@ import ( "errors" "go.opentelemetry.io/otel/metric" - "go.opentelemetry.io/otel/metric/noop" + noopmetric "go.opentelemetry.io/otel/metric/noop" "go.opentelemetry.io/otel/trace" "go.opentelemetry.io/collector/component" @@ -131,5 +131,5 @@ func getLeveledMeter(meter metric.Meter, cfgLevel, srvLevel configtelemetry.Leve if cfgLevel <= srvLevel { return meter } - return noop.Meter{} + return noopmetric.Meter{} } diff --git a/vendor/github.com/prometheus/prometheus/promql/engine.go b/vendor/github.com/prometheus/prometheus/promql/engine.go index 40c93e6cd4b..18a2420a495 100644 --- a/vendor/github.com/prometheus/prometheus/promql/engine.go +++ b/vendor/github.com/prometheus/prometheus/promql/engine.go @@ -2358,6 +2358,11 @@ func (ev *evaluator) matrixIterSlice( } } + if mint == maxt { + // Empty range: return the empty slices. + return floats, histograms + } + soughtValueType := it.Seek(maxt) if soughtValueType == chunkenc.ValNone { if it.Err() != nil { @@ -3476,15 +3481,14 @@ func handleVectorBinopError(err error, e *parser.BinaryExpr) annotations.Annotat if err == nil { return nil } - metricName := "" + op := parser.ItemTypeStr[e.Op] pos := e.PositionRange() if errors.Is(err, annotations.PromQLInfo) || errors.Is(err, annotations.PromQLWarning) { return annotations.New().Add(err) } - if errors.Is(err, histogram.ErrHistogramsIncompatibleSchema) { - return annotations.New().Add(annotations.NewMixedExponentialCustomHistogramsWarning(metricName, pos)) - } else if errors.Is(err, histogram.ErrHistogramsIncompatibleBounds) { - return annotations.New().Add(annotations.NewIncompatibleCustomBucketsHistogramsWarning(metricName, pos)) + // TODO(NeerajGartia21): Test the exact annotation output once the testing framework can do so. + if errors.Is(err, histogram.ErrHistogramsIncompatibleSchema) || errors.Is(err, histogram.ErrHistogramsIncompatibleBounds) { + return annotations.New().Add(annotations.NewIncompatibleBucketLayoutInBinOpWarning(op, pos)) } return nil } diff --git a/vendor/github.com/prometheus/prometheus/promql/functions.go b/vendor/github.com/prometheus/prometheus/promql/functions.go index 605661e5a0e..1cb8b2af2de 100644 --- a/vendor/github.com/prometheus/prometheus/promql/functions.go +++ b/vendor/github.com/prometheus/prometheus/promql/functions.go @@ -187,35 +187,48 @@ func extrapolatedRate(vals []parser.Value, args parser.Expressions, enh *EvalNod // not a histogram, and a warning wrapped in an annotation in that case. // Otherwise, it returns the calculated histogram and an empty annotation. func histogramRate(points []HPoint, isCounter bool, metricName string, pos posrange.PositionRange) (*histogram.FloatHistogram, annotations.Annotations) { - prev := points[0].H - usingCustomBuckets := prev.UsesCustomBuckets() - last := points[len(points)-1].H + var ( + prev = points[0].H + usingCustomBuckets = prev.UsesCustomBuckets() + last = points[len(points)-1].H + annos annotations.Annotations + ) + if last == nil { - return nil, annotations.New().Add(annotations.NewMixedFloatsHistogramsWarning(metricName, pos)) + return nil, annos.Add(annotations.NewMixedFloatsHistogramsWarning(metricName, pos)) } - minSchema := prev.Schema - if last.Schema < minSchema { - minSchema = last.Schema + // We check for gauge type histograms in the loop below, but the loop + // below does not run on the first and last point, so check the first + // and last point now. + if isCounter && (prev.CounterResetHint == histogram.GaugeType || last.CounterResetHint == histogram.GaugeType) { + annos.Add(annotations.NewNativeHistogramNotCounterWarning(metricName, pos)) } - if last.UsesCustomBuckets() != usingCustomBuckets { - return nil, annotations.New().Add(annotations.NewMixedExponentialCustomHistogramsWarning(metricName, pos)) + // Null out the 1st sample if there is a counter reset between the 1st + // and 2nd. In this case, we want to ignore any incompatibility in the + // bucket layout of the 1st sample because we do not need to look at it. + if isCounter && len(points) > 1 { + second := points[1].H + if second != nil && second.DetectReset(prev) { + prev = &histogram.FloatHistogram{} + prev.Schema = second.Schema + prev.CustomValues = second.CustomValues + usingCustomBuckets = second.UsesCustomBuckets() + } } - var annos annotations.Annotations - - // We check for gauge type histograms in the loop below, but the loop below does not run on the first and last point, - // so check the first and last point now. - if isCounter && (prev.CounterResetHint == histogram.GaugeType || last.CounterResetHint == histogram.GaugeType) { - annos.Add(annotations.NewNativeHistogramNotCounterWarning(metricName, pos)) + if last.UsesCustomBuckets() != usingCustomBuckets { + return nil, annos.Add(annotations.NewMixedExponentialCustomHistogramsWarning(metricName, pos)) } // First iteration to find out two things: // - What's the smallest relevant schema? // - Are all data points histograms? - // TODO(beorn7): Find a way to check that earlier, e.g. by handing in a - // []FloatPoint and a []HistogramPoint separately. + minSchema := prev.Schema + if last.Schema < minSchema { + minSchema = last.Schema + } for _, currPoint := range points[1 : len(points)-1] { curr := currPoint.H if curr == nil { @@ -286,46 +299,115 @@ func funcIncrease(vals []parser.Value, args parser.Expressions, enh *EvalNodeHel // === irate(node parser.ValueTypeMatrix) (Vector, Annotations) === func funcIrate(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) { - return instantValue(vals, enh.Out, true) + return instantValue(vals, args, enh.Out, true) } // === idelta(node model.ValMatrix) (Vector, Annotations) === func funcIdelta(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) { - return instantValue(vals, enh.Out, false) + return instantValue(vals, args, enh.Out, false) } -func instantValue(vals []parser.Value, out Vector, isRate bool) (Vector, annotations.Annotations) { - samples := vals[0].(Matrix)[0] +func instantValue(vals []parser.Value, args parser.Expressions, out Vector, isRate bool) (Vector, annotations.Annotations) { + var ( + samples = vals[0].(Matrix)[0] + metricName = samples.Metric.Get(labels.MetricName) + ss = make([]Sample, 0, 2) + annos annotations.Annotations + ) + // No sense in trying to compute a rate without at least two points. Drop // this Vector element. // TODO: add RangeTooShortWarning - if len(samples.Floats) < 2 { + if len(samples.Floats)+len(samples.Histograms) < 2 { return out, nil } - lastSample := samples.Floats[len(samples.Floats)-1] - previousSample := samples.Floats[len(samples.Floats)-2] + // Add the last 2 float samples if they exist. + for i := max(0, len(samples.Floats)-2); i < len(samples.Floats); i++ { + ss = append(ss, Sample{ + F: samples.Floats[i].F, + T: samples.Floats[i].T, + }) + } - var resultValue float64 - if isRate && lastSample.F < previousSample.F { - // Counter reset. - resultValue = lastSample.F - } else { - resultValue = lastSample.F - previousSample.F + // Add the last 2 histogram samples into their correct position if they exist. + for i := max(0, len(samples.Histograms)-2); i < len(samples.Histograms); i++ { + s := Sample{ + H: samples.Histograms[i].H, + T: samples.Histograms[i].T, + } + switch { + case len(ss) == 0: + ss = append(ss, s) + case len(ss) == 1: + if s.T < ss[0].T { + ss = append([]Sample{s}, ss...) + } else { + ss = append(ss, s) + } + case s.T < ss[0].T: + // s is older than 1st, so discard it. + case s.T > ss[1].T: + // s is newest, so add it as 2nd and make the old 2nd the new 1st. + ss[0] = ss[1] + ss[1] = s + default: + // In all other cases, we just make s the new 1st. + // This establishes a correct order, even in the (irregular) + // case of equal timestamps. + ss[0] = s + } } - sampledInterval := lastSample.T - previousSample.T + resultSample := ss[1] + sampledInterval := ss[1].T - ss[0].T if sampledInterval == 0 { // Avoid dividing by 0. return out, nil } + switch { + case ss[1].H == nil && ss[0].H == nil: + if !isRate || ss[1].F >= ss[0].F { + // Gauge or counter without reset. + resultSample.F = ss[1].F - ss[0].F + } + // In case of a counter reset, we leave resultSample at + // its current value, which is already ss[1]. + case ss[1].H != nil && ss[0].H != nil: + resultSample.H = ss[1].H.Copy() + // irate should only be applied to counters. + if isRate && (ss[1].H.CounterResetHint == histogram.GaugeType || ss[0].H.CounterResetHint == histogram.GaugeType) { + annos.Add(annotations.NewNativeHistogramNotCounterWarning(metricName, args.PositionRange())) + } + // idelta should only be applied to gauges. + if !isRate && (ss[1].H.CounterResetHint != histogram.GaugeType || ss[0].H.CounterResetHint != histogram.GaugeType) { + annos.Add(annotations.NewNativeHistogramNotGaugeWarning(metricName, args.PositionRange())) + } + if !isRate || !ss[1].H.DetectReset(ss[0].H) { + _, err := resultSample.H.Sub(ss[0].H) + if errors.Is(err, histogram.ErrHistogramsIncompatibleSchema) { + return out, annos.Add(annotations.NewMixedExponentialCustomHistogramsWarning(metricName, args.PositionRange())) + } else if errors.Is(err, histogram.ErrHistogramsIncompatibleBounds) { + return out, annos.Add(annotations.NewIncompatibleCustomBucketsHistogramsWarning(metricName, args.PositionRange())) + } + } + resultSample.H.CounterResetHint = histogram.GaugeType + resultSample.H.Compact(0) + default: + // Mix of a float and a histogram. + return out, annos.Add(annotations.NewMixedFloatsHistogramsWarning(metricName, args.PositionRange())) + } if isRate { // Convert to per-second. - resultValue /= float64(sampledInterval) / 1000 + if resultSample.H == nil { + resultSample.F /= float64(sampledInterval) / 1000 + } else { + resultSample.H.Div(float64(sampledInterval) / 1000) + } } - return append(out, Sample{F: resultValue}), nil + return append(out, resultSample), annos } // Calculate the trend value at the given index i in raw data d. @@ -404,11 +486,22 @@ func funcDoubleExponentialSmoothing(vals []parser.Value, args parser.Expressions return append(enh.Out, Sample{F: s1}), nil } +// filterFloats filters out histogram samples from the vector in-place. +func filterFloats(v Vector) Vector { + floats := v[:0] + for _, s := range v { + if s.H == nil { + floats = append(floats, s) + } + } + return floats +} + // === sort(node parser.ValueTypeVector) (Vector, Annotations) === func funcSort(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) { // NaN should sort to the bottom, so take descending sort with NaN first and // reverse it. - byValueSorter := vectorByReverseValueHeap(vals[0].(Vector)) + byValueSorter := vectorByReverseValueHeap(filterFloats(vals[0].(Vector))) sort.Sort(sort.Reverse(byValueSorter)) return Vector(byValueSorter), nil } @@ -417,7 +510,7 @@ func funcSort(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) func funcSortDesc(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) { // NaN should sort to the bottom, so take ascending sort with NaN first and // reverse it. - byValueSorter := vectorByValueHeap(vals[0].(Vector)) + byValueSorter := vectorByValueHeap(filterFloats(vals[0].(Vector))) sort.Sort(sort.Reverse(byValueSorter)) return Vector(byValueSorter), nil } @@ -549,11 +642,27 @@ func funcRound(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper // === Scalar(node parser.ValueTypeVector) Scalar === func funcScalar(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) { - v := vals[0].(Vector) - if len(v) != 1 { + var ( + v = vals[0].(Vector) + value float64 + found bool + ) + + for _, s := range v { + if s.H == nil { + if found { + // More than one float found, return NaN. + return append(enh.Out, Sample{F: math.NaN()}), nil + } + found = true + value = s.F + } + } + // Return the single float if found, otherwise return NaN. + if !found { return append(enh.Out, Sample{F: math.NaN()}), nil } - return append(enh.Out, Sample{F: v[0].F}), nil + return append(enh.Out, Sample{F: value}), nil } func aggrOverTime(vals []parser.Value, enh *EvalNodeHelper, aggrFn func(Series) float64) Vector { @@ -1543,7 +1652,7 @@ func (ev *evaluator) evalLabelReplace(ctx context.Context, args parser.Expressio if err != nil { panic(fmt.Errorf("invalid regular expression in label_replace(): %s", regexStr)) } - if !model.LabelNameRE.MatchString(dst) { + if !model.LabelName(dst).IsValid() { panic(fmt.Errorf("invalid destination label name in label_replace(): %s", dst)) } @@ -1620,6 +1729,9 @@ 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 } @@ -1811,16 +1923,7 @@ func (s vectorByValueHeap) Len() int { } func (s vectorByValueHeap) Less(i, j int) bool { - // We compare histograms based on their sum of observations. - // TODO(beorn7): Is that what we want? vi, vj := s[i].F, s[j].F - if s[i].H != nil { - vi = s[i].H.Sum - } - if s[j].H != nil { - vj = s[j].H.Sum - } - if math.IsNaN(vi) { return true } @@ -1850,16 +1953,7 @@ func (s vectorByReverseValueHeap) Len() int { } func (s vectorByReverseValueHeap) Less(i, j int) bool { - // We compare histograms based on their sum of observations. - // TODO(beorn7): Is that what we want? vi, vj := s[i].F, s[j].F - if s[i].H != nil { - vi = s[i].H.Sum - } - if s[j].H != nil { - vj = s[j].H.Sum - } - if math.IsNaN(vi) { return true } 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 9bcb7e69455..49ab6c50ffc 100644 --- a/vendor/github.com/prometheus/prometheus/promql/promqltest/testdata/functions.test +++ b/vendor/github.com/prometheus/prometheus/promql/promqltest/testdata/functions.test @@ -218,6 +218,13 @@ clear load 5m http_requests_total{path="/foo"} 0+10x10 http_requests_total{path="/bar"} 0+10x5 0+10x5 + http_requests_histogram{path="/a"} {{sum:2 count:2}}+{{sum:3 count:3}}x5 + http_requests_histogram{path="/b"} 0 0 {{sum:1 count:1}} {{sum:4 count:4}} + http_requests_histogram{path="/c"} 0 0 {{sum:1 count:1}} {{sum:4 count:4 counter_reset_hint:gauge}} + http_requests_histogram{path="/d"} 0 0 {{sum:1 count:1 counter_reset_hint:gauge}} {{sum:4 count:4}} + http_requests_histogram{path="/e"} 0 1 2 {{sum:4 count:4}} + http_requests_histogram{path="/f"} 0 0 {{sum:1 count:1}} {{schema:-53 sum:3 count:3 custom_values:[5 10] buckets:[3]}} + http_requests_histogram{path="/g"} 0 0 {{schema:-53 sum:3 count:3 custom_values:[1] buckets:[3]}} {{schema:-53 sum:3 count:3 custom_values:[5 10] buckets:[3]}} eval instant at 50m irate(http_requests_total[50m]) {path="/foo"} .03333333333333333333 @@ -228,6 +235,28 @@ eval instant at 30m irate(http_requests_total[50m]) {path="/foo"} .03333333333333333333 {path="/bar"} 0 +eval instant at 20m irate(http_requests_histogram{path="/a"}[20m]) + {path="/a"} {{sum:0.01 count:0.01 counter_reset_hint:gauge}} + +eval instant at 20m irate(http_requests_histogram{path="/b"}[20m]) + {path="/b"} {{sum:0.01 count:0.01 counter_reset_hint:gauge}} + +eval instant at 20m irate(http_requests_histogram{path="/b"}[6m]) + +eval_warn instant at 20m irate(http_requests_histogram{path="/c"}[20m]) + {path="/c"} {{sum:0.01 count:0.01 counter_reset_hint:gauge}} + +eval_warn instant at 20m irate(http_requests_histogram{path="/d"}[20m]) + {path="/d"} {{sum:0.01 count:0.01 counter_reset_hint:gauge}} + +eval_warn instant at 20m irate(http_requests_histogram{path="/e"}[20m]) + +eval instant at 20m irate(http_requests_histogram{path="/f"}[20m]) + {path="/f"} {{schema:-53 sum:0.01 count:0.01 custom_values:[5 10] buckets:[0.01]}} + +eval instant at 20m irate(http_requests_histogram{path="/g"}[20m]) + {path="/g"} {{schema:-53 sum:0.01 count:0.01 custom_values:[5 10] buckets:[0.01]}} + clear # Tests for delta(). @@ -259,11 +288,38 @@ clear load 5m http_requests{path="/foo"} 0 50 100 150 http_requests{path="/bar"} 0 50 100 50 + http_requests_histogram{path="/a"} {{sum:2 count:2 counter_reset_hint:gauge}}+{{sum:1 count:3 counter_reset_hint:gauge}}x5 + http_requests_histogram{path="/b"} 0 0 {{sum:1 count:1 counter_reset_hint:gauge}} {{sum:2 count:2 counter_reset_hint:gauge}} + http_requests_histogram{path="/c"} 0 0 {{sum:1 count:1}} {{sum:2 count:2 counter_reset_hint:gauge}} + http_requests_histogram{path="/d"} 0 0 {{sum:1 count:1 counter_reset_hint:gauge}} {{sum:2 count:2}} + http_requests_histogram{path="/e"} 0 1 2 {{sum:1 count:2 counter_reset_hint:gauge}} + http_requests_histogram{path="/f"} 0 0 {{sum:1 count:1 counter_reset_hint:gauge}} {{schema:-53 sum:1 count:1 custom_values:[5 10] buckets:[1] counter_reset_hint:gauge}} + http_requests_histogram{path="/g"} 0 0 {{schema:-53 sum:1 count:1 custom_values:[1] buckets:[2] counter_reset_hint:gauge}} {{schema:-53 sum:1 count:1 custom_values:[5 10] buckets:[1] counter_reset_hint:gauge}} eval instant at 20m idelta(http_requests[20m]) {path="/foo"} 50 {path="/bar"} -50 +eval instant at 20m idelta(http_requests_histogram{path="/a"}[20m]) + {path="/a"} {{sum:1 count:3 counter_reset_hint:gauge}} + +eval instant at 20m idelta(http_requests_histogram{path="/b"}[20m]) + {path="/b"} {{sum:1 count:1 counter_reset_hint:gauge}} + +eval instant at 20m idelta(http_requests_histogram{path="/b"}[6m]) + +eval_warn instant at 20m idelta(http_requests_histogram{path="/c"}[20m]) + {path="/c"} {{sum:1 count:1 counter_reset_hint:gauge}} + +eval_warn instant at 20m idelta(http_requests_histogram{path="/d"}[20m]) + {path="/d"} {{sum:1 count:1 counter_reset_hint:gauge}} + +eval_warn instant at 20m idelta(http_requests_histogram{path="/e"}[20m]) + +eval_warn instant at 20m idelta(http_requests_histogram{path="/f"}[20m]) + +eval_warn instant at 20m idelta(http_requests_histogram{path="/g"}[20m]) + clear # Tests for deriv() and predict_linear(). @@ -410,7 +466,7 @@ eval instant at 0m label_replace(testmetric, "dst", "", "dst", ".*") eval_fail instant at 0m label_replace(testmetric, "dst", "value-$1", "src", "(.*") # label_replace fails when the destination label name is not a valid Prometheus label name. -eval_fail instant at 0m label_replace(testmetric, "invalid-label-name", "", "src", "(.*)") +eval_fail instant at 0m label_replace(testmetric, "\xff", "", "src", "(.*)") # label_replace fails when there would be duplicated identical output label sets. eval_fail instant at 0m label_replace(testmetric, "src", "", "", "") @@ -443,6 +499,8 @@ eval instant at 20s timestamp(metric) load 5m testmetric{src="a",src1="b",src2="c",dst="original-destination-value"} 0 testmetric{src="d",src1="e",src2="f",dst="original-destination-value"} 1 + dup{label="a", this="a"} 1.0 + dup{label="b", this="a"} 1.0 # label_join joins all src values in order. eval instant at 0m label_join(testmetric, "dst", "-", "src", "src1", "src2") @@ -474,6 +532,9 @@ eval instant at 0m label_join(testmetric1, "dst", ", ", "src", "src1", "src2") testmetric1{src="foo",src1="bar",src2="foobar",dst="foo, bar, foobar"} 0 testmetric1{src="fizz",src1="buzz",src2="fizzbuzz",dst="fizz, buzz, fizzbuzz"} 1 +eval_fail instant at 0m label_join(dup, "label", "", "this") + expected_fail_message vector cannot contain metrics with the same labelset + clear # Tests for vector. @@ -581,6 +642,7 @@ load 5m http_requests{job="app-server", instance="1", group="production"} 0+60x10 http_requests{job="app-server", instance="0", group="canary"} 0+70x10 http_requests{job="app-server", instance="1", group="canary"} 0+80x10 + http_requests{job="app-server", instance="2", group="canary"} {{schema:0 sum:1 count:1}}x15 eval_ordered instant at 50m sort(http_requests) http_requests{group="production", instance="0", job="api-server"} 100 @@ -1591,3 +1653,22 @@ load 1m eval range from 0 to 5m step 1m round(mixed_metric) {} _ 1 2 3 + +# Test scalar() with histograms. +load 1m + metric{type="float", l="x"} 1 + metric{type="float", l="y"} 2 + metric{type="histogram", l="x"} {{schema:0 sum:1 count:1}} + metric{type="histogram", l="x"} {{schema:0 sum:1 count:1}} + +# Two floats in the vector. +eval instant at 0m scalar(metric) + NaN + +# No floats in the vector. +eval instant at 0m scalar({type="histogram"}) + NaN + +# One float in the vector. +eval instant at 0m scalar({l="x"}) + 1 diff --git a/vendor/github.com/prometheus/prometheus/promql/promqltest/testdata/native_histograms.test b/vendor/github.com/prometheus/prometheus/promql/promqltest/testdata/native_histograms.test index f03b39a9f68..dd119c0617c 100644 --- a/vendor/github.com/prometheus/prometheus/promql/promqltest/testdata/native_histograms.test +++ b/vendor/github.com/prometheus/prometheus/promql/promqltest/testdata/native_histograms.test @@ -1013,7 +1013,7 @@ eval instant at 5m sum(custom_buckets_histogram) clear -# Test 'this native histogram metric is not a gauge' warning for rate +# Test 'this native histogram metric is not a counter' warning for rate load 30s some_metric {{schema:0 sum:1 count:1 buckets:[1] counter_reset_hint:gauge}} {{schema:0 sum:2 count:2 buckets:[2] counter_reset_hint:gauge}} {{schema:0 sum:3 count:3 buckets:[3] counter_reset_hint:gauge}} @@ -1022,7 +1022,7 @@ eval_warn instant at 30s rate(some_metric[1m]) {} {{count:0.03333333333333333 sum:0.03333333333333333 buckets:[0.03333333333333333]}} # Test the case where we have more than two points for rate -eval_warn instant at 1m rate(some_metric[1m]) +eval_warn instant at 1m rate(some_metric[1m30s]) {} {{count:0.03333333333333333 sum:0.03333333333333333 buckets:[0.03333333333333333]}} clear @@ -1032,20 +1032,20 @@ load 30s some_metric {{schema:0 sum:1 count:1 buckets:[1]}} {{schema:-53 sum:1 count:1 custom_values:[5 10] buckets:[1]}} {{schema:0 sum:5 count:4 buckets:[1 2 1]}} {{schema:-53 sum:1 count:1 custom_values:[5 10] buckets:[1]}} # Start and end with exponential, with custom in the middle. -eval_warn instant at 1m rate(some_metric[1m]) +eval_warn instant at 1m rate(some_metric[1m30s]) # Should produce no results. # Start and end with custom, with exponential in the middle. -eval_warn instant at 1m30s rate(some_metric[1m]) +eval_warn instant at 1m30s rate(some_metric[1m30s]) # Should produce no results. -# Start with custom, end with exponential. -eval_warn instant at 1m rate(some_metric[1m]) - # Should produce no results. +# Start with custom, end with exponential. Return the exponential histogram divided by 30. +eval instant at 1m rate(some_metric[1m]) + {} {{schema:0 sum:0.16666666666666666 count:0.13333333333333333 buckets:[0.03333333333333333 0.06666666666666667 0.03333333333333333]}} -# Start with exponential, end with custom. -eval_warn instant at 30s rate(some_metric[1m]) - # Should produce no results. +# Start with exponential, end with custom. Return the custom buckets histogram divided by 30. +eval instant at 30s rate(some_metric[1m]) + {} {{schema:-53 sum:0.03333333333333333 count:0.03333333333333333 custom_values:[5 10] buckets:[0.03333333333333333]}} clear @@ -1179,7 +1179,10 @@ eval_info range from 0 to 6m step 6m metric2 > metric2 clear load 6m - nhcb_metric {{schema:-53 sum:1 count:1 custom_values:[2] buckets:[1]}} {{schema:-53 sum:1 count:1 custom_values:[5 10] buckets:[1]}} {{schema:-53 sum:1 count:1 custom_values:[5 10] buckets:[1]}} + nhcb_metric {{schema:-53 sum:1 count:1 custom_values:[2] buckets:[1]}} {{schema:-53 sum:1 count:1 custom_values:[2] buckets:[1]}} {{schema:-53 sum:1 count:1 custom_values:[5 10] buckets:[1]}} {{schema:-53 sum:1 count:1 custom_values:[5 10] buckets:[1]}} + +# If evaluating at 12m, the first two NHCBs have the same custom values +# while the 3rd one has different ones. eval_warn instant at 12m sum_over_time(nhcb_metric[13m]) @@ -1206,6 +1209,38 @@ eval_warn instant at 12m rate(nhcb_metric[13m]) eval instant at 12m resets(nhcb_metric[13m]) {} 1 +# Now doing the same again, but at 18m, where the first NHCB has +# different custom_values compared to the other two. This now +# works with no warning for increase() and rate(). No change +# otherwise. + +eval_warn instant at 18m sum_over_time(nhcb_metric[13m]) + +eval_warn instant at 18m avg_over_time(nhcb_metric[13m]) + +eval instant at 18m last_over_time(nhcb_metric[13m]) +nhcb_metric{} {{schema:-53 sum:1 count:1 custom_values:[5 10] buckets:[1]}} + +eval instant at 18m count_over_time(nhcb_metric[13m]) +{} 3 + +eval instant at 18m present_over_time(nhcb_metric[13m]) +{} 1 + +eval instant at 18m changes(nhcb_metric[13m]) +{} 1 + +eval_warn instant at 18m delta(nhcb_metric[13m]) + +eval instant at 18m increase(nhcb_metric[13m]) +{} {{schema:-53 count:1.0833333333333333 sum:1.0833333333333333 custom_values:[5 10] buckets:[1.0833333333333333]}} + +eval instant at 18m rate(nhcb_metric[13m]) +{} {{schema:-53 count:0.0013888888888888887 sum:0.0013888888888888887 custom_values:[5 10] buckets:[0.0013888888888888887]}} + +eval instant at 18m resets(nhcb_metric[13m]) +{} 1 + clear load 1m diff --git a/vendor/github.com/prometheus/prometheus/tsdb/db.go b/vendor/github.com/prometheus/prometheus/tsdb/db.go index 69278947aca..d8844bbef7b 100644 --- a/vendor/github.com/prometheus/prometheus/tsdb/db.go +++ b/vendor/github.com/prometheus/prometheus/tsdb/db.go @@ -1075,9 +1075,14 @@ func open(dir string, l *slog.Logger, r prometheus.Registerer, opts *Options, rn db.metrics.maxBytes.Set(float64(maxBytes)) db.metrics.retentionDuration.Set((time.Duration(opts.RetentionDuration) * time.Millisecond).Seconds()) + // Calling db.reload() calls db.reloadBlocks() which requires cmtx to be locked. + db.cmtx.Lock() if err := db.reload(); err != nil { + db.cmtx.Unlock() return nil, err } + db.cmtx.Unlock() + // Set the min valid time for the ingested samples // to be no lower than the maxt of the last block. minValidTime := int64(math.MinInt64) @@ -1458,6 +1463,7 @@ func (db *DB) CompactOOOHead(ctx context.Context) error { // Callback for testing. var compactOOOHeadTestingCallback func() +// The db.cmtx mutex should be held before calling this method. func (db *DB) compactOOOHead(ctx context.Context) error { if !db.oooWasEnabled.Load() { return nil @@ -1512,6 +1518,7 @@ func (db *DB) compactOOOHead(ctx context.Context) error { // compactOOO creates a new block per possible block range in the compactor's directory from the OOO Head given. // Each ULID in the result corresponds to a block in a unique time range. +// The db.cmtx mutex should be held before calling this method. func (db *DB) compactOOO(dest string, oooHead *OOOCompactionHead) (_ []ulid.ULID, err error) { start := time.Now() @@ -1578,7 +1585,7 @@ func (db *DB) compactOOO(dest string, oooHead *OOOCompactionHead) (_ []ulid.ULID } // compactHead compacts the given RangeHead. -// The compaction mutex should be held before calling this method. +// The db.cmtx should be held before calling this method. func (db *DB) compactHead(head *RangeHead, truncateMemory bool) error { uids, err := db.compactor.Write(db.dir, head, head.MinTime(), head.BlockMaxTime(), nil) if err != nil { @@ -1607,7 +1614,7 @@ func (db *DB) compactHead(head *RangeHead, truncateMemory bool) error { } // compactBlocks compacts all the eligible on-disk blocks. -// The compaction mutex should be held before calling this method. +// The db.cmtx should be held before calling this method. func (db *DB) compactBlocks() (err error) { // Check for compactions of multiple blocks. for { @@ -1664,6 +1671,7 @@ func getBlock(allBlocks []*Block, id ulid.ULID) (*Block, bool) { } // reload reloads blocks and truncates the head and its WAL. +// The db.cmtx mutex should be held before calling this method. func (db *DB) reload() error { if err := db.reloadBlocks(); err != nil { return fmt.Errorf("reloadBlocks: %w", err) @@ -1680,6 +1688,7 @@ func (db *DB) reload() error { // reloadBlocks reloads blocks without touching head. // Blocks that are obsolete due to replacement or retention will be deleted. +// The db.cmtx mutex should be held before calling this method. func (db *DB) reloadBlocks() (err error) { defer func() { if err != nil { @@ -1688,13 +1697,9 @@ func (db *DB) reloadBlocks() (err error) { db.metrics.reloads.Inc() }() - // Now that we reload TSDB every minute, there is a high chance for a race condition with a reload - // triggered by CleanTombstones(). We need to lock the reload to avoid the situation where - // a normal reload and CleanTombstones try to delete the same block. - db.mtx.Lock() - defer db.mtx.Unlock() - + db.mtx.RLock() loadable, corrupted, err := openBlocks(db.logger, db.dir, db.blocks, db.chunkPool, db.opts.PostingsDecoderFactory, db.opts.SeriesHashCache, db.opts.BlockPostingsForMatchersCacheTTL, db.opts.BlockPostingsForMatchersCacheMaxItems, db.opts.BlockPostingsForMatchersCacheMaxBytes, db.opts.BlockPostingsForMatchersCacheForce, db.opts.BlockPostingsForMatchersCacheMetrics) + db.mtx.RUnlock() if err != nil { return err } @@ -1720,11 +1725,13 @@ func (db *DB) reloadBlocks() (err error) { if len(corrupted) > 0 { // Corrupted but no child loaded for it. // Close all new blocks to release the lock for windows. + db.mtx.RLock() for _, block := range loadable { if _, open := getBlock(db.blocks, block.Meta().ULID); !open { block.Close() } } + db.mtx.RUnlock() errs := tsdb_errors.NewMulti() for ulid, err := range corrupted { if err != nil { @@ -1763,8 +1770,10 @@ func (db *DB) reloadBlocks() (err error) { }) // Swap new blocks first for subsequently created readers to be seen. + db.mtx.Lock() oldBlocks := db.blocks db.blocks = toLoad + db.mtx.Unlock() // Only check overlapping blocks when overlapping compaction is enabled. if db.opts.EnableOverlappingCompaction { diff --git a/vendor/github.com/prometheus/prometheus/tsdb/head_read.go b/vendor/github.com/prometheus/prometheus/tsdb/head_read.go index 675639db0b0..5a491386488 100644 --- a/vendor/github.com/prometheus/prometheus/tsdb/head_read.go +++ b/vendor/github.com/prometheus/prometheus/tsdb/head_read.go @@ -121,15 +121,19 @@ func (h *headIndexReader) PostingsForMatchers(ctx context.Context, concurrent bo func (h *headIndexReader) SortedPostings(p index.Postings) index.Postings { series := make([]*memSeries, 0, 128) + notFoundSeriesCount := 0 // Fetch all the series only once. for p.Next() { s := h.head.series.getByID(chunks.HeadSeriesRef(p.At())) if s == nil { - h.head.logger.Debug("Looked up series not found") + notFoundSeriesCount++ } else { series = append(series, s) } } + if notFoundSeriesCount > 0 { + h.head.logger.Debug("Looked up series not found", "count", notFoundSeriesCount) + } if err := p.Err(); err != nil { return index.ErrPostings(fmt.Errorf("expand postings: %w", err)) } @@ -154,11 +158,12 @@ func (h *headIndexReader) ShardedPostings(p index.Postings, shardIndex, shardCou } out := make([]storage.SeriesRef, 0, 128) + notFoundSeriesCount := 0 for p.Next() { s := h.head.series.getByID(chunks.HeadSeriesRef(p.At())) if s == nil { - h.head.logger.Debug("Looked up series not found") + notFoundSeriesCount++ continue } @@ -169,6 +174,9 @@ func (h *headIndexReader) ShardedPostings(p index.Postings, shardIndex, shardCou out = append(out, storage.SeriesRef(s.ref)) } + if notFoundSeriesCount > 0 { + h.head.logger.Debug("Looked up series not found", "count", notFoundSeriesCount) + } return index.NewListPostings(out) } diff --git a/vendor/github.com/prometheus/prometheus/util/annotations/annotations.go b/vendor/github.com/prometheus/prometheus/util/annotations/annotations.go index 5b2fde152bd..95783957a7e 100644 --- a/vendor/github.com/prometheus/prometheus/util/annotations/annotations.go +++ b/vendor/github.com/prometheus/prometheus/util/annotations/annotations.go @@ -143,6 +143,7 @@ var ( NativeHistogramNotGaugeWarning = fmt.Errorf("%w: this native histogram metric is not a gauge:", PromQLWarning) MixedExponentialCustomHistogramsWarning = fmt.Errorf("%w: vector contains a mix of histograms with exponential and custom buckets schemas for metric name", PromQLWarning) IncompatibleCustomBucketsHistogramsWarning = fmt.Errorf("%w: vector contains histograms with incompatible custom buckets for metric name", PromQLWarning) + IncompatibleBucketLayoutInBinOpWarning = fmt.Errorf("%w: incompatible bucket layout encountered for binary operator", PromQLWarning) PossibleNonCounterInfo = fmt.Errorf("%w: metric might not be a counter, name does not end in _total/_sum/_count/_bucket:", PromQLInfo) HistogramQuantileForcedMonotonicityInfo = fmt.Errorf("%w: input to histogram_quantile needed to be fixed for monotonicity (see https://prometheus.io/docs/prometheus/latest/querying/functions/#histogram_quantile) for metric name", PromQLInfo) @@ -295,9 +296,20 @@ func NewHistogramIgnoredInAggregationInfo(aggregation string, pos posrange.Posit } } +// NewHistogramIgnoredInMixedRangeInfo is used when a histogram is ignored +// in a range vector which contains mix of floats and histograms. func NewHistogramIgnoredInMixedRangeInfo(metricName string, pos posrange.PositionRange) error { return annoErr{ PositionRange: pos, Err: fmt.Errorf("%w %q", HistogramIgnoredInMixedRangeInfo, metricName), } } + +// NewIncompatibleBucketLayoutInBinOpWarning is used if binary operators act on a +// combination of two incompatible histograms. +func NewIncompatibleBucketLayoutInBinOpWarning(operator string, pos posrange.PositionRange) error { + return annoErr{ + PositionRange: pos, + Err: fmt.Errorf("%w %s", IncompatibleBucketLayoutInBinOpWarning, operator), + } +} diff --git a/vendor/github.com/prometheus/sigv4/Makefile.common b/vendor/github.com/prometheus/sigv4/Makefile.common index fc47bdbb215..d1576bb3133 100644 --- a/vendor/github.com/prometheus/sigv4/Makefile.common +++ b/vendor/github.com/prometheus/sigv4/Makefile.common @@ -61,7 +61,7 @@ PROMU_URL := https://github.com/prometheus/promu/releases/download/v$(PROMU_ SKIP_GOLANGCI_LINT := GOLANGCI_LINT := GOLANGCI_LINT_OPTS ?= -GOLANGCI_LINT_VERSION ?= v1.62.0 +GOLANGCI_LINT_VERSION ?= v1.63.4 # golangci-lint only supports linux, darwin and windows platforms on i386/amd64/arm64. # windows isn't included here because of the path separator being different. ifeq ($(GOHOSTOS),$(filter $(GOHOSTOS),linux darwin)) diff --git a/vendor/google.golang.org/api/internal/version.go b/vendor/google.golang.org/api/internal/version.go index e55b0e5b710..fad0d7dbf95 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.218.0" +const Version = "0.219.0" diff --git a/vendor/google.golang.org/api/option/option.go b/vendor/google.golang.org/api/option/option.go index eb54813aae6..e3321ca4a66 100644 --- a/vendor/google.golang.org/api/option/option.go +++ b/vendor/google.golang.org/api/option/option.go @@ -44,6 +44,14 @@ func (w withCredFile) Apply(o *internal.DialSettings) { // WithCredentialsFile returns a ClientOption that authenticates // API calls with the given service account or refresh token JSON // credentials file. +// +// Important: If you accept a credential configuration (credential +// JSON/File/Stream) from an external source for authentication to Google +// Cloud Platform, you must validate it before providing it to any Google +// API or library. Providing an unvalidated credential configuration to +// Google APIs can compromise the security of your systems and data. For +// more information, refer to [Validate credential configurations from +// external sources](https://cloud.google.com/docs/authentication/external/externally-sourced-credentials). func WithCredentialsFile(filename string) ClientOption { return withCredFile(filename) } @@ -51,6 +59,14 @@ func WithCredentialsFile(filename string) ClientOption { // WithServiceAccountFile returns a ClientOption that uses a Google service // account credentials file to authenticate. // +// Important: If you accept a credential configuration (credential +// JSON/File/Stream) from an external source for authentication to Google +// Cloud Platform, you must validate it before providing it to any Google +// API or library. Providing an unvalidated credential configuration to +// Google APIs can compromise the security of your systems and data. For +// more information, refer to [Validate credential configurations from +// external sources](https://cloud.google.com/docs/authentication/external/externally-sourced-credentials). +// // Deprecated: Use WithCredentialsFile instead. func WithServiceAccountFile(filename string) ClientOption { return WithCredentialsFile(filename) @@ -59,6 +75,14 @@ func WithServiceAccountFile(filename string) ClientOption { // WithCredentialsJSON returns a ClientOption that authenticates // API calls with the given service account or refresh token JSON // credentials. +// +// Important: If you accept a credential configuration (credential +// JSON/File/Stream) from an external source for authentication to Google +// Cloud Platform, you must validate it before providing it to any Google +// API or library. Providing an unvalidated credential configuration to +// Google APIs can compromise the security of your systems and data. For +// more information, refer to [Validate credential configurations from +// external sources](https://cloud.google.com/docs/authentication/external/externally-sourced-credentials). func WithCredentialsJSON(p []byte) ClientOption { return withCredentialsJSON(p) } diff --git a/vendor/modules.txt b/vendor/modules.txt index 74273bc5b34..00097bdbd48 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -937,15 +937,14 @@ github.com/oklog/ulid # github.com/okzk/sdnotify v0.0.0-20240725214427-1c1fdd37c5ac ## explicit github.com/okzk/sdnotify -# github.com/open-telemetry/opentelemetry-collector-contrib/internal/exp/metrics v0.116.0 +# github.com/open-telemetry/opentelemetry-collector-contrib/internal/exp/metrics v0.118.0 ## explicit; go 1.22.0 github.com/open-telemetry/opentelemetry-collector-contrib/internal/exp/metrics/identity github.com/open-telemetry/opentelemetry-collector-contrib/internal/exp/metrics/staleness -github.com/open-telemetry/opentelemetry-collector-contrib/internal/exp/metrics/streams -# github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil v0.116.0 +# github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil v0.118.0 ## explicit; go 1.22.0 github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil -# github.com/open-telemetry/opentelemetry-collector-contrib/processor/deltatocumulativeprocessor v0.116.0 +# github.com/open-telemetry/opentelemetry-collector-contrib/processor/deltatocumulativeprocessor v0.118.0 ## explicit; go 1.22.0 github.com/open-telemetry/opentelemetry-collector-contrib/processor/deltatocumulativeprocessor github.com/open-telemetry/opentelemetry-collector-contrib/processor/deltatocumulativeprocessor/internal/data @@ -1076,7 +1075,7 @@ github.com/prometheus/exporter-toolkit/web github.com/prometheus/procfs github.com/prometheus/procfs/internal/fs github.com/prometheus/procfs/internal/util -# github.com/prometheus/prometheus v1.99.0 => github.com/grafana/mimir-prometheus v0.0.0-20250306234455-f6f6f2cceada +# github.com/prometheus/prometheus v1.99.0 => github.com/grafana/mimir-prometheus v0.0.0-20250307031102-9ba23e267c3d ## explicit; go 1.22.7 github.com/prometheus/prometheus/config github.com/prometheus/prometheus/discovery @@ -1137,7 +1136,7 @@ github.com/prometheus/prometheus/util/teststorage github.com/prometheus/prometheus/util/testutil github.com/prometheus/prometheus/util/zeropool github.com/prometheus/prometheus/web/api/v1 -# github.com/prometheus/sigv4 v0.1.1 +# github.com/prometheus/sigv4 v0.1.2 ## explicit; go 1.21 github.com/prometheus/sigv4 # github.com/rainycape/unidecode v0.0.0-20150907023854-cb7f23ec59be @@ -1536,7 +1535,7 @@ golang.org/x/tools/internal/stdlib golang.org/x/tools/internal/typeparams golang.org/x/tools/internal/typesinternal golang.org/x/tools/internal/versions -# google.golang.org/api v0.218.0 +# google.golang.org/api v0.219.0 ## explicit; go 1.22 google.golang.org/api/googleapi google.golang.org/api/googleapi/transport @@ -1761,7 +1760,7 @@ sigs.k8s.io/kustomize/kyaml/yaml/walk sigs.k8s.io/yaml sigs.k8s.io/yaml/goyaml.v2 sigs.k8s.io/yaml/goyaml.v3 -# github.com/prometheus/prometheus => github.com/grafana/mimir-prometheus v0.0.0-20250306234455-f6f6f2cceada +# github.com/prometheus/prometheus => github.com/grafana/mimir-prometheus v0.0.0-20250307031102-9ba23e267c3d # github.com/hashicorp/memberlist => github.com/grafana/memberlist v0.3.1-0.20220714140823-09ffed8adbbe # gopkg.in/yaml.v3 => github.com/colega/go-yaml-yaml v0.0.0-20220720105220-255a8d16d094 # github.com/grafana/regexp => github.com/grafana/regexp v0.0.0-20240531075221-3685f1377d7b