Skip to content

Commit 86408ce

Browse files
authored
Merge pull request #7031 from cortexproject/patch-1.19
Backport remote read fix to release 1.19
2 parents babe421 + 9b46c84 commit 86408ce

File tree

8 files changed

+68
-39
lines changed

8 files changed

+68
-39
lines changed

.github/workflows/scripts/install-docker.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#!/bin/bash
22

33
set -x
4-
VER="20.10.19"
4+
VER="28.0.4"
55
curl -L -o /tmp/docker-$VER.tgz https://download.docker.com/linux/static/stable/x86_64/docker-$VER.tgz
66
tar -xz -C /tmp -f /tmp/docker-$VER.tgz
77
mkdir -vp ~/.docker/cli-plugins/

.github/workflows/test-build-deploy.yml

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ on:
1515

1616
jobs:
1717
lint:
18-
runs-on: ubuntu-20.04
18+
runs-on: ubuntu-24.04
1919
container:
2020
image: quay.io/cortexproject/build-image:master-03a8f8c98
2121
steps:
@@ -44,7 +44,7 @@ jobs:
4444
run: make BUILD_IN_CONTAINER=false check-white-noise
4545

4646
test:
47-
runs-on: ubuntu-20.04
47+
runs-on: ubuntu-24.04
4848
container:
4949
image: quay.io/cortexproject/build-image:master-03a8f8c98
5050
steps:
@@ -62,7 +62,7 @@ jobs:
6262
- name: Run Tests
6363
run: make BUILD_IN_CONTAINER=false test
6464
test-no-race:
65-
runs-on: ubuntu-20.04
65+
runs-on: ubuntu-24.04
6666
container:
6767
image: quay.io/cortexproject/build-image:master-03a8f8c98
6868
steps:
@@ -82,7 +82,7 @@ jobs:
8282

8383
security:
8484
name: CodeQL
85-
runs-on: ubuntu-20.04
85+
runs-on: ubuntu-24.04
8686
permissions:
8787
actions: read
8888
contents: read
@@ -105,7 +105,7 @@ jobs:
105105

106106

107107
build:
108-
runs-on: ubuntu-20.04
108+
runs-on: ubuntu-24.04
109109
container:
110110
image: quay.io/cortexproject/build-image:master-03a8f8c98
111111
steps:
@@ -150,7 +150,7 @@ jobs:
150150

151151
integration:
152152
needs: build
153-
runs-on: ubuntu-20.04
153+
runs-on: ubuntu-24.04
154154
strategy:
155155
fail-fast: false
156156
matrix:
@@ -230,7 +230,7 @@ jobs:
230230

