diff --git a/.mdox.validate.yaml b/.mdox.validate.yaml index ba983aadc21..4a3edc4dbb1 100644 --- a/.mdox.validate.yaml +++ b/.mdox.validate.yaml @@ -47,3 +47,6 @@ validators: # Expired certificate - regex: 'bestpractices\.coreinfrastructure\.org\/projects\/3048' type: 'ignore' + # Frequent DNS issues. + - regex: 'build\.thebeat\.co' + type: 'ignore' diff --git a/CHANGELOG.md b/CHANGELOG.md index 24206ec32bf..f89c47c8552 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,16 @@ We use *breaking :warning:* to mark changes that are not backward compatible (re ### Fixed +### Added + +### Changed + +### Removed + +## [v0.37.0 - ](https://github.com/thanos-io/thanos/tree/release-0.37) + +### Fixed + - [#7511](https://github.com/thanos-io/thanos/pull/7511) Query Frontend: fix doubled gzip compression for response body. - [#7592](https://github.com/thanos-io/thanos/pull/7592) Ruler: Only increment `thanos_rule_evaluation_with_warnings_total` metric for non PromQL warnings. - [#7614](https://github.com/thanos-io/thanos/pull/7614) *: fix debug log formatting. @@ -26,6 +36,8 @@ We use *breaking :warning:* to mark changes that are not backward compatible (re - [#7852](https://github.com/thanos-io/thanos/pull/7852) Query Frontend: pass "stats" parameter forward to queriers and fix Prometheus stats merging. - [#7832](https://github.com/thanos-io/thanos/pull/7832) Query Frontend: Fix cache keys for dynamic split intervals. - [#7885](https://github.com/thanos-io/thanos/pull/7885) Store: Return chunks to the pool after completing a Series call. +- [#7893](https://github.com/thanos-io/thanos/pull/7893) Sidecar: Fix retrieval of external labels for Prometheus v3.0.0. +- [#7903](https://github.com/thanos-io/thanos/pull/7903) Query: Fix panic on regex store matchers. ### Added - [#7763](https://github.com/thanos-io/thanos/pull/7763) Ruler: use native histograms for client latency metrics. diff --git a/VERSION b/VERSION index 8aa7336bf08..c05b0f07860 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.37.0-dev +0.38.0-dev diff --git a/cmd/thanos/main_test.go b/cmd/thanos/main_test.go index fcc394a6149..6cbd84cb152 100644 --- a/cmd/thanos/main_test.go +++ b/cmd/thanos/main_test.go @@ -105,6 +105,16 @@ func (b *erroringBucket) Name() string { return b.bkt.Name() } +// IterWithAttributes allows to iterate over objects in the bucket with their attributes. +func (b *erroringBucket) IterWithAttributes(ctx context.Context, dir string, f func(objstore.IterObjectAttributes) error, options ...objstore.IterOption) error { + return b.bkt.IterWithAttributes(ctx, dir, f, options...) +} + +// SupportedIterOptions returns the supported iteration options. +func (b *erroringBucket) SupportedIterOptions() []objstore.IterOptionType { + return b.bkt.SupportedIterOptions() +} + // Ensures that downsampleBucket() stops its work properly // after an error occurs with some blocks in the backlog. // Testing for https://github.com/thanos-io/thanos/issues/4960. diff --git a/cmd/thanos/store.go b/cmd/thanos/store.go index b83a018b062..795c108cd3f 100644 --- a/cmd/thanos/store.go +++ b/cmd/thanos/store.go @@ -21,6 +21,7 @@ import ( "github.com/prometheus/client_golang/prometheus" commonmodel "github.com/prometheus/common/model" "github.com/prometheus/common/route" + "gopkg.in/yaml.v2" "github.com/thanos-io/objstore" "github.com/thanos-io/objstore/client" @@ -32,6 +33,7 @@ import ( "github.com/thanos-io/thanos/pkg/block/metadata" "github.com/thanos-io/thanos/pkg/component" hidden "github.com/thanos-io/thanos/pkg/extflag" + "github.com/thanos-io/thanos/pkg/exthttp" "github.com/thanos-io/thanos/pkg/extkingpin" "github.com/thanos-io/thanos/pkg/extprom" extpromhttp "github.com/thanos-io/thanos/pkg/extprom/http" @@ -308,8 +310,11 @@ func runStore( if err != nil { return err } - - bkt, err := client.NewBucket(logger, confContentYaml, conf.component.String(), nil) + customBktConfig := exthttp.DefaultCustomBucketConfig() + if err := yaml.Unmarshal(confContentYaml, &customBktConfig); err != nil { + return errors.Wrap(err, "parsing config YAML file") + } + bkt, err := client.NewBucket(logger, confContentYaml, conf.component.String(), exthttp.CreateHedgedTransportWithConfig(customBktConfig)) if err != nil { return err } diff --git a/docs/components/receive.md b/docs/components/receive.md index 4082bee625b..cd0dc322c73 100644 --- a/docs/components/receive.md +++ b/docs/components/receive.md @@ -113,6 +113,7 @@ config: insecure_skip_verify: false disable_compression: false chunk_size_bytes: 0 + max_retries: 0 prefix: "" ``` diff --git a/docs/components/sidecar.md b/docs/components/sidecar.md index d8e9cb930d4..01295a5d4db 100644 --- a/docs/components/sidecar.md +++ b/docs/components/sidecar.md @@ -75,6 +75,7 @@ config: insecure_skip_verify: false disable_compression: false chunk_size_bytes: 0 + max_retries: 0 prefix: "" ``` diff --git a/docs/components/store.md b/docs/components/store.md index f9f70b42a17..34250001469 100644 --- a/docs/components/store.md +++ b/docs/components/store.md @@ -34,6 +34,7 @@ config: insecure_skip_verify: false disable_compression: false chunk_size_bytes: 0 + max_retries: 0 prefix: "" ``` @@ -576,6 +577,33 @@ Note that there must be no trailing slash in the `peers` configuration i.e. one If timeout is set to zero then there is no timeout for fetching and fetching's lifetime is equal to the lifetime to the original request's lifetime. It is recommended to keep it higher than zero. It is generally preferred to keep this value higher because the fetching operation potentially includes loading of data from remote object storage. +## Hedged Requests + +Thanos Store Gateway supports `hedged requests` to enhance performance and reliability, particularly in high-latency environments. This feature addresses `long-tail latency issues` that can occur between the Thanos Store Gateway and an external cache, reducing the impact of slower response times on overall performance. + +The configuration options for hedged requests allow for tuning based on latency tolerance and cost considerations, as some providers may charge per request. + +In the `bucket.yml` file, you can specify the following fields under `hedging_config`: + +- `enabled`: bool to enable hedged requests. +- `up_to`: maximum number of hedged requests allowed for each initial request. + - **Purpose**: controls the redundancy level of hedged requests to improve response times. + - **Cost vs. Benefit**: increasing up_to can reduce latency but may increase costs, as some providers charge per request. Higher values provide diminishing returns on latency beyond a certain level. +- `quantile`: latency threshold, specified as a quantile (e.g., percentile), which determines when additional hedged requests should be sent. + - **Purpose**: controls when hedged requests are triggered based on response time distribution. + - **Cost vs. Benefit**: lower quantile (e.g., 0.7) initiates hedged requests sooner, potentially raising costs while lowering latency variance. A higher quantile (e.g., 0.95) will initiate hedged requests later, reducing cost by limiting redundancy. + +By default, `hedging_config` is set as follows: + +```yaml +hedging_config: + enabled: false + up_to: 3 + quantile: 0.9 +``` + +This configuration sends up to three additional requests if the initial request response time exceeds the 90th percentile. + ## Index Header In order to query series inside blocks from object storage, Store Gateway has to know certain initial info from each block index. In order to achieve so, on startup the Gateway builds an `index-header` for each block and stores it on local disk; such `index-header` is build by downloading specific pieces of original block's index, stored on local disk and then mmaped and used by Store Gateway. diff --git a/docs/components/tools.md b/docs/components/tools.md index 426861fdff8..7e34ab4c275 100644 --- a/docs/components/tools.md +++ b/docs/components/tools.md @@ -129,6 +129,7 @@ config: insecure_skip_verify: false disable_compression: false chunk_size_bytes: 0 + max_retries: 0 prefix: "" ``` @@ -700,6 +701,7 @@ config: insecure_skip_verify: false disable_compression: false chunk_size_bytes: 0 + max_retries: 0 prefix: "" ``` @@ -803,6 +805,7 @@ config: insecure_skip_verify: false disable_compression: false chunk_size_bytes: 0 + max_retries: 0 prefix: "" ``` diff --git a/docs/storage.md b/docs/storage.md index 78aacb38e98..48033629a2b 100644 --- a/docs/storage.md +++ b/docs/storage.md @@ -103,6 +103,7 @@ config: kms_encryption_context: {} encryption_key: "" sts_endpoint: "" + max_retries: 0 prefix: "" ``` @@ -305,6 +306,7 @@ config: insecure_skip_verify: false disable_compression: false chunk_size_bytes: 0 + max_retries: 0 prefix: "" ``` @@ -495,6 +497,7 @@ config: endpoint: "" secret_key: "" secret_id: "" + max_retries: 0 http_config: idle_conn_timeout: 1m30s response_header_timeout: 2m diff --git a/go.mod b/go.mod index 87d174294a8..c33f922e2d1 100644 --- a/go.mod +++ b/go.mod @@ -36,12 +36,12 @@ require ( github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 github.com/jpillora/backoff v1.0.0 github.com/json-iterator/go v1.1.12 - github.com/klauspost/compress v1.17.9 + github.com/klauspost/compress v1.17.11 github.com/leanovate/gopter v0.2.9 github.com/lightstep/lightstep-tracer-go v0.25.0 github.com/lovoo/gcloud-opentracing v0.3.0 github.com/miekg/dns v1.1.62 - github.com/minio/minio-go/v7 v7.0.72 // indirect + github.com/minio/minio-go/v7 v7.0.80 // indirect github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f github.com/oklog/run v1.1.0 github.com/oklog/ulid v1.3.1 @@ -53,7 +53,7 @@ require ( github.com/pkg/errors v0.9.1 github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/prometheus/alertmanager v0.27.0 - github.com/prometheus/client_golang v1.20.4 + github.com/prometheus/client_golang v1.20.5 github.com/prometheus/client_model v0.6.1 github.com/prometheus/common v0.60.0 github.com/prometheus/exporter-toolkit v0.12.0 @@ -61,8 +61,8 @@ require ( github.com/prometheus/prometheus v0.55.1-0.20241102120812-a6fd22b9d2c8 github.com/sony/gobreaker v0.5.0 github.com/stretchr/testify v1.9.0 - github.com/thanos-io/objstore v0.0.0-20241028150459-cfdd0e50390d - github.com/thanos-io/promql-engine v0.0.0-20240921092401-37747eddbd31 + github.com/thanos-io/objstore v0.0.0-20241111205755-d1dd89d41f97 + github.com/thanos-io/promql-engine v0.0.0-20241106100125-097e6e9f425a github.com/uber/jaeger-client-go v2.30.0+incompatible github.com/uber/jaeger-lib v2.4.1+incompatible // indirect github.com/vimeo/galaxycache v0.0.0-20210323154928-b7e5d71c067a @@ -138,6 +138,7 @@ require ( github.com/containerd/cgroups/v3 v3.0.3 // indirect github.com/docker/go-units v0.5.0 // indirect github.com/elastic/go-licenser v0.3.1 // indirect + github.com/go-ini/ini v1.67.0 // indirect github.com/go-openapi/runtime v0.27.1 // indirect github.com/goccy/go-json v0.10.3 // indirect github.com/godbus/dbus/v5 v5.0.4 // indirect @@ -145,6 +146,7 @@ require ( github.com/google/s2a-go v0.1.8 // indirect github.com/huaweicloud/huaweicloud-sdk-go-obs v3.23.3+incompatible // indirect github.com/jcchavezs/porto v0.1.0 // indirect + github.com/leesper/go_rng v0.0.0-20190531154944-a612b043e353 // indirect github.com/mdlayher/socket v0.4.1 // indirect github.com/mdlayher/vsock v1.2.1 // indirect github.com/metalmatze/signal v0.0.0-20210307161603-1c9aa721a97a // indirect @@ -192,10 +194,12 @@ require ( github.com/aws/smithy-go v1.11.1 // indirect github.com/baidubce/bce-sdk-go v0.9.111 // indirect github.com/beorn7/perks v1.0.1 // indirect + github.com/caio/go-tdigest v3.1.0+incompatible github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/chromedp/sysutil v1.0.0 // indirect github.com/clbanning/mxj v1.8.4 // indirect github.com/coreos/go-systemd/v22 v22.5.0 // indirect + github.com/cristalhq/hedgedhttp v0.9.1 github.com/dennwc/varint v1.0.0 // indirect github.com/edsrzf/mmap-go v1.1.0 // indirect github.com/elastic/go-sysinfo v1.8.1 // indirect @@ -248,7 +252,7 @@ require ( github.com/prometheus/procfs v0.15.1 // indirect github.com/redis/rueidis v1.0.45-alpha.1 github.com/rivo/uniseg v0.2.0 // indirect - github.com/rs/xid v1.5.0 // indirect + github.com/rs/xid v1.6.0 // indirect github.com/santhosh-tekuri/jsonschema v1.2.4 // indirect github.com/shirou/gopsutil/v3 v3.22.9 // indirect github.com/sirupsen/logrus v1.9.3 // indirect @@ -276,7 +280,6 @@ require ( golang.org/x/tools v0.24.0 // indirect gonum.org/v1/gonum v0.15.0 // indirect google.golang.org/protobuf v1.35.1 - gopkg.in/ini.v1 v1.67.0 // indirect howett.net/plist v0.0.0-20181124034731-591f970eefbb // indirect ) diff --git a/go.sum b/go.sum index 2dd75164a9e..9250fff8b87 100644 --- a/go.sum +++ b/go.sum @@ -1452,6 +1452,8 @@ github.com/bluele/gcache v0.0.2 h1:WcbfdXICg7G/DGBh1PFfcirkWOQV+v077yF1pSy3DGw= github.com/bluele/gcache v0.0.2/go.mod h1:m15KV+ECjptwSPxKhOhQoAFQVtUFjTVkc3H8o0t/fp0= github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= github.com/boombuler/barcode v1.0.1/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= +github.com/caio/go-tdigest v3.1.0+incompatible h1:uoVMJ3Q5lXmVLCCqaMGHLBWnbGoN6Lpu7OAUPR60cds= +github.com/caio/go-tdigest v3.1.0+incompatible/go.mod h1:sHQM/ubZStBUmF1WbB8FAm8q9GjDajLC5T7ydxE3JHI= github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= @@ -1508,6 +1510,8 @@ github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSV github.com/cortexproject/promqlsmith v0.0.0-20240506042652-6cfdd9739a5e h1:nOWmgQD3L/Z0bmm29iDxB7nlqjMnh7yD/PNOx9rnZmA= github.com/cortexproject/promqlsmith v0.0.0-20240506042652-6cfdd9739a5e/go.mod h1:+bSqRETXJ1uk2S93m//htzTVqu8DJPvlGEb3bSE9PzI= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/cristalhq/hedgedhttp v0.9.1 h1:g68L9cf8uUyQKQJwciD0A1Vgbsz+QgCjuB1I8FAsCDs= +github.com/cristalhq/hedgedhttp v0.9.1/go.mod h1:XkqWU6qVMutbhW68NnzjWrGtH8NUx1UfYqGYtHVKIsI= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= @@ -1604,6 +1608,8 @@ github.com/go-fonts/stix v0.1.0/go.mod h1:w/c1f0ldAUlJmLBvlbkvVXLAD+tAMqobIIQpmn github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A= +github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= @@ -1941,8 +1947,8 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o github.com/klauspost/asmfmt v1.3.2/go.mod h1:AG8TuvYojzulgDAMCnYn50l/5QV3Bs/tp6j0HLHbNSE= github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= -github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= -github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= +github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.2.3/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= @@ -1970,6 +1976,8 @@ github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7 github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8= github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80 h1:6Yzfa6GP0rIo/kULo2bwGEkFvCePZ3qHDDTC3/J9Swo= github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs= +github.com/leesper/go_rng v0.0.0-20190531154944-a612b043e353 h1:X/79QL0b4YJVO5+OsPH9rF2u428CIrGL/jLmPsoOQQ4= +github.com/leesper/go_rng v0.0.0-20190531154944-a612b043e353/go.mod h1:N0SVk0uhy+E1PZ3C9ctsPRlvOPAFPkCNlcPBDkt0N3U= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20210210170715-a8dfcb80d3a7 h1:YjW+hUb8Fh2S58z4av4t/0cBMK/Q0aP48RocCFsC8yI= github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20210210170715-a8dfcb80d3a7/go.mod h1:Spd59icnvRxSKuyijbbwe5AemzvcyXAUBgApa7VybMw= @@ -2021,8 +2029,8 @@ github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8/go.mod h1:mC1jAcs github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3/go.mod h1:RagcQ7I8IeTMnF8JTXieKnO4Z6JCsikNEzj0DwauVzE= github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34= github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM= -github.com/minio/minio-go/v7 v7.0.72 h1:ZSbxs2BfJensLyHdVOgHv+pfmvxYraaUy07ER04dWnA= -github.com/minio/minio-go/v7 v7.0.72/go.mod h1:4yBA8v80xGA30cfM3fz0DKYMXunWl/AV/6tWEs9ryzo= +github.com/minio/minio-go/v7 v7.0.80 h1:2mdUHXEykRdY/BigLt3Iuu1otL0JTogT0Nmltg0wujk= +github.com/minio/minio-go/v7 v7.0.80/go.mod h1:84gmIilaX4zcvAWWzJ5Z1WI5axN+hAbM5w25xf8xvC0= github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM= github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= @@ -2136,8 +2144,8 @@ github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrb github.com/prometheus/client_golang v1.13.0/go.mod h1:vTeo+zgvILHsnnj/39Ou/1fPN5nJFOEMgftOUOmlvYQ= github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y= github.com/prometheus/client_golang v1.15.1/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk= -github.com/prometheus/client_golang v1.20.4 h1:Tgh3Yr67PaOv/uTqloMsCEdeuFTatm5zIq5+qNN23vI= -github.com/prometheus/client_golang v1.20.4/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= +github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y= +github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= @@ -2187,8 +2195,8 @@ github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTE github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= -github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc= -github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU= +github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0= github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w= github.com/ruudk/golang-pdf417 v0.0.0-20201230142125-a7e3863a1245/go.mod h1:pQAZKsJ8yyVxGRWYNEm9oFB8ieLgKFnamEyDmSA0BRk= github.com/santhosh-tekuri/jsonschema v1.2.4 h1:hNhW8e7t+H1vgY+1QeEQpveR6D4+OwKPXCfD2aieJis= @@ -2249,10 +2257,10 @@ github.com/tencentyun/cos-go-sdk-v5 v0.7.40 h1:W6vDGKCHe4wBACI1d2UgE6+50sJFhRWU4 github.com/tencentyun/cos-go-sdk-v5 v0.7.40/go.mod h1:4dCEtLHGh8QPxHEkgq+nFaky7yZxQuYwgSJM87icDaw= github.com/thanos-community/galaxycache v0.0.0-20211122094458-3a32041a1f1e h1:f1Zsv7OAU9iQhZwigp50Yl38W10g/vd5NC8Rdk1Jzng= github.com/thanos-community/galaxycache v0.0.0-20211122094458-3a32041a1f1e/go.mod h1:jXcofnrSln/cLI6/dhlBxPQZEEQHVPCcFaH75M+nSzM= -github.com/thanos-io/objstore v0.0.0-20241028150459-cfdd0e50390d h1:k+SLTP1mjNqXxsCiq4UYeKCe07le0ieffyuHm/YfmH8= -github.com/thanos-io/objstore v0.0.0-20241028150459-cfdd0e50390d/go.mod h1:/ZMUxFcp/nT6oYV5WslH9k07NU/+86+aibgZRmMMr/4= -github.com/thanos-io/promql-engine v0.0.0-20240921092401-37747eddbd31 h1:xPaP58g+3EPohdw4cv+6jv5+LcX6LynhHvQcYwTAMxQ= -github.com/thanos-io/promql-engine v0.0.0-20240921092401-37747eddbd31/go.mod h1:wx0JlRZtsB2S10JYUgeg5GqLfMxw31SzArP+28yyE00= +github.com/thanos-io/objstore v0.0.0-20241111205755-d1dd89d41f97 h1:VjG0mwhN1DkncwDHFvrpd12/2TLfgYNRmEQA48ikp+0= +github.com/thanos-io/objstore v0.0.0-20241111205755-d1dd89d41f97/go.mod h1:vyzFrBXgP+fGNG2FopEGWOO/zrIuoy7zt3LpLeezRsw= +github.com/thanos-io/promql-engine v0.0.0-20241106100125-097e6e9f425a h1:BhWU58VHOxkxQEMByih9fM2WwuwCGtk5AulIcSRSr0A= +github.com/thanos-io/promql-engine v0.0.0-20241106100125-097e6e9f425a/go.mod h1:wx0JlRZtsB2S10JYUgeg5GqLfMxw31SzArP+28yyE00= github.com/themihai/gomemcache v0.0.0-20180902122335-24332e2d58ab h1:7ZR3hmisBWw77ZpO1/o86g+JV3VKlk3d48jopJxzTjU= github.com/themihai/gomemcache v0.0.0-20180902122335-24332e2d58ab/go.mod h1:eheTFp954zcWZXCU8d0AT76ftsQOTo4DTqkN/h3k1MY= github.com/tinylib/msgp v1.1.9 h1:SHf3yoO2sGA0veCJeCBYLHuttAVFHGm2RHgNodW7wQU= diff --git a/pkg/api/query/v1.go b/pkg/api/query/v1.go index 2d502abe264..1b4bb205008 100644 --- a/pkg/api/query/v1.go +++ b/pkg/api/query/v1.go @@ -495,7 +495,7 @@ func processAnalysis(a *engine.AnalyzeOutputNode) queryTelemetry { analysis.PeakSamples = a.PeakSamples() analysis.TotalSamples = a.TotalSamples() for _, c := range a.Children { - analysis.Children = append(analysis.Children, processAnalysis(&c)) + analysis.Children = append(analysis.Children, processAnalysis(c)) } return analysis } diff --git a/pkg/api/query/v1_test.go b/pkg/api/query/v1_test.go index 923a9cf4113..0c08a0ebca9 100644 --- a/pkg/api/query/v1_test.go +++ b/pkg/api/query/v1_test.go @@ -1722,6 +1722,16 @@ func TestParseStoreDebugMatchersParam(t *testing.T) { labels.MustNewMatcher(labels.MatchEqual, "cluster", "test"), }}, }, + { + storeMatchers: `{__address__=~"localhost:.*"}`, + fail: false, + result: [][]*labels.Matcher{{labels.MustNewMatcher(labels.MatchRegexp, "__address__", "localhost:.*")}}, + }, + { + storeMatchers: `{__address__!~"localhost:.*"}`, + fail: false, + result: [][]*labels.Matcher{{labels.MustNewMatcher(labels.MatchNotRegexp, "__address__", "localhost:.*")}}, + }, } { t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { api := QueryAPI{ @@ -1736,11 +1746,24 @@ func TestParseStoreDebugMatchersParam(t *testing.T) { storeMatchers, err := api.parseStoreDebugMatchersParam(r) if !tc.fail { - testutil.Equals(t, tc.result, storeMatchers) + testutil.Equals(t, len(tc.result), len(storeMatchers)) + for i, m := range tc.result { + testutil.Equals(t, len(m), len(storeMatchers[i])) + for j, n := range m { + testutil.Equals(t, n.String(), storeMatchers[i][j].String()) + } + } testutil.Equals(t, (*baseAPI.ApiError)(nil), err) } else { testutil.NotOk(t, err) } + + // We don't care about results but checking for panic. + for _, matchers := range storeMatchers { + for _, matcher := range matchers { + _ = matcher.Matches("") + } + } }) } } diff --git a/pkg/block/fetcher.go b/pkg/block/fetcher.go index f3204b651f0..28fdf17a6a8 100644 --- a/pkg/block/fetcher.go +++ b/pkg/block/fetcher.go @@ -252,7 +252,7 @@ func (f *RecursiveLister) GetActiveAndPartialBlockIDs(ctx context.Context, ch ch case ch <- id: } return nil - }, objstore.WithRecursiveIter) + }, objstore.WithRecursiveIter()) return partialBlocks, err } diff --git a/pkg/cache/groupcache.go b/pkg/cache/groupcache.go index 43bf1aeefe2..c68828863fe 100644 --- a/pkg/cache/groupcache.go +++ b/pkg/cache/groupcache.go @@ -208,7 +208,7 @@ func NewGroupcacheWithConfig(logger log.Logger, reg prometheus.Registerer, conf if err := bucket.Iter(ctx, parsedData.Name, func(s string) error { list = append(list, s) return nil - }, objstore.WithRecursiveIter); err != nil { + }, objstore.WithRecursiveIter()); err != nil { return err } diff --git a/pkg/exthttp/hedging.go b/pkg/exthttp/hedging.go new file mode 100644 index 00000000000..09a1b3e8a29 --- /dev/null +++ b/pkg/exthttp/hedging.go @@ -0,0 +1,96 @@ +// Copyright (c) The Thanos Authors. +// Licensed under the Apache License 2.0. + +package exthttp + +import ( + "fmt" + "net/http" + "sync" + "time" + + "github.com/caio/go-tdigest" + "github.com/cristalhq/hedgedhttp" +) + +type CustomBucketConfig struct { + HedgingConfig HedgingConfig `yaml:"hedging_config"` +} + +type HedgingConfig struct { + Enabled bool `yaml:"enabled"` + UpTo uint `yaml:"up_to"` + Quantile float64 `yaml:"quantile"` +} + +func DefaultCustomBucketConfig() CustomBucketConfig { + return CustomBucketConfig{ + HedgingConfig: HedgingConfig{ + Enabled: false, + UpTo: 3, + Quantile: 0.9, + }, + } +} + +type hedgingRoundTripper struct { + Transport http.RoundTripper + TDigest *tdigest.TDigest + mu sync.RWMutex + config HedgingConfig +} + +func (hrt *hedgingRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { + start := time.Now() + resp, err := hrt.Transport.RoundTrip(req) + if err != nil { + return nil, err + } + duration := float64(time.Since(start).Milliseconds()) + hrt.mu.Lock() + err = hrt.TDigest.Add(duration) + if err != nil { + return nil, err + } + hrt.mu.Unlock() + return resp, err +} + +func (hrt *hedgingRoundTripper) nextFn() (int, time.Duration) { + hrt.mu.RLock() + defer hrt.mu.RUnlock() + + delayMs := hrt.TDigest.Quantile(hrt.config.Quantile) + delay := time.Duration(delayMs) * time.Millisecond + upto := int(hrt.config.UpTo) + return upto, delay +} + +func CreateHedgedTransportWithConfig(config CustomBucketConfig) func(rt http.RoundTripper) http.RoundTripper { + if !config.HedgingConfig.Enabled { + return func(rt http.RoundTripper) http.RoundTripper { + return rt + } + } + return func(rt http.RoundTripper) http.RoundTripper { + td, err := tdigest.New() + if err != nil { + panic(fmt.Sprintf("BUG: Failed to initialize T-Digest: %v", err)) + } + hrt := &hedgingRoundTripper{ + Transport: rt, + TDigest: td, + config: config.HedgingConfig, + } + cfg := hedgedhttp.Config{ + Transport: hrt, + Upto: int(config.HedgingConfig.UpTo), + Next: hrt.nextFn, + } + hedgedrt, err := hedgedhttp.New(cfg) + if err != nil { + panic(fmt.Sprintf("BUG: Failed to create hedged transport: %v", err)) + } + return hedgedrt + } +} diff --git a/pkg/extpromql/parser.go b/pkg/extpromql/parser.go index 43d7188fdc5..a4e92e9c557 100644 --- a/pkg/extpromql/parser.go +++ b/pkg/extpromql/parser.go @@ -43,16 +43,7 @@ func ParseMetricSelector(input string) ([]*labels.Matcher, error) { return nil, fmt.Errorf("expected type *parser.VectorSelector, got %T", expr) } - matchers := make([]*labels.Matcher, len(vs.LabelMatchers)) - for i, lm := range vs.LabelMatchers { - matchers[i] = &labels.Matcher{ - Type: lm.Type, - Name: lm.Name, - Value: lm.Value, - } - } - - return matchers, nil + return vs.LabelMatchers, nil } func isEmptyNameMatcherErr(err error) bool { diff --git a/pkg/promclient/promclient.go b/pkg/promclient/promclient.go index 20618e8bd9a..dcb4d80b3bd 100644 --- a/pkg/promclient/promclient.go +++ b/pkg/promclient/promclient.go @@ -27,7 +27,6 @@ import ( "github.com/pkg/errors" "github.com/prometheus/common/expfmt" "github.com/prometheus/common/model" - "github.com/prometheus/prometheus/config" "github.com/prometheus/prometheus/model/labels" "github.com/prometheus/prometheus/model/timestamp" "github.com/prometheus/prometheus/promql" @@ -201,13 +200,15 @@ func (c *Client) ExternalLabels(ctx context.Context, base *url.URL) (labels.Labe return labels.EmptyLabels(), errors.Wrapf(err, "unmarshal response: %v", string(body)) } var cfg struct { - GlobalConfig config.GlobalConfig `yaml:"global"` + GlobalConfig struct { + ExternalLabels map[string]string `yaml:"external_labels"` + } `yaml:"global"` } if err := yaml.Unmarshal([]byte(d.Data.YAML), &cfg); err != nil { return labels.EmptyLabels(), errors.Wrapf(err, "parse Prometheus config: %v", d.Data.YAML) } - return cfg.GlobalConfig.ExternalLabels, nil + return labels.FromMap(cfg.GlobalConfig.ExternalLabels), nil } type Flags struct { diff --git a/pkg/promclient/promclient_test.go b/pkg/promclient/promclient_test.go new file mode 100644 index 00000000000..00173d3302b --- /dev/null +++ b/pkg/promclient/promclient_test.go @@ -0,0 +1,67 @@ +// Copyright (c) The Thanos Authors. +// Licensed under the Apache License 2.0. + +// Package promclient offers helper client function for various API endpoints. + +package promclient + +import ( + "context" + "fmt" + "net/http" + "net/http/httptest" + "net/url" + "testing" + + "github.com/efficientgo/core/testutil" +) + +func TestExternalLabels(t *testing.T) { + for _, tc := range []struct { + name string + response string + err bool + labels map[string]string + }{ + { + name: "invalid payload", + response: `{`, + err: true, + }, + { + name: "unknown scrape protocol", + response: `{"status":"success","data":{"yaml":"global:\n scrape_interval: 1m\n scrape_timeout: 10s\n scrape_protocols:\n - OpenMetricsText1.0.0\n - OpenMetricsText0.0.1\n - PrometheusText1.0.0\n - PrometheusText0.0.4\n - UnknownScrapeProto\n evaluation_interval: 1m\n external_labels:\n az: \"1\"\n region: eu-west\nruntime:\n gogc: 75\n"}}`, + labels: map[string]string{ + "region": "eu-west", + "az": "1", + }, + }, + { + name: "no external labels", + response: `{"status":"success","data":{"yaml":"global:\n scrape_interval: 1m\n scrape_timeout: 10s\n scrape_protocols:\n - OpenMetricsText1.0.0\n - OpenMetricsText0.0.1\n - PrometheusText1.0.0\n - PrometheusText0.0.4\n - UnknownScrapeProto\n evaluation_interval: 1m\nruntime:\n gogc: 75\n"}}`, + labels: map[string]string{}, + }, + } { + t.Run(tc.name, func(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + fmt.Fprintln(w, tc.response) + })) + defer ts.Close() + + u, err := url.Parse(ts.URL) + testutil.Ok(t, err) + + ext, err := NewDefaultClient().ExternalLabels(context.Background(), u) + if tc.err { + testutil.NotOk(t, err) + return + } + + testutil.Ok(t, err) + testutil.Equals(t, len(tc.labels), ext.Len()) + for k, v := range tc.labels { + testutil.Equals(t, v, ext.Get(k)) + } + }) + } +} diff --git a/pkg/query/remote_engine.go b/pkg/query/remote_engine.go index c625cab5ba5..77a74c9a6fa 100644 --- a/pkg/query/remote_engine.go +++ b/pkg/query/remote_engine.go @@ -126,12 +126,10 @@ func (r *remoteEngine) MinT() int64 { highestMintByLabelSet[key] = lset.MinTime continue } - if lset.MinTime > lsetMinT { highestMintByLabelSet[key] = lset.MinTime } } - var mint int64 = math.MaxInt64 for _, m := range highestMintByLabelSet { if m < mint { @@ -190,7 +188,6 @@ func (r *remoteEngine) adjustedInfos() infopb.TSDBInfos { labelpb.ZLabelsFromPromLabels(builder.Labels())), ) } - return infos } diff --git a/pkg/receive/capnp_server.go b/pkg/receive/capnp_server.go index 34b406bfa93..3ca359744bc 100644 --- a/pkg/receive/capnp_server.go +++ b/pkg/receive/capnp_server.go @@ -14,7 +14,6 @@ import ( "github.com/pkg/errors" "github.com/thanos-io/thanos/pkg/receive/writecapnp" - "github.com/thanos-io/thanos/pkg/runutil" ) type CapNProtoServer struct { @@ -39,7 +38,6 @@ func (c *CapNProtoServer) ListenAndServe() error { } go func() { - defer runutil.CloseWithLogOnErr(c.logger, conn, "receive capnp conn") rpcConn := rpc.NewConn(rpc.NewPackedStreamTransport(conn), &rpc.Options{ // The BootstrapClient is the RPC interface that will be made available // to the remote endpoint by default. diff --git a/pkg/store/bucket.go b/pkg/store/bucket.go index eec1de10053..d9940221ffd 100644 --- a/pkg/store/bucket.go +++ b/pkg/store/bucket.go @@ -922,17 +922,15 @@ func (s *BucketStore) TSDBInfos() []infopb.TSDBInfo { sort.Slice(infos, func(i, j int) bool { return infos[i].MinTime < infos[j].MinTime }) cur := infos[0] - for i, info := range infos { + for _, info := range infos { if info.MinTime > cur.MaxTime { res = append(res, cur) cur = info continue } cur.MaxTime = info.MaxTime - if i == len(infos)-1 { - res = append(res, cur) - } } + res = append(res, cur) } return res @@ -1574,6 +1572,8 @@ func (s *BucketStore) Series(req *storepb.SeriesRequest, seriesSrv storepb.Store tenant, ) + defer blockClient.Close() + g.Go(func() error { span, _ := tracing.StartSpan(gctx, "bucket_store_block_series", tracing.Tags{ @@ -3381,7 +3381,6 @@ func (r *bucketIndexReader) Close() error { } func (b *blockSeriesClient) CloseSend() error { - b.Close() return nil } diff --git a/pkg/store/bucket_test.go b/pkg/store/bucket_test.go index ec3e0bee7e7..e8dffd093b1 100644 --- a/pkg/store/bucket_test.go +++ b/pkg/store/bucket_test.go @@ -682,6 +682,8 @@ func TestBucketStore_TSDBInfo(t *testing.T) { {mint: 3500, maxt: 5000, extLabels: labels.FromStrings("a", "b")}, {mint: 0, maxt: 1000, extLabels: labels.FromStrings("a", "c")}, {mint: 500, maxt: 2000, extLabels: labels.FromStrings("a", "c")}, + {mint: 0, maxt: 1000, extLabels: labels.FromStrings("a", "d")}, + {mint: 2000, maxt: 3000, extLabels: labels.FromStrings("a", "d")}, } { id1, err := e2eutil.CreateBlock(ctx, dir, series, 10, tt.mint, tt.maxt, tt.extLabels, 0, metadata.NoneFunc) testutil.Ok(t, err) @@ -738,6 +740,16 @@ func TestBucketStore_TSDBInfo(t *testing.T) { MinTime: 0, MaxTime: 2000, }, + { + Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{{Name: "a", Value: "d"}}}, + MinTime: 0, + MaxTime: 1000, + }, + { + Labels: labelpb.ZLabelSet{Labels: []labelpb.ZLabel{{Name: "a", Value: "d"}}}, + MinTime: 2000, + MaxTime: 3000, + }, }) } diff --git a/test/e2e/query_test.go b/test/e2e/query_test.go index 0351cc9bc81..11bb203247b 100644 --- a/test/e2e/query_test.go +++ b/test/e2e/query_test.go @@ -2376,3 +2376,93 @@ func TestDistributedEngineWithExtendedFunctions(t *testing.T) { }, time.Now, promclient.QueryOptions{}, 1) testutil.Equals(t, model.SampleValue(0), result[0].Value) } + +func TestDistributedEngineWithDisjointTSDBs(t *testing.T) { + t.Skip("skipping test as this replicates a bug") + e, err := e2e.New(e2e.WithName("dist-disj-tsdbs")) + testutil.Ok(t, err) + t.Cleanup(e2ethanos.CleanScenario(t, e)) + + ctx := context.Background() + l := log.NewLogfmtLogger(os.Stdout) + now := time.Now() + + bucket1 := "dist-disj-tsdbs-test1" + minio1 := e2edb.NewMinio(e, "1", bucket1, e2edb.WithMinioTLS()) + testutil.Ok(t, e2e.StartAndWaitReady(minio1)) + + bkt1, err := s3.NewBucketWithConfig(l, e2ethanos.NewS3Config(bucket1, minio1.Endpoint("http"), minio1.Dir()), "test", nil) + testutil.Ok(t, err) + + // Setup a storage GW with 2 blocks that have a gap to trigger distributed query MinT bug + dir1 := filepath.Join(e.SharedDir(), "tmp1") + testutil.Ok(t, os.MkdirAll(filepath.Join(e.SharedDir(), dir1), os.ModePerm)) + blockID1, err := e2eutil.CreateBlockWithBlockDelay(ctx, + dir1, + []labels.Labels{labels.FromStrings("__name__", "foo", "instance", "foo_1")}, + 1000, + timestamp.FromTime(now.Add(-10*time.Hour)), + timestamp.FromTime(now.Add(-8*time.Hour)), + 30*time.Minute, + labels.FromStrings("prometheus", "p1", "replica", "0"), + 0, + metadata.NoneFunc, + ) + testutil.Ok(t, err) + testutil.Ok(t, objstore.UploadDir(ctx, l, bkt1, path.Join(dir1, blockID1.String()), blockID1.String())) + + blockID2, err := e2eutil.CreateBlockWithBlockDelay(ctx, + dir1, + []labels.Labels{labels.FromStrings("__name__", "foo", "instance", "foo_1")}, + 1000, + timestamp.FromTime(now.Add(-4*time.Hour)), + timestamp.FromTime(now.Add(-2*time.Hour)), + 30*time.Minute, + labels.FromStrings("prometheus", "p1", "replica", "0"), + 0, + metadata.NoneFunc, + ) + testutil.Ok(t, err) + testutil.Ok(t, objstore.UploadDir(ctx, l, bkt1, path.Join(dir1, blockID2.String()), blockID2.String())) + store1 := e2ethanos.NewStoreGW( + e, + "s1", + client.BucketConfig{ + Type: client.S3, + Config: e2ethanos.NewS3Config(bucket1, minio1.InternalEndpoint("http"), minio1.InternalDir()), + }, + "", + "", + nil, + ) + testutil.Ok(t, e2e.StartAndWaitReady(store1)) + + querierLeaf1 := e2ethanos.NewQuerierBuilder(e, "1", store1.InternalEndpoint("grpc")).Init() + + // We need another querier to circumvent the passthrough optimizer + promConfig2 := e2ethanos.DefaultPromConfig("p2", 0, "", "", e2ethanos.LocalPrometheusTarget) + prom2, sidecar2 := e2ethanos.NewPrometheusWithSidecar(e, "p2", promConfig2, "", e2ethanos.DefaultPrometheusImage(), "") + querierLeaf2 := e2ethanos.NewQuerierBuilder(e, "2", sidecar2.InternalEndpoint("grpc")).Init() + + querierDistributed := e2ethanos.NewQuerierBuilder(e, "3", + querierLeaf1.InternalEndpoint("grpc"), + querierLeaf2.InternalEndpoint("grpc"), + ). + WithEngine(v1.PromqlEngineThanos). + WithQueryMode("distributed"). + Init() + + testutil.Ok(t, e2e.StartAndWaitReady(querierLeaf1, prom2, sidecar2, querierLeaf2, querierDistributed)) + + // We would expect 2x2h ranges for the 2 blocks containing foo samples. That would be around 240 expected sample pairs in the result matrix. + // We assert on more then 200 to reduce flakiness + rangeQuery(t, ctx, querierDistributed.Endpoint("http"), func() string { return "foo" }, timestamp.FromTime(now.Add(-24*time.Hour)), timestamp.FromTime(now), 60, promclient.QueryOptions{}, func(res model.Matrix) error { + if res.Len() < 1 { + return errors.New("No result series returned") + } + if nvals := len(res[0].Values); nvals < 200 { + return errors.Errorf("Too few values in result matrix, got %d, expected > 200", nvals) + } + return nil + }) +}