Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create new cortex_querier_codec_response_size histogram to track the size of the encoded Query responses #6444

Merged
merged 2 commits into from
Dec 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
* [ENHANCEMENT] Ingester: Make sure unregistered ingester joining the ring after WAL replay. #6277
* [ENHANCEMENT] Distributor: Add a new `-distributor.num-push-workers` flag to use a goroutine worker pool when sending data from distributor to ingesters. #6406
* [ENHANCEMENT] Ingester: If a limit per label set entry doesn't have any label, use it as the default partition to catch all series that doesn't match any other label sets entries. #6435
* [ENHANCEMENT] Querier: Add new `cortex_querier_codec_response_size` metric to track the size of the encoded query responses from queriers. #6444
* [BUGFIX] Runtime-config: Handle absolute file paths when working directory is not / #6224
* [BUGFIX] Ruler: Allow rule evaluation to complete during shutdown. #6326
* [BUGFIX] Ring: update ring with new ip address when instance is lost, rejoins, but heartbeat is disabled. #6271
Expand Down
10 changes: 7 additions & 3 deletions pkg/api/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -231,11 +231,15 @@ func NewQuerierHandler(
nil,
false,
)
// Let's clear all codecs to create the instrumented ones
api.ClearCodecs()
cm := codec.NewInstrumentedCodecMetrics(reg)

// JSON codec is already installed. Install Protobuf codec to give the option for using either.
api.InstallCodec(codec.ProtobufCodec{CortexInternal: false})
api.InstallCodec(codec.NewInstrumentedCodec(v1.JSONCodec{}, cm))
// Install Protobuf codec to give the option for using either.
api.InstallCodec(codec.NewInstrumentedCodec(codec.ProtobufCodec{CortexInternal: false}, cm))
// Protobuf codec for Cortex internal requests. This should be used by Cortex Ruler only for remote evaluation.
api.InstallCodec(codec.ProtobufCodec{CortexInternal: true})
api.InstallCodec(codec.NewInstrumentedCodec(codec.ProtobufCodec{CortexInternal: true}, cm))

router := mux.NewRouter()

Expand Down
57 changes: 57 additions & 0 deletions pkg/querier/codec/instrumented_codec.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package codec

import (
"time"

"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
v1 "github.com/prometheus/prometheus/web/api/v1"
"github.com/weaveworks/common/middleware"
)

type InstrumentedCodecMetrics struct {
responseSizeHistogram *prometheus.HistogramVec
}

func NewInstrumentedCodecMetrics(reg prometheus.Registerer) *InstrumentedCodecMetrics {
return &InstrumentedCodecMetrics{
responseSizeHistogram: promauto.With(reg).NewHistogramVec(prometheus.HistogramOpts{
Namespace: "cortex",
Name: "querier_codec_response_size",
alanprot marked this conversation as resolved.
Show resolved Hide resolved
Help: "Size of the encoded prometheus response from the queriers.",
Buckets: middleware.BodySizeBuckets,
NativeHistogramBucketFactor: 1.1,
NativeHistogramMaxBucketNumber: 100,
NativeHistogramMinResetDuration: time.Hour,
}, []string{"content_type"}),
}
}

type InstrumentedCodec struct {
uc v1.Codec

metrics *InstrumentedCodecMetrics
}

func (c *InstrumentedCodec) ContentType() v1.MIMEType {
return c.uc.ContentType()
}

func (c *InstrumentedCodec) CanEncode(resp *v1.Response) bool {
return c.uc.CanEncode(resp)
}

func (c *InstrumentedCodec) Encode(resp *v1.Response) ([]byte, error) {
b, err := c.uc.Encode(resp)
if err == nil {
c.metrics.responseSizeHistogram.WithLabelValues(c.uc.ContentType().String()).Observe(float64(len((b))))
}
return b, err
}

func NewInstrumentedCodec(uc v1.Codec, m *InstrumentedCodecMetrics) v1.Codec {
return &InstrumentedCodec{
uc: uc,
metrics: m,
}
}
23 changes: 22 additions & 1 deletion pkg/querier/codec/protobuf_codec_test.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
package codec

import (
"bytes"
"fmt"
"testing"

"github.com/gogo/protobuf/proto"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/testutil"
"github.com/prometheus/common/model"
"github.com/prometheus/prometheus/model/histogram"
"github.com/prometheus/prometheus/model/labels"
Expand Down Expand Up @@ -437,7 +441,9 @@ func TestProtobufCodec_Encode(t *testing.T) {

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
codec := ProtobufCodec{CortexInternal: test.cortexInternal}
reg := prometheus.NewPedanticRegistry()
cm := NewInstrumentedCodecMetrics(reg)
codec := NewInstrumentedCodec(ProtobufCodec{CortexInternal: test.cortexInternal}, cm)
body, err := codec.Encode(&v1.Response{
Status: tripperware.StatusSuccess,
Data: test.data,
Expand All @@ -446,6 +452,21 @@ func TestProtobufCodec_Encode(t *testing.T) {
b, err := proto.Marshal(test.expected)
require.NoError(t, err)
require.Equal(t, string(b), string(body))
require.NoError(t, testutil.GatherAndCompare(reg, bytes.NewBufferString(fmt.Sprintf(`
# HELP cortex_querier_codec_response_size Size of the encoded prometheus response from the queriers.
# TYPE cortex_querier_codec_response_size histogram
cortex_querier_codec_response_size_bucket{content_type="`+codec.ContentType().String()+`",le="1.048576e+06"} 1
cortex_querier_codec_response_size_bucket{content_type="`+codec.ContentType().String()+`",le="2.62144e+06"} 1
cortex_querier_codec_response_size_bucket{content_type="`+codec.ContentType().String()+`",le="5.24288e+06"} 1
cortex_querier_codec_response_size_bucket{content_type="`+codec.ContentType().String()+`",le="1.048576e+07"} 1
cortex_querier_codec_response_size_bucket{content_type="`+codec.ContentType().String()+`",le="2.62144e+07"} 1
cortex_querier_codec_response_size_bucket{content_type="`+codec.ContentType().String()+`",le="5.24288e+07"} 1
cortex_querier_codec_response_size_bucket{content_type="`+codec.ContentType().String()+`",le="1.048576e+08"} 1
cortex_querier_codec_response_size_bucket{content_type="`+codec.ContentType().String()+`",le="2.62144e+08"} 1
cortex_querier_codec_response_size_bucket{content_type="`+codec.ContentType().String()+`",le="+Inf"} 1
cortex_querier_codec_response_size_sum{content_type="`+codec.ContentType().String()+`"} %v
cortex_querier_codec_response_size_count{content_type="`+codec.ContentType().String()+`"} 1
`, len(body))), "cortex_querier_codec_response_size"))
})
}
}
Loading