Skip to content

Commit 3f3a8bf

Browse files
bborehampracucci
andauthored
Querier: labels requests always fetch from store and respect time ranges (grafana#518)
* querier: always fetch labels from store Remove description of the feature as experimental; make the option `-querier.query-store-for-labels-enabled` always on. * Querier: labels requests obey query-ingesters-within If `-querier.query-ingesters-within` is set, make series, labels and label-values requests to ingesters only within that time. Ingesters may have data going back much further, depending on retention setting, so it's more efficient to query just the window we won't get from blocks storage. Historically we only queried ingesters, so it was good to make the request as long as possible, but now we always fetch labels from block storage we don't need to. * Use RegisterFlags with logger * Review feedback * Fix up calls to RegisterFlags In other tools and places that use the config struct. * Fix up test to match new behaviour. * Ingesters: respect start/end time on labels&series requests Previously ingester would return all current data if you asked for an old range. Now it will return data in range. With the new behaviour, if you don't set `EndTimestampMs` then it is treated as zero and you will get nothing back. This is on the internal gRPC API and it different to the Prometheus API which treats no end time as "till the end of time". One test needed fixing as it actually requested data from the wrong time - the zero value `time.Time{}` is a specific point in the past. Make it request data for all time instead. Fix up other tests to match the new behaviour. * Removed duplicated CHANGELOG entry Signed-off-by: Marco Pracucci <marco@pracucci.com> Co-authored-by: Marco Pracucci <marco@pracucci.com>
1 parent 7128022 commit 3f3a8bf

18 files changed

Lines changed: 83 additions & 149 deletions

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@
6767
* [CHANGE] Store-gateway: index cache now includes tenant in cache keys, this invalidates previous cached entries. #607
6868
* [CHANGE] Removed the deprecated `-<prefix>.fifocache.size` flag. #618
6969
* [CHANGE] Ruler: removed the support for the deprecated storage configuration via `-ruler.storage.*` CLI flags (and their respective YAML config options). Use `-ruler-storage.*` instead. #628
70+
* [CHANGE] Querier: always fetch labels from store and respect start/end times in request; the option `-querier.query-store-for-labels-enabled` is now always on. #518
7071
* [CHANGE] Query Frontend: range query response now omits the `data` field when it's empty (error case) like Prometheus does, previously it was `"data":{"resultType":"","result":null}`. #629
7172
* [CHANGE] Query Frontend: instant queries now honor the `-querier.max-retries-per-request` flag. #630
7273
* [CHANGE] Alertmanager: removed `-alertmanager.storage.*` configuration options, with the exception of the CLI flags `-alertmanager.storage.path` and `-alertmanager.storage.retention`. Use `-alertmanager-storage.*` instead. #632
@@ -161,6 +162,7 @@
161162
* [ENHANCEMENT] Ring/Memberlist: reduce CPU utilization for rings with a large number of members. #537 #563 #634
162163
* [ENHANCEMENT] Add histogram metrics `cortex_distributor_sample_delay_seconds` and `cortex_ingester_tsdb_sample_out_of_order_delta_seconds` #488
163164
* [ENHANCEMENT] Compactor: expose low-level concurrency options for compactor: `-compactor.max-opening-blocks-concurrency`, `-compactor.max-closing-blocks-concurrency`, `-compactor.symbols-flushers-concurrency`, used when using `split-and-merge` compaction strategy. #569
165+
* [ENHANCEMENT] Querier: labels requests now obey `-querier.query-ingesters-within`, making them a little more efficient. #518
164166
* [ENHANCEMENT] Store-gateway: label values with matchers now doesn't preload or list series, reducing latency and memory consumption. #534
165167
* [ENHANCEMENT] Azure client: expose option to configure MSI URL and user-assigned identity. #584
166168
* [ENHANCEMENT] Store-gateway: the results of `LabelNames()`, `LabelValues()` and `Series(skipChunks=true)` calls are now cached in the index cache. #590

cmd/mimir/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ func main() {
7676

7777
// This sets default values from flags to the config.
7878
// It needs to be called before parsing the config file!
79-
flagext.RegisterFlags(&cfg)
79+
flagext.RegisterFlagsWithLogger(util_log.Logger, &cfg)
8080

8181
if configFile != "" {
8282
if err := LoadConfig(configFile, expandENV, &cfg); err != nil {

docs/blocks-storage/querier.md

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -111,11 +111,6 @@ querier:
111111
# CLI flag: -querier.query-ingesters-within
112112
[query_ingesters_within: <duration> | default = 0s]
113113

114-
# Query long-term store for series, label values and label names APIs. Works
115-
# only with blocks engine.
116-
# CLI flag: -querier.query-store-for-labels-enabled
117-
[query_store_for_labels_enabled: <boolean> | default = false]
118-
119114
# True to enable queriers to use an optimized implementation which passes down
120115
# to ingesters the label matchers when running the label names API. Can be
121116
# enabled once all ingesters run a version >= the one where this option has

docs/configuration/config-file-reference.md

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -807,11 +807,6 @@ The `querier_config` configures the querier.
807807
# CLI flag: -querier.query-ingesters-within
808808
[query_ingesters_within: <duration> | default = 0s]
809809
810-
# Query long-term store for series, label values and label names APIs. Works
811-
# only with blocks engine.
812-
# CLI flag: -querier.query-store-for-labels-enabled
813-
[query_store_for_labels_enabled: <boolean> | default = false]
814-
815810
# True to enable queriers to use an optimized implementation which passes down
816811
# to ingesters the label matchers when running the label names API. Can be
817812
# enabled once all ingesters run a version >= the one where this option has been

docs/configuration/v1-guarantees.md

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ The Cortex maintainers commit to ensuring future version of Cortex can read data
2424
Cortex strives to be 100% API compatible with Prometheus (under `/prometheus/*` and `/api/prom/*`); any deviation from this is considered a bug, except:
2525

2626
- Requiring the `__name__` label on queries when querying the [chunks storage](../chunks-storage/_index.md) (queries to ingesters or clusters running the blocks storage are not affected).
27-
- For queries to the `/api/v1/series`, `/api/v1/labels` and `/api/v1/label/{name}/values` endpoints, query's time range is ignored and the data is always fetched from ingesters. There is experimental support to query the long-term store with the _blocks_ storage engine when `-querier.query-store-for-labels-enabled` is set.
2827
- Additional API endpoints for creating, removing and modifying alerts and recording rules.
2928
- Additional API around pushing metrics (under `/api/push`).
3029
- Additional API endpoints for management of Cortex itself, such as the ring. These APIs are not part of the any compatibility guarantees.
@@ -54,7 +53,6 @@ Currently experimental features are:
5453
- OpenStack Swift storage support (both in blocks and chunks storage).
5554
- Metric relabeling in the distributor.
5655
- Scalable query-frontend (when using query-scheduler)
57-
- Querying store for series, labels APIs (`-querier.query-store-for-labels-enabled`)
5856
- Distributor: do not extend writes on unhealthy ingesters (`-distributor.extend-writes=false`)
5957
- Tenant Deletion in Purger, for blocks storage.
6058
- Query-frontend: query stats tracking (`-frontend.query-stats-enabled`)

integration/getting_started_single_process_config_test.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ package integration
88

99
import (
1010
"fmt"
11+
"math"
1112
"testing"
1213
"time"
1314

@@ -63,11 +64,15 @@ func TestGettingStartedSingleProcessConfigWithBlocksStorage(t *testing.T) {
6364
require.Equal(t, model.ValVector, result.Type())
6465
assert.Equal(t, expectedVector, result.(model.Vector))
6566

66-
labelValues, err := c.LabelValues("foo", time.Time{}, time.Time{}, nil)
67+
// Work round Prometheus client lib not having a way to omit the start&end params
68+
minTime := time.Unix(math.MinInt64/1000+62135596801, 0).UTC()
69+
maxTime := time.Unix(math.MaxInt64/1000-62135596801, 999999999).UTC()
70+
71+
labelValues, err := c.LabelValues("foo", minTime, maxTime, nil)
6772
require.NoError(t, err)
6873
require.Equal(t, model.LabelValues{"bar"}, labelValues)
6974

70-
labelNames, err := c.LabelNames(time.Time{}, time.Time{})
75+
labelNames, err := c.LabelNames(minTime, maxTime)
7176
require.NoError(t, err)
7277
require.Equal(t, []string{"__name__", "foo"}, labelNames)
7378

integration/querier_test.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -674,13 +674,13 @@ func testMetadataQueriesWithBlocksStorage(
674674
},
675675
labelNames: []string{labels.MetricName, lastSeriesInStorageName, lastSeriesInIngesterBlocksName, firstSeriesInIngesterHeadName},
676676
},
677-
"query metadata entirely outside the ingester range should return the head data as well": {
677+
"query metadata entirely outside the ingester range should not return the head data": {
678678
from: lastSeriesInStorageTs.Add(-2 * blockRangePeriod),
679679
to: lastSeriesInStorageTs,
680680
seriesTests: []seriesTest{
681681
{
682682
lookup: firstSeriesInIngesterHeadName,
683-
ok: true,
683+
ok: false,
684684
resp: firstSeriesInIngesterHead.Labels,
685685
},
686686
{
@@ -696,7 +696,7 @@ func testMetadataQueriesWithBlocksStorage(
696696
labelValuesTests: []labelValuesTest{
697697
{
698698
label: labels.MetricName,
699-
resp: []string{lastSeriesInStorageName, firstSeriesInIngesterHeadName},
699+
resp: []string{lastSeriesInStorageName},
700700
},
701701
{
702702
label: labels.MetricName,
@@ -705,11 +705,11 @@ func testMetadataQueriesWithBlocksStorage(
705705
},
706706
{
707707
label: labels.MetricName,
708-
resp: []string{firstSeriesInIngesterHeadName},
708+
resp: []string{},
709709
matches: []string{firstSeriesInIngesterHeadName},
710710
},
711711
},
712-
labelNames: []string{labels.MetricName, lastSeriesInStorageName, firstSeriesInIngesterHeadName},
712+
labelNames: []string{labels.MetricName, lastSeriesInStorageName},
713713
},
714714
}
715715

integration/query_frontend_test.go

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -257,16 +257,14 @@ func runQueryFrontendTest(t *testing.T, cfg queryFrontendTestConfig) {
257257
require.Regexp(t, "querier_wall_time;dur=[0-9.]*, response_time;dur=[0-9.]*$", res.Header.Values("Server-Timing")[0])
258258
}
259259

260-
// In this test we do ensure that the /series start/end time is ignored and Mimir
261-
// always returns series in ingesters memory. No need to repeat it for each user.
260+
// Beyond the range of -querier.query-ingesters-within should return nothing. No need to repeat it for each user.
262261
if userID == 0 {
263262
start := now.Add(-1000 * time.Hour)
264263
end := now.Add(-999 * time.Hour)
265264

266265
result, err := c.Series([]string{"series_1"}, start, end)
267266
require.NoError(t, err)
268-
require.Len(t, result, 1)
269-
assert.Equal(t, model.LabelSet{labels.MetricName: "series_1"}, result[0])
267+
require.Len(t, result, 0)
270268
}
271269

272270
for q := 0; q < numQueriesPerUser; q++ {

pkg/ingester/ingester_v2.go

Lines changed: 2 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import (
99
"context"
1010
"fmt"
1111
"io"
12-
"math"
1312
"net/http"
1413
"os"
1514
"path/filepath"
@@ -1164,12 +1163,7 @@ func (i *Ingester) LabelValues(ctx context.Context, req *client.LabelValuesReque
11641163
return &client.LabelValuesResponse{}, nil
11651164
}
11661165

1167-
mint, maxt, err := metadataQueryRange(startTimestampMs, endTimestampMs, db)
1168-
if err != nil {
1169-
return nil, err
1170-
}
1171-
1172-
q, err := db.Querier(ctx, mint, maxt)
1166+
q, err := db.Querier(ctx, startTimestampMs, endTimestampMs)
11731167
if err != nil {
11741168
return nil, err
11751169
}
@@ -1205,11 +1199,6 @@ func (i *Ingester) LabelNames(ctx context.Context, req *client.LabelNamesRequest
12051199
return nil, err
12061200
}
12071201

1208-
mint, maxt, err = metadataQueryRange(mint, maxt, db)
1209-
if err != nil {
1210-
return nil, err
1211-
}
1212-
12131202
q, err := db.Querier(ctx, mint, maxt)
12141203
if err != nil {
12151204
return nil, err
@@ -1247,11 +1236,7 @@ func (i *Ingester) MetricsForLabelMatchers(ctx context.Context, req *client.Metr
12471236
return nil, err
12481237
}
12491238

1250-
mint, maxt, err := metadataQueryRange(req.StartTimestampMs, req.EndTimestampMs, db)
1251-
if err != nil {
1252-
return nil, err
1253-
}
1254-
1239+
mint, maxt := req.StartTimestampMs, req.EndTimestampMs
12551240
q, err := db.Querier(ctx, mint, maxt)
12561241
if err != nil {
12571242
return nil, err
@@ -2370,33 +2355,6 @@ func (i *Ingester) FlushHandler(w http.ResponseWriter, r *http.Request) {
23702355
w.WriteHeader(http.StatusNoContent)
23712356
}
23722357

2373-
// metadataQueryRange returns the best range to query for metadata queries based on the timerange in the ingester.
2374-
func metadataQueryRange(queryStart, queryEnd int64, db *userTSDB) (mint, maxt int64, err error) {
2375-
// Ingesters are run with limited retention and we don't support querying the store-gateway for labels yet.
2376-
// This means if someone loads a dashboard that is outside the range of the ingester, and we only return the
2377-
// data for the timerange requested (which will be empty), the dashboards will break. To fix this we should
2378-
// return the "head block" range until we can query the store-gateway.
2379-
2380-
// Now the question would be what to do when the query is partially in the ingester range. I would err on the side
2381-
// of caution and query the entire db, as I can't think of a good way to query the head + the overlapping range.
2382-
mint, maxt = queryStart, queryEnd
2383-
2384-
lowestTs, err := db.StartTime()
2385-
if err != nil {
2386-
return mint, maxt, err
2387-
}
2388-
2389-
// Completely outside.
2390-
if queryEnd < lowestTs {
2391-
mint, maxt = db.Head().MinTime(), db.Head().MaxTime()
2392-
} else if queryStart < lowestTs {
2393-
// Partially inside.
2394-
mint, maxt = 0, math.MaxInt64
2395-
}
2396-
2397-
return
2398-
}
2399-
24002358
func wrappedTSDBIngestErr(ingestErr error, timestamp model.Time, labels []mimirpb.LabelAdapter) error {
24012359
if ingestErr == nil {
24022360
return nil

pkg/ingester/ingester_v2_test.go

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1248,7 +1248,7 @@ func Test_Ingester_LabelNames(t *testing.T) {
12481248
expected := []string{"__name__", "status", "route"}
12491249

12501250
// Get label names
1251-
res, err := i.LabelNames(ctx, &client.LabelNamesRequest{})
1251+
res, err := i.LabelNames(ctx, &client.LabelNamesRequest{EndTimestampMs: math.MaxInt64})
12521252
require.NoError(t, err)
12531253
assert.ElementsMatch(t, expected, res.LabelNames)
12541254
})
@@ -1311,7 +1311,7 @@ func Test_Ingester_LabelValues(t *testing.T) {
13111311

13121312
// Get label values
13131313
for labelName, expectedValues := range expected {
1314-
req := &client.LabelValuesRequest{LabelName: labelName}
1314+
req := &client.LabelValuesRequest{LabelName: labelName, EndTimestampMs: math.MaxInt64}
13151315
res, err := i.LabelValues(ctx, req)
13161316
require.NoError(t, err)
13171317
assert.ElementsMatch(t, expectedValues, res.LabelValues)
@@ -1802,7 +1802,7 @@ func TestIngester_LabelNames_ShouldNotCreateTSDBIfDoesNotExists(t *testing.T) {
18021802
// Mock request
18031803
userID := "test"
18041804
ctx := user.InjectOrgID(context.Background(), userID)
1805-
req := &client.LabelNamesRequest{}
1805+
req := &client.LabelNamesRequest{EndTimestampMs: math.MaxInt64}
18061806

18071807
res, err := i.LabelNames(ctx, req)
18081808
require.NoError(t, err)
@@ -1963,18 +1963,15 @@ func Test_Ingester_MetricsForLabelMatchers(t *testing.T) {
19631963
{Labels: mimirpb.FromLabelsToLabelAdapters(fixtures[2].lbls)},
19641964
},
19651965
},
1966-
"should NOT filter metrics by time range to always return known metrics even when queried for older time ranges": {
1966+
"should filter metrics by time range to return nothing when queried for older time ranges": {
19671967
from: 100,
19681968
to: 1000,
19691969
matchers: []*client.LabelMatchers{{
19701970
Matchers: []*client.LabelMatcher{
19711971
{Type: client.EQUAL, Name: model.MetricNameLabel, Value: "test_1"},
19721972
},
19731973
}},
1974-
expected: []*mimirpb.Metric{
1975-
{Labels: mimirpb.FromLabelsToLabelAdapters(fixtures[0].lbls)},
1976-
{Labels: mimirpb.FromLabelsToLabelAdapters(fixtures[1].lbls)},
1977-
},
1974+
expected: []*mimirpb.Metric{},
19781975
},
19791976
"should not return duplicated metrics on overlapping matchers": {
19801977
from: math.MinInt64,

0 commit comments

Comments
 (0)