231231
integration-configs-db:
232232
needs: build
233-
runs-on: ubuntu-20.04
233+
runs-on: ubuntu-24.04
234234
steps:
235235
- name: Checkout Repo
236236
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
@@ -252,7 +252,7 @@ jobs:
252252
deploy_website:
253253
needs: [build, test]
254254
if: (github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags/')) && github.repository == 'cortexproject/cortex'
255-
runs-on: ubuntu-20.04
255+
runs-on: ubuntu-24.04
256256
container:
257257
image: quay.io/cortexproject/build-image:master-03a8f8c98
258258
steps:
@@ -294,7 +294,7 @@ jobs:
294294
deploy:
295295
needs: [build, test, lint, integration, integration-configs-db]
296296
if: (github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags/')) && github.repository == 'cortexproject/cortex'
297-
runs-on: ubuntu-20.04
297+
runs-on: ubuntu-24.04
298298
container:
299299
image: quay.io/cortexproject/build-image:master-03a8f8c98
300300
steps:

CHANGELOG.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@
22

33
## master / unreleased
44

5-
## 1.19.0 in progress
5+
## 1.19.1 2025-09-20
6+
7+
* [BUGFIX] Frontend: Fix remote read snappy input due to request string logging when query stats enabled. #7025
8+
9+
## 1.19.0 2025-02-27
610

711
* [CHANGE] Deprecate `-blocks-storage.tsdb.wal-compression-enabled` flag (use `blocks-storage.tsdb.wal-compression-type` instead). #6529
812
* [CHANGE] OTLP: Change OTLP handler to be consistent with the Prometheus OTLP handler. #6272

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
1.19.0
1+
1.19.1

docs/getting-started/.env

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
CORTEX_VERSION=v1.19.0
1+
CORTEX_VERSION=v1.19.1
22
GRAFANA_VERSION=10.4.2
33
PROMETHEUS_VERSION=v2.51.2
44
SEAWEEDFS_VERSION=3.67

integration/query_frontend_test.go

Lines changed: 8 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import (
1212
"net/http"
1313
"os"
1414
"path/filepath"
15-
"strconv"
1615
"sync"
1716
"testing"
1817
"time"
@@ -37,7 +36,6 @@ import (
3736
type queryFrontendTestConfig struct {
3837
testMissingMetricName bool
3938
querySchedulerEnabled bool
40-
queryStatsEnabled bool
4139
remoteReadEnabled bool
4240
testSubQueryStepSize bool
4341
setup func(t *testing.T, s *e2e.Scenario) (configFile string, flags map[string]string)
@@ -60,7 +58,6 @@ func TestQueryFrontendWithBlocksStorageViaFlags(t *testing.T) {
6058
func TestQueryFrontendWithBlocksStorageViaFlagsAndQueryStatsEnabled(t *testing.T) {
6159
runQueryFrontendTest(t, queryFrontendTestConfig{
6260
testMissingMetricName: false,
63-
queryStatsEnabled: true,
6461
setup: func(t *testing.T, s *e2e.Scenario) (configFile string, flags map[string]string) {
6562
flags = BlocksStorageFlags()
6663

@@ -91,7 +88,6 @@ func TestQueryFrontendWithBlocksStorageViaFlagsAndWithQuerySchedulerAndQueryStat
9188
runQueryFrontendTest(t, queryFrontendTestConfig{
9289
testMissingMetricName: false,
9390
querySchedulerEnabled: true,
94-
queryStatsEnabled: true,
9591
setup: func(t *testing.T, s *e2e.Scenario) (configFile string, flags map[string]string) {
9692
flags = BlocksStorageFlags()
9793

@@ -167,7 +163,6 @@ func TestQueryFrontendWithVerticalSharding(t *testing.T) {
167163
runQueryFrontendTest(t, queryFrontendTestConfig{
168164
testMissingMetricName: false,
169165
querySchedulerEnabled: false,
170-
queryStatsEnabled: true,
171166
setup: func(t *testing.T, s *e2e.Scenario) (configFile string, flags map[string]string) {
172167
require.NoError(t, writeFileToSharedDir(s, cortexConfigFile, []byte(BlocksStorageConfig)))
173168

@@ -187,7 +182,6 @@ func TestQueryFrontendWithVerticalShardingQueryScheduler(t *testing.T) {
187182
runQueryFrontendTest(t, queryFrontendTestConfig{
188183
testMissingMetricName: false,
189184
querySchedulerEnabled: true,
190-
queryStatsEnabled: true,
191185
setup: func(t *testing.T, s *e2e.Scenario) (configFile string, flags map[string]string) {
192186
require.NoError(t, writeFileToSharedDir(s, cortexConfigFile, []byte(BlocksStorageConfig)))
193187

@@ -207,7 +201,6 @@ func TestQueryFrontendProtobufCodec(t *testing.T) {
207201
runQueryFrontendTest(t, queryFrontendTestConfig{
208202
testMissingMetricName: false,
209203
querySchedulerEnabled: true,
210-
queryStatsEnabled: true,
211204
setup: func(t *testing.T, s *e2e.Scenario) (configFile string, flags map[string]string) {
212205
require.NoError(t, writeFileToSharedDir(s, cortexConfigFile, []byte(BlocksStorageConfig)))
213206

@@ -273,7 +266,7 @@ func runQueryFrontendTest(t *testing.T, cfg queryFrontendTestConfig) {
273266
"-querier.split-queries-by-interval": "24h",
274267
"-querier.query-ingesters-within": "12h", // Required by the test on query /series out of ingesters time range
275268
"-frontend.memcached.addresses": "dns+" + memcached.NetworkEndpoint(e2ecache.MemcachedPort),
276-
"-frontend.query-stats-enabled": strconv.FormatBool(cfg.queryStatsEnabled),
269+
"-frontend.query-stats-enabled": "true", // Always enable query stats to capture regressions
277270
})
278271

279272
// Start the query-scheduler if enabled.
@@ -361,7 +354,7 @@ func runQueryFrontendTest(t *testing.T, cfg queryFrontendTestConfig) {
361354
}
362355

363356
// No need to repeat the test on Server-Timing header for each user.
364-
if userID == 0 && cfg.queryStatsEnabled {
357+
if userID == 0 {
365358
res, _, err := c.QueryRaw("{instance=~\"hello.*\"}", time.Now(), map[string]string{})
366359
require.NoError(t, err)
367360
require.Regexp(t, "querier_wall_time;dur=[0-9.]*, response_time;dur=[0-9.]*$", res.Header.Values("Server-Timing")[0])
@@ -412,15 +405,11 @@ func runQueryFrontendTest(t *testing.T, cfg queryFrontendTestConfig) {
412405

413406
wg.Wait()
414407

415-
extra := float64(2)
408+
extra := float64(3) // Always include query stats test
416409
if cfg.testMissingMetricName {
417410
extra++
418411
}
419412

420-
if cfg.queryStatsEnabled {
421-
extra++
422-
}
423-
424413
if cfg.remoteReadEnabled {
425414
extra++
426415
}
@@ -437,15 +426,11 @@ func runQueryFrontendTest(t *testing.T, cfg queryFrontendTestConfig) {
437426
require.NoError(t, querier.WaitSumMetricsWithOptions(e2e.Greater(numUsers*numQueriesPerUser), []string{"cortex_request_duration_seconds"}, e2e.WithMetricCount))
438427
require.NoError(t, querier.WaitSumMetricsWithOptions(e2e.Greater(numUsers*numQueriesPerUser), []string{"cortex_querier_request_duration_seconds"}, e2e.WithMetricCount))
439428

440-
// Ensure query stats metrics are tracked only when enabled.
441-
if cfg.queryStatsEnabled {
442-
require.NoError(t, queryFrontend.WaitSumMetricsWithOptions(
443-
e2e.Greater(0),
444-
[]string{"cortex_query_seconds_total"},
445-
e2e.WithLabelMatchers(labels.MustNewMatcher(labels.MatchEqual, "user", "user-1"))))
446-
} else {
447-
require.NoError(t, queryFrontend.WaitRemovedMetric("cortex_query_seconds_total"))
448-
}
429+
// Ensure query stats metrics are always tracked to capture regressions.
430+
require.NoError(t, queryFrontend.WaitSumMetricsWithOptions(
431+
e2e.Greater(0),
432+
[]string{"cortex_query_seconds_total"},
433+
e2e.WithLabelMatchers(labels.MustNewMatcher(labels.MatchEqual, "user", "user-1"))))
449434

450435
// Ensure no service-specific metrics prefix is used by the wrong service.
451436
assertServiceMetricsPrefixes(t, Distributor, distributor)

pkg/frontend/transport/handler.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,8 @@ func (f *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
224224
// We parse form here so that we can use buf as body, in order to
225225
// prevent https://github.com/cortexproject/cortex/issues/5201.
226226
// Exclude remote read here as we don't have to buffer its body.
227-
if !strings.Contains(r.URL.Path, "api/v1/read") {
227+
isRemoteRead := strings.Contains(r.URL.Path, "api/v1/read")
228+
if !isRemoteRead {
228229
if err := r.ParseForm(); err != nil {
229230
statusCode := http.StatusBadRequest
230231
if util.IsRequestBodyTooLarge(err) {
@@ -240,8 +241,9 @@ func (f *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
240241
r.Body = io.NopCloser(&buf)
241242
}
242243

243-
// Log request
244-
if f.cfg.QueryStatsEnabled {
244+
// Log request if the request is not remote read.
245+
// We need to parse remote read proto to be properly log it so skip it.
246+
if f.cfg.QueryStatsEnabled && !isRemoteRead {
245247
queryString = f.parseRequestQueryString(r, buf)
246248
f.logQueryRequest(r, queryString)
247249
}

pkg/frontend/transport/handler_test.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -608,3 +608,41 @@ func Test_TenantFederation_MaxTenant(t *testing.T) {
608608
})
609609
}
610610
}
611+
612+
func TestHandler_RemoteReadRequest_DoesNotParseQueryString(t *testing.T) {
613+
// Create a mock round tripper that captures the request
614+
var capturedRequest *http.Request
615+
roundTripper := roundTripperFunc(func(req *http.Request) (*http.Response, error) {
616+
capturedRequest = req
617+
return &http.Response{
618+
StatusCode: http.StatusOK,
619+
Body: io.NopCloser(strings.NewReader("{}")),
620+
}, nil
621+
})
622+
623+
// Use a larger MaxBodySize to avoid the "request body too large" error
624+
handler := NewHandler(HandlerConfig{QueryStatsEnabled: true, MaxBodySize: 10 * 1024 * 1024}, tenantfederation.Config{}, roundTripper, log.NewNopLogger(), nil)
625+
handlerWithAuth := middleware.Merge(middleware.AuthenticateUser).Wrap(handler)
626+
627+
// Create a remote read request with a body that would be corrupted by parseRequestQueryString
628+
originalBody := "snappy-compressed-data"
629+
req := httptest.NewRequest("POST", "http://fake/api/v1/read", strings.NewReader(originalBody))
630+
req.Header.Set("X-Scope-OrgId", "user-1")
631+
req.Header.Set("Content-Type", "application/x-protobuf")
632+
req.Header.Set("Content-Encoding", "snappy")
633+
634+
resp := httptest.NewRecorder()
635+
handlerWithAuth.ServeHTTP(resp, req)
636+
637+
// Verify the request was successful
638+
require.Equal(t, http.StatusOK, resp.Code)
639+
640+
// Verify that the original request body was preserved and not corrupted
641+
require.NotNil(t, capturedRequest)
642+
bodyBytes, err := io.ReadAll(capturedRequest.Body)
643+
require.NoError(t, err)
644+
require.Equal(t, originalBody, string(bodyBytes))
645+
646+
// Verify that the request body is still readable (not replaced with empty buffer)
647+
require.NotEmpty(t, string(bodyBytes))
648+
}

0 commit comments

Comments
 (0)