Skip to content

Commit 1bdcc65

Browse files
QueryFrontend: pass "stats" parameter forward (#7852)
If a querier sees a "stats" parameter in the query request, it will attach important information about the query execution to the response. But currently, even if an user sets this value, the Query Frontend will lose this value in its middleware/roundtrippers. This PR fixes this problem by properly encoding/decoding the requests in QFE. Signed-off-by: Pedro Tanaka <[email protected]>
1 parent 7d95913 commit 1bdcc65

File tree

9 files changed

+218
-108
lines changed

9 files changed

+218
-108
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ We use *breaking :warning:* to mark changes that are not backward compatible (re
2323
- [#7679](https://github.com/thanos-io/thanos/pull/7679) Query: respect store.limit.* flags when evaluating queries
2424
- [#7821](https://github.com/thanos-io/thanos/pull/7679) Query/Receive: Fix coroutine leak introduced in https://github.com/thanos-io/thanos/pull/7796.
2525
- [#7843](https://github.com/thanos-io/thanos/pull/7843) Query Frontend: fix slow query logging for non-query endpoints.
26+
- [#7852](https://github.com/thanos-io/thanos/pull/7852) Query Frontend: pass "stats" parameter forward to queriers and fix Prometheus stats merging.
2627

2728
### Added
2829
- [#7763](https://github.com/thanos-io/thanos/pull/7763) Ruler: use native histograms for client latency metrics.

internal/cortex/querier/queryrange/query_range.go

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import (
88
"context"
99
stdjson "encoding/json"
1010
"fmt"
11-
io "io"
11+
"io"
1212
"math"
1313
"net/http"
1414
"net/url"
@@ -329,6 +329,7 @@ func (prometheusCodec) DecodeRequest(_ context.Context, r *http.Request, forward
329329
result.Query = r.FormValue("query")
330330
result.Stats = r.FormValue("stats")
331331
result.Path = r.URL.Path
332+
result.Stats = r.FormValue("stats")
332333

333334
// Include the specified headers from http request in prometheusRequest.
334335
for _, header := range forwardHeaders {
@@ -706,6 +707,8 @@ func (s *PrometheusInstantQueryData) MarshalJSON() ([]byte, error) {
706707
func StatsMerge(resps []Response) *PrometheusResponseStats {
707708
output := map[int64]*PrometheusResponseQueryableSamplesStatsPerStep{}
708709
hasStats := false
710+
peakSamples := int32(0)
711+
totalSamples := int64(0)
709712
for _, resp := range resps {
710713
stats := resp.GetStats()
711714
if stats == nil {
@@ -720,6 +723,11 @@ func StatsMerge(resps []Response) *PrometheusResponseStats {
720723
for _, s := range stats.Samples.TotalQueryableSamplesPerStep {
721724
output[s.GetTimestampMs()] = s
722725
}
726+
727+
if stats.Samples.PeakSamples > peakSamples {
728+
peakSamples = stats.Samples.PeakSamples
729+
}
730+
totalSamples += stats.Samples.TotalQueryableSamples
723731
}
724732

725733
if !hasStats {
@@ -733,10 +741,12 @@ func StatsMerge(resps []Response) *PrometheusResponseStats {
733741

734742
sort.Slice(keys, func(i, j int) bool { return keys[i] < keys[j] })
735743

736-
result := &PrometheusResponseStats{Samples: &PrometheusResponseSamplesStats{}}
744+
result := &PrometheusResponseStats{Samples: &PrometheusResponseSamplesStats{
745+
PeakSamples: peakSamples,
746+
TotalQueryableSamples: totalSamples,
747+
}}
737748
for _, key := range keys {
738749
result.Samples.TotalQueryableSamplesPerStep = append(result.Samples.TotalQueryableSamplesPerStep, output[key])
739-
result.Samples.TotalQueryableSamples += output[key].Value
740750
}
741751

742752
return result

internal/cortex/querier/queryrange/query_range_test.go

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ package queryrange
66
import (
77
"bytes"
88
"context"
9-
io "io"
9+
"io"
1010
"net/http"
1111
"strconv"
1212
"testing"
@@ -36,6 +36,20 @@ func TestRequest(t *testing.T) {
3636
url: query,
3737
expected: &parsedRequestWithHeaders,
3838
},
39+
{
40+
url: "/api/v1/query_range?end=60&query=sum%28container_memory_rss%29+by+%28namespace%29&start=0&stats=all&step=14",
41+
expected: &PrometheusRequest{
42+
Path: "/api/v1/query_range",
43+
Start: 0,
44+
End: 60_000,
45+
Step: 14_000,
46+
Query: "sum(container_memory_rss) by (namespace)",
47+
Stats: "all",
48+
Headers: []*PrometheusRequestHeader{
49+
{Name: "Test-Header", Values: []string{"test"}},
50+
},
51+
},
52+
},
3953
{
4054
url: "api/v1/query_range?start=foo&stats=all",
4155
expectedErr: httpgrpc.Errorf(http.StatusBadRequest, "invalid parameter \"start\"; cannot parse \"foo\" to a valid timestamp"),
@@ -129,7 +143,7 @@ func TestResponseWithStats(t *testing.T) {
129143
expected *PrometheusResponse
130144
}{
131145
{
132-
body: `{"status":"success","data":{"resultType":"matrix","result":[{"metric":{"foo":"bar"},"values":[[1536673680,"137"],[1536673780,"137"]]}],"stats":{"samples":{"totalQueryableSamples":10,"totalQueryableSamplesPerStep":[[1536673680,5],[1536673780,5]]}},"analysis":null}}`,
146+
body: `{"status":"success","data":{"resultType":"matrix","result":[{"metric":{"foo":"bar"},"values":[[1536673680,"137"],[1536673780,"137"]]}],"stats":{"samples":{"peakSamples":0,"totalQueryableSamples":10,"totalQueryableSamplesPerStep":[[1536673680,5],[1536673780,5]]}},"analysis":null}}`,
133147
expected: &PrometheusResponse{
134148
Status: "success",
135149
Data: PrometheusData{
@@ -158,7 +172,7 @@ func TestResponseWithStats(t *testing.T) {
158172
},
159173
},
160174
{
161-
body: `{"status":"success","data":{"resultType":"matrix","result":[{"metric":{"foo":"bar"},"values":[[1536673680,"137"],[1536673780,"137"]]}],"stats":{"samples":{"totalQueryableSamples":10,"totalQueryableSamplesPerStep":[[1536673680,5],[1536673780,5]]}},"analysis":{"name":"[noArgFunction]","executionTime":"1s","children":null}}}`,
175+
body: `{"status":"success","data":{"resultType":"matrix","result":[{"metric":{"foo":"bar"},"values":[[1536673680,"137"],[1536673780,"137"]]}],"stats":{"samples":{"peakSamples":0,"totalQueryableSamples":10,"totalQueryableSamplesPerStep":[[1536673680,5],[1536673780,5]]}},"analysis":{"name":"[noArgFunction]","executionTime":"1s","children":null}}}`,
162176
expected: &PrometheusResponse{
163177
Status: "success",
164178
Data: PrometheusData{
@@ -211,11 +225,27 @@ func TestResponseWithStats(t *testing.T) {
211225
}
212226
resp2, err := PrometheusCodec.EncodeResponse(context.Background(), resp)
213227
require.NoError(t, err)
214-
assert.Equal(t, response, resp2)
228+
assert.Equal(t, prettyPrintJsonBody(t, response.Body), prettyPrintJsonBody(t, resp2.Body))
215229
})
216230
}
217231
}
218232

233+
func prettyPrintJsonBody(t *testing.T, body io.ReadCloser) string {
234+
t.Helper()
235+
236+
bodyContent, err := io.ReadAll(body)
237+
require.NoError(t, err)
238+
239+
var jsonData interface{}
240+
err = json.Unmarshal(bodyContent, &jsonData)
241+
require.NoError(t, err)
242+
243+
prettyBytes, err := json.MarshalIndent(jsonData, "", " ")
244+
require.NoError(t, err)
245+
246+
return string(prettyBytes)
247+
}
248+
219249
func TestMergeAPIResponses(t *testing.T) {
220250
for _, tc := range []struct {
221251
name string
@@ -659,7 +689,7 @@ func TestMergeAPIResponses(t *testing.T) {
659689
},
660690
},
661691
Stats: &PrometheusResponseStats{Samples: &PrometheusResponseSamplesStats{
662-
TotalQueryableSamples: 25,
692+
TotalQueryableSamples: 30,
663693
TotalQueryableSamplesPerStep: []*PrometheusResponseQueryableSamplesStatsPerStep{
664694
{Value: 5, TimestampMs: 1000},
665695
{Value: 5, TimestampMs: 2000},
@@ -696,7 +726,7 @@ func TestMergeAPIResponses(t *testing.T) {
696726
},
697727
},
698728
Stats: &PrometheusResponseStats{Samples: &PrometheusResponseSamplesStats{
699-
TotalQueryableSamples: 28,
729+
TotalQueryableSamples: 36,
700730
TotalQueryableSamplesPerStep: []*PrometheusResponseQueryableSamplesStatsPerStep{
701731
{Value: 1, TimestampMs: 1000},
702732
{Value: 2, TimestampMs: 2000},
@@ -734,7 +764,7 @@ func TestMergeAPIResponses(t *testing.T) {
734764
},
735765
},
736766
Stats: &PrometheusResponseStats{Samples: &PrometheusResponseSamplesStats{
737-
TotalQueryableSamples: 15,
767+
TotalQueryableSamples: 26,
738768
TotalQueryableSamplesPerStep: []*PrometheusResponseQueryableSamplesStatsPerStep{
739769
{Value: 1, TimestampMs: 1000},
740770
{Value: 2, TimestampMs: 2000},
@@ -769,7 +799,7 @@ func TestMergeAPIResponses(t *testing.T) {
769799
},
770800
},
771801
Stats: &PrometheusResponseStats{Samples: &PrometheusResponseSamplesStats{
772-
TotalQueryableSamples: 14,
802+
TotalQueryableSamples: 40,
773803
TotalQueryableSamplesPerStep: []*PrometheusResponseQueryableSamplesStatsPerStep{
774804
{Value: 2, TimestampMs: 2000},
775805
{Value: 3, TimestampMs: 3000},

0 commit comments

Comments
 (0)