From 86c9bd21e817e076d972cc3717f9a7f1b23ba3cd Mon Sep 17 00:00:00 2001 From: Ryan Hall Date: Tue, 25 May 2021 12:02:23 -0700 Subject: [PATCH 1/2] Add M3-Limit-Instance-Multiple header This allows overriding the query instance_multiple config value per request. Will be useful for testing the feature on a per request basis. --- .../handleroptions/fetch_options.go | 22 +++++++++++++++++-- .../handleroptions/fetch_options_test.go | 17 ++++++++++++++ src/x/headers/headers.go | 3 +++ 3 files changed, 40 insertions(+), 2 deletions(-) diff --git a/src/query/api/v1/handler/prometheus/handleroptions/fetch_options.go b/src/query/api/v1/handler/prometheus/handleroptions/fetch_options.go index a812d19828..2e789d51c6 100644 --- a/src/query/api/v1/handler/prometheus/handleroptions/fetch_options.go +++ b/src/query/api/v1/handler/prometheus/handleroptions/fetch_options.go @@ -134,6 +134,20 @@ func ParseLimit(req *http.Request, header, formValue string, defaultLimit int) ( return defaultLimit, nil } +// ParseInstanceMultiple parses request instance multiple from header. +func ParseInstanceMultiple(req *http.Request, defaultValue float32) (float32, error) { + if str := req.Header.Get(headers.LimitInstanceMultipleHeader); str != "" { + v, err := strconv.ParseFloat(str, 32) + if err != nil { + err = fmt.Errorf( + "could not parse instance multiple: input=%s, err=%v", str, err) + return 0, err + } + return float32(v), nil + } + return defaultValue, nil +} + // ParseRequireExhaustive parses request limit require exhaustive from header or // query string. func ParseRequireExhaustive(req *http.Request, defaultValue bool) (bool, error) { @@ -214,9 +228,13 @@ func (b fetchOptionsBuilder) newFetchOptions( if err != nil { return nil, nil, err } - fetchOpts.SeriesLimit = seriesLimit - fetchOpts.InstanceMultiple = b.opts.Limits.InstanceMultiple + + instanceMultiple, err := ParseInstanceMultiple(req, b.opts.Limits.InstanceMultiple) + if err != nil { + return nil, nil, err + } + fetchOpts.InstanceMultiple = instanceMultiple docsLimit, err := ParseLimit(req, headers.LimitMaxDocsHeader, "docsLimit", b.opts.Limits.DocsLimit) diff --git a/src/query/api/v1/handler/prometheus/handleroptions/fetch_options_test.go b/src/query/api/v1/handler/prometheus/handleroptions/fetch_options_test.go index f2b4dfae2c..7fe97544e6 100644 --- a/src/query/api/v1/handler/prometheus/handleroptions/fetch_options_test.go +++ b/src/query/api/v1/handler/prometheus/handleroptions/fetch_options_test.go @@ -475,3 +475,20 @@ func TestTimeoutParseWithGetRequestParam(t *testing.T) { assert.NoError(t, err) assert.Equal(t, timeout, time.Millisecond) } + +func TestInstanceMultiple(t *testing.T) { + req := httptest.NewRequest("GET", "/", nil) + m, err := ParseInstanceMultiple(req, 2.0) + require.NoError(t, err) + require.Equal(t, float32(2.0), m) + + req.Header.Set(headers.LimitInstanceMultipleHeader, "3.0") + m, err = ParseInstanceMultiple(req, 2.0) + require.NoError(t, err) + require.Equal(t, float32(3.0), m) + + req.Header.Set(headers.LimitInstanceMultipleHeader, "blah") + m, err = ParseInstanceMultiple(req, 2.0) + require.Error(t, err) + require.Contains(t, err.Error(), "could not parse instance multiple") +} diff --git a/src/x/headers/headers.go b/src/x/headers/headers.go index 59cede447d..76368b7320 100644 --- a/src/x/headers/headers.go +++ b/src/x/headers/headers.go @@ -84,6 +84,9 @@ const ( // the number of time series returned by each storage node. LimitMaxSeriesHeader = M3HeaderPrefix + "Limit-Max-Series" + // LimitInstanceMultipleHeader overrides the PerQueryLimitsConfiguration.InstanceMultiple for the request. + LimitInstanceMultipleHeader = M3HeaderPrefix + "Limit-Instance-Multiple" + // LimitMaxDocsHeader is the M3 limit docs header that limits // the number of docs returned by each storage node. LimitMaxDocsHeader = M3HeaderPrefix + "Limit-Max-Docs" From 23ed009b512a48de93aa0b89f0aeda73c356b107 Mon Sep 17 00:00:00 2001 From: Ryan Hall Date: Tue, 25 May 2021 12:39:53 -0700 Subject: [PATCH 2/2] lint --- .../api/v1/handler/prometheus/handleroptions/fetch_options.go | 2 +- .../v1/handler/prometheus/handleroptions/fetch_options_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/query/api/v1/handler/prometheus/handleroptions/fetch_options.go b/src/query/api/v1/handler/prometheus/handleroptions/fetch_options.go index 2e789d51c6..d30e631be8 100644 --- a/src/query/api/v1/handler/prometheus/handleroptions/fetch_options.go +++ b/src/query/api/v1/handler/prometheus/handleroptions/fetch_options.go @@ -140,7 +140,7 @@ func ParseInstanceMultiple(req *http.Request, defaultValue float32) (float32, er v, err := strconv.ParseFloat(str, 32) if err != nil { err = fmt.Errorf( - "could not parse instance multiple: input=%s, err=%v", str, err) + "could not parse instance multiple: input=%s, err=%w", str, err) return 0, err } return float32(v), nil diff --git a/src/query/api/v1/handler/prometheus/handleroptions/fetch_options_test.go b/src/query/api/v1/handler/prometheus/handleroptions/fetch_options_test.go index 7fe97544e6..312d8589f3 100644 --- a/src/query/api/v1/handler/prometheus/handleroptions/fetch_options_test.go +++ b/src/query/api/v1/handler/prometheus/handleroptions/fetch_options_test.go @@ -488,7 +488,7 @@ func TestInstanceMultiple(t *testing.T) { require.Equal(t, float32(3.0), m) req.Header.Set(headers.LimitInstanceMultipleHeader, "blah") - m, err = ParseInstanceMultiple(req, 2.0) + _, err = ParseInstanceMultiple(req, 2.0) require.Error(t, err) require.Contains(t, err.Error(), "could not parse instance multiple") }