diff --git a/CHANGELOG.md b/CHANGELOG.md index ab4540b930..cbf1d2e172 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -44,6 +44,7 @@ * [ENHANCEMENT] Compactor: Split cleaner cycle for active and deleted tenants. #6112 * [ENHANCEMENT] Compactor: Introduce cleaner visit marker. #6113 * [ENHANCEMENT] Query Frontend: Add cortex_query_samples_total metric. #6142 +* [ENHANCEMENT] Ingester: Implement metadata API limit. #6128 * [BUGFIX] Configsdb: Fix endline issue in db password. #5920 * [BUGFIX] Ingester: Fix `user` and `type` labels for the `cortex_ingester_tsdb_head_samples_appended_total` TSDB metric. #5952 * [BUGFIX] Querier: Enforce max query length check for `/api/v1/series` API even though `ignoreMaxQueryLength` is set to true. #6018 diff --git a/pkg/distributor/distributor.go b/pkg/distributor/distributor.go index 664a221b10..82b49a5631 100644 --- a/pkg/distributor/distributor.go +++ b/pkg/distributor/distributor.go @@ -21,6 +21,7 @@ import ( "github.com/prometheus/prometheus/model/labels" "github.com/prometheus/prometheus/model/relabel" "github.com/prometheus/prometheus/scrape" + "github.com/prometheus/prometheus/storage" "github.com/weaveworks/common/httpgrpc" "github.com/weaveworks/common/instrument" "github.com/weaveworks/common/user" @@ -671,7 +672,7 @@ func (d *Distributor) Push(ctx context.Context, req *cortexpb.WriteRequest) (*co return nil, err } // If there wasn't an error but removeReplica is false that means we didn't find both HA labels. - if !removeReplica { + if !removeReplica { // False, Nil d.nonHASamples.WithLabelValues(userID).Add(float64(numFloatSamples + numHistogramSamples)) } } @@ -1021,7 +1022,7 @@ func (d *Distributor) ForReplicationSet(ctx context.Context, replicationSet ring }) } -func (d *Distributor) LabelValuesForLabelNameCommon(ctx context.Context, from, to model.Time, labelName model.LabelName, f func(ctx context.Context, rs ring.ReplicationSet, req *ingester_client.LabelValuesRequest) ([]interface{}, error), matchers ...*labels.Matcher) ([]string, error) { +func (d *Distributor) LabelValuesForLabelNameCommon(ctx context.Context, from, to model.Time, labelName model.LabelName, hints *storage.LabelHints, f func(ctx context.Context, rs ring.ReplicationSet, req *ingester_client.LabelValuesRequest) ([]interface{}, error), matchers ...*labels.Matcher) ([]string, error) { span, ctx := opentracing.StartSpanFromContext(ctx, "Distributor.LabelValues", opentracing.Tags{ "name": labelName, "start": from.Unix(), @@ -1032,8 +1033,8 @@ func (d *Distributor) LabelValuesForLabelNameCommon(ctx context.Context, from, t if err != nil { return nil, err } - - req, err := ingester_client.ToLabelValuesRequest(labelName, from, to, matchers) + limit := getLimitFromLabelHints(hints) + req, err := ingester_client.ToLabelValuesRequest(labelName, from, to, limit, matchers) if err != nil { return nil, err } @@ -1053,13 +1054,16 @@ func (d *Distributor) LabelValuesForLabelNameCommon(ctx context.Context, from, t if err != nil { return nil, err } + if limit > 0 && len(r) > limit { + r = r[:limit] + } span.SetTag("result_length", len(r)) return r, nil } // LabelValuesForLabelName returns all the label values that are associated with a given label name. -func (d *Distributor) LabelValuesForLabelName(ctx context.Context, from, to model.Time, labelName model.LabelName, matchers ...*labels.Matcher) ([]string, error) { - return d.LabelValuesForLabelNameCommon(ctx, from, to, labelName, func(ctx context.Context, rs ring.ReplicationSet, req *ingester_client.LabelValuesRequest) ([]interface{}, error) { +func (d *Distributor) LabelValuesForLabelName(ctx context.Context, from, to model.Time, labelName model.LabelName, hint *storage.LabelHints, matchers ...*labels.Matcher) ([]string, error) { + return d.LabelValuesForLabelNameCommon(ctx, from, to, labelName, hint, func(ctx context.Context, rs ring.ReplicationSet, req *ingester_client.LabelValuesRequest) ([]interface{}, error) { return d.ForReplicationSet(ctx, rs, d.cfg.ZoneResultsQuorumMetadata, func(ctx context.Context, client ingester_client.IngesterClient) (interface{}, error) { resp, err := client.LabelValues(ctx, req) if err != nil { @@ -1071,8 +1075,8 @@ func (d *Distributor) LabelValuesForLabelName(ctx context.Context, from, to mode } // LabelValuesForLabelNameStream returns all the label values that are associated with a given label name. -func (d *Distributor) LabelValuesForLabelNameStream(ctx context.Context, from, to model.Time, labelName model.LabelName, matchers ...*labels.Matcher) ([]string, error) { - return d.LabelValuesForLabelNameCommon(ctx, from, to, labelName, func(ctx context.Context, rs ring.ReplicationSet, req *ingester_client.LabelValuesRequest) ([]interface{}, error) { +func (d *Distributor) LabelValuesForLabelNameStream(ctx context.Context, from, to model.Time, labelName model.LabelName, hint *storage.LabelHints, matchers ...*labels.Matcher) ([]string, error) { + return d.LabelValuesForLabelNameCommon(ctx, from, to, labelName, hint, func(ctx context.Context, rs ring.ReplicationSet, req *ingester_client.LabelValuesRequest) ([]interface{}, error) { return d.ForReplicationSet(ctx, rs, d.cfg.ZoneResultsQuorumMetadata, func(ctx context.Context, client ingester_client.IngesterClient) (interface{}, error) { stream, err := client.LabelValuesStream(ctx, req) if err != nil { @@ -1096,7 +1100,7 @@ func (d *Distributor) LabelValuesForLabelNameStream(ctx context.Context, from, t }, matchers...) } -func (d *Distributor) LabelNamesCommon(ctx context.Context, from, to model.Time, f func(ctx context.Context, rs ring.ReplicationSet, req *ingester_client.LabelNamesRequest) ([]interface{}, error)) ([]string, error) { +func (d *Distributor) LabelNamesCommon(ctx context.Context, from, to model.Time, hints *storage.LabelHints, f func(ctx context.Context, rs ring.ReplicationSet, req *ingester_client.LabelNamesRequest) ([]interface{}, error)) ([]string, error) { span, ctx := opentracing.StartSpanFromContext(ctx, "Distributor.LabelNames", opentracing.Tags{ "start": from.Unix(), "end": to.Unix(), @@ -1107,9 +1111,11 @@ func (d *Distributor) LabelNamesCommon(ctx context.Context, from, to model.Time, return nil, err } + limit := getLimitFromLabelHints(hints) req := &ingester_client.LabelNamesRequest{ StartTimestampMs: int64(from), EndTimestampMs: int64(to), + Limit: int64(limit), } resps, err := f(ctx, replicationSet, req) if err != nil { @@ -1126,13 +1132,17 @@ func (d *Distributor) LabelNamesCommon(ctx context.Context, from, to model.Time, if err != nil { return nil, err } + if limit > 0 && len(r) > limit { + r = r[:limit] + } + span.SetTag("result_length", len(r)) return r, nil } -func (d *Distributor) LabelNamesStream(ctx context.Context, from, to model.Time) ([]string, error) { - return d.LabelNamesCommon(ctx, from, to, func(ctx context.Context, rs ring.ReplicationSet, req *ingester_client.LabelNamesRequest) ([]interface{}, error) { +func (d *Distributor) LabelNamesStream(ctx context.Context, from, to model.Time, hints *storage.LabelHints) ([]string, error) { + return d.LabelNamesCommon(ctx, from, to, hints, func(ctx context.Context, rs ring.ReplicationSet, req *ingester_client.LabelNamesRequest) ([]interface{}, error) { return d.ForReplicationSet(ctx, rs, d.cfg.ZoneResultsQuorumMetadata, func(ctx context.Context, client ingester_client.IngesterClient) (interface{}, error) { stream, err := client.LabelNamesStream(ctx, req) if err != nil { @@ -1157,8 +1167,8 @@ func (d *Distributor) LabelNamesStream(ctx context.Context, from, to model.Time) } // LabelNames returns all the label names. -func (d *Distributor) LabelNames(ctx context.Context, from, to model.Time) ([]string, error) { - return d.LabelNamesCommon(ctx, from, to, func(ctx context.Context, rs ring.ReplicationSet, req *ingester_client.LabelNamesRequest) ([]interface{}, error) { +func (d *Distributor) LabelNames(ctx context.Context, from, to model.Time, hint *storage.LabelHints) ([]string, error) { + return d.LabelNamesCommon(ctx, from, to, hint, func(ctx context.Context, rs ring.ReplicationSet, req *ingester_client.LabelNamesRequest) ([]interface{}, error) { return d.ForReplicationSet(ctx, rs, d.cfg.ZoneResultsQuorumMetadata, func(ctx context.Context, client ingester_client.IngesterClient) (interface{}, error) { resp, err := client.LabelNames(ctx, req) if err != nil { @@ -1170,8 +1180,8 @@ func (d *Distributor) LabelNames(ctx context.Context, from, to model.Time) ([]st } // MetricsForLabelMatchers gets the metrics that match said matchers -func (d *Distributor) MetricsForLabelMatchers(ctx context.Context, from, through model.Time, matchers ...*labels.Matcher) ([]model.Metric, error) { - return d.metricsForLabelMatchersCommon(ctx, from, through, func(ctx context.Context, rs ring.ReplicationSet, req *ingester_client.MetricsForLabelMatchersRequest, metrics *map[model.Fingerprint]model.Metric, mutex *sync.Mutex, queryLimiter *limiter.QueryLimiter) error { +func (d *Distributor) MetricsForLabelMatchers(ctx context.Context, from, through model.Time, hint *storage.SelectHints, matchers ...*labels.Matcher) ([]model.Metric, error) { + return d.metricsForLabelMatchersCommon(ctx, from, through, hint, func(ctx context.Context, rs ring.ReplicationSet, req *ingester_client.MetricsForLabelMatchersRequest, metrics *map[model.Fingerprint]model.Metric, mutex *sync.Mutex, queryLimiter *limiter.QueryLimiter) error { _, err := d.ForReplicationSet(ctx, rs, false, func(ctx context.Context, client ingester_client.IngesterClient) (interface{}, error) { resp, err := client.MetricsForLabelMatchers(ctx, req) if err != nil { @@ -1199,8 +1209,8 @@ func (d *Distributor) MetricsForLabelMatchers(ctx context.Context, from, through }, matchers...) } -func (d *Distributor) MetricsForLabelMatchersStream(ctx context.Context, from, through model.Time, matchers ...*labels.Matcher) ([]model.Metric, error) { - return d.metricsForLabelMatchersCommon(ctx, from, through, func(ctx context.Context, rs ring.ReplicationSet, req *ingester_client.MetricsForLabelMatchersRequest, metrics *map[model.Fingerprint]model.Metric, mutex *sync.Mutex, queryLimiter *limiter.QueryLimiter) error { +func (d *Distributor) MetricsForLabelMatchersStream(ctx context.Context, from, through model.Time, hint *storage.SelectHints, matchers ...*labels.Matcher) ([]model.Metric, error) { + return d.metricsForLabelMatchersCommon(ctx, from, through, hint, func(ctx context.Context, rs ring.ReplicationSet, req *ingester_client.MetricsForLabelMatchersRequest, metrics *map[model.Fingerprint]model.Metric, mutex *sync.Mutex, queryLimiter *limiter.QueryLimiter) error { _, err := d.ForReplicationSet(ctx, rs, false, func(ctx context.Context, client ingester_client.IngesterClient) (interface{}, error) { stream, err := client.MetricsForLabelMatchersStream(ctx, req) if err != nil { @@ -1239,14 +1249,14 @@ func (d *Distributor) MetricsForLabelMatchersStream(ctx context.Context, from, t }, matchers...) } -func (d *Distributor) metricsForLabelMatchersCommon(ctx context.Context, from, through model.Time, f func(context.Context, ring.ReplicationSet, *ingester_client.MetricsForLabelMatchersRequest, *map[model.Fingerprint]model.Metric, *sync.Mutex, *limiter.QueryLimiter) error, matchers ...*labels.Matcher) ([]model.Metric, error) { +func (d *Distributor) metricsForLabelMatchersCommon(ctx context.Context, from, through model.Time, hints *storage.SelectHints, f func(context.Context, ring.ReplicationSet, *ingester_client.MetricsForLabelMatchersRequest, *map[model.Fingerprint]model.Metric, *sync.Mutex, *limiter.QueryLimiter) error, matchers ...*labels.Matcher) ([]model.Metric, error) { replicationSet, err := d.GetIngestersForMetadata(ctx) queryLimiter := limiter.QueryLimiterFromContextWithFallback(ctx) if err != nil { return nil, err } - req, err := ingester_client.ToMetricsForLabelMatchersRequest(from, through, matchers) + req, err := ingester_client.ToMetricsForLabelMatchersRequest(from, through, getLimitFromSelectHints(hints), matchers) if err != nil { return nil, err } @@ -1438,3 +1448,17 @@ func findHALabels(replicaLabel, clusterLabel string, labels []cortexpb.LabelAdap return cluster, replica } + +func getLimitFromLabelHints(hints *storage.LabelHints) int { + if hints != nil { + return hints.Limit + } + return 0 +} + +func getLimitFromSelectHints(hints *storage.SelectHints) int { + if hints != nil { + return hints.Limit + } + return 0 +} diff --git a/pkg/distributor/distributor_test.go b/pkg/distributor/distributor_test.go index 75c104528d..15ed3cad20 100644 --- a/pkg/distributor/distributor_test.go +++ b/pkg/distributor/distributor_test.go @@ -1904,7 +1904,7 @@ func BenchmarkDistributor_GetLabelsValues(b *testing.B) { b.ResetTimer() b.ReportAllocs() for i := 0; i < b.N; i++ { - _, err := ds[0].LabelValuesForLabelName(ctx, model.Time(time.Now().UnixMilli()), model.Time(time.Now().UnixMilli()), "__name__") + _, err := ds[0].LabelValuesForLabelName(ctx, model.Time(time.Now().UnixMilli()), model.Time(time.Now().UnixMilli()), "__name__", nil) require.NoError(b, err) } }) @@ -2270,7 +2270,7 @@ func TestDistributor_MetricsForLabelMatchers_SingleSlowIngester(t *testing.T) { } for i := 0; i < 50; i++ { - _, err := ds[0].MetricsForLabelMatchers(ctx, now, now, mustNewMatcher(labels.MatchEqual, model.MetricNameLabel, "test")) + _, err := ds[0].MetricsForLabelMatchers(ctx, now, now, nil, mustNewMatcher(labels.MatchEqual, model.MetricNameLabel, "test")) require.NoError(t, err) } } @@ -2439,7 +2439,7 @@ func TestDistributor_MetricsForLabelMatchers(t *testing.T) { } { - metrics, err := ds[0].MetricsForLabelMatchers(ctx, now, now, testData.matchers...) + metrics, err := ds[0].MetricsForLabelMatchers(ctx, now, now, nil, testData.matchers...) if testData.expectedErr != nil { assert.ErrorIs(t, err, testData.expectedErr) @@ -2457,7 +2457,7 @@ func TestDistributor_MetricsForLabelMatchers(t *testing.T) { } { - metrics, err := ds[0].MetricsForLabelMatchersStream(ctx, now, now, testData.matchers...) + metrics, err := ds[0].MetricsForLabelMatchersStream(ctx, now, now, nil, testData.matchers...) if testData.expectedErr != nil { assert.ErrorIs(t, err, testData.expectedErr) return @@ -2544,7 +2544,7 @@ func BenchmarkDistributor_MetricsForLabelMatchers(b *testing.B) { for n := 0; n < b.N; n++ { now := model.Now() - metrics, err := ds[0].MetricsForLabelMatchers(ctx, now, now, testData.matchers...) + metrics, err := ds[0].MetricsForLabelMatchers(ctx, now, now, nil, testData.matchers...) if testData.expectedErr != nil { assert.EqualError(b, err, testData.expectedErr.Error()) @@ -3197,7 +3197,7 @@ func (i *mockIngester) MetricsForLabelMatchersStream(ctx context.Context, req *c return nil, errFail } - _, _, multiMatchers, err := client.FromMetricsForLabelMatchersRequest(req) + _, _, _, multiMatchers, err := client.FromMetricsForLabelMatchersRequest(req) if err != nil { return nil, err } @@ -3229,7 +3229,7 @@ func (i *mockIngester) MetricsForLabelMatchers(ctx context.Context, req *client. return nil, errFail } - _, _, multiMatchers, err := client.FromMetricsForLabelMatchersRequest(req) + _, _, _, multiMatchers, err := client.FromMetricsForLabelMatchersRequest(req) if err != nil { return nil, err } diff --git a/pkg/ingester/client/compat.go b/pkg/ingester/client/compat.go index cbac06a46f..6e4a81d634 100644 --- a/pkg/ingester/client/compat.go +++ b/pkg/ingester/client/compat.go @@ -114,7 +114,7 @@ func ToQueryResponse(matrix model.Matrix) *QueryResponse { } // ToMetricsForLabelMatchersRequest builds a MetricsForLabelMatchersRequest proto -func ToMetricsForLabelMatchersRequest(from, to model.Time, matchers []*labels.Matcher) (*MetricsForLabelMatchersRequest, error) { +func ToMetricsForLabelMatchersRequest(from, to model.Time, limit int, matchers []*labels.Matcher) (*MetricsForLabelMatchersRequest, error) { ms, err := toLabelMatchers(matchers) if err != nil { return nil, err @@ -124,6 +124,7 @@ func ToMetricsForLabelMatchersRequest(from, to model.Time, matchers []*labels.Ma StartTimestampMs: int64(from), EndTimestampMs: int64(to), MatchersSet: []*LabelMatchers{{Matchers: ms}}, + Limit: int64(limit), }, nil } @@ -174,22 +175,22 @@ func SeriesSetToQueryResponse(s storage.SeriesSet) (*QueryResponse, error) { } // FromMetricsForLabelMatchersRequest unpacks a MetricsForLabelMatchersRequest proto -func FromMetricsForLabelMatchersRequest(req *MetricsForLabelMatchersRequest) (model.Time, model.Time, [][]*labels.Matcher, error) { +func FromMetricsForLabelMatchersRequest(req *MetricsForLabelMatchersRequest) (model.Time, model.Time, int, [][]*labels.Matcher, error) { matchersSet := make([][]*labels.Matcher, 0, len(req.MatchersSet)) for _, matchers := range req.MatchersSet { matchers, err := FromLabelMatchers(matchers.Matchers) if err != nil { - return 0, 0, nil, err + return 0, 0, 0, nil, err } matchersSet = append(matchersSet, matchers) } from := model.Time(req.StartTimestampMs) to := model.Time(req.EndTimestampMs) - return from, to, matchersSet, nil + return from, to, int(req.Limit), matchersSet, nil } // ToLabelValuesRequest builds a LabelValuesRequest proto -func ToLabelValuesRequest(labelName model.LabelName, from, to model.Time, matchers []*labels.Matcher) (*LabelValuesRequest, error) { +func ToLabelValuesRequest(labelName model.LabelName, from, to model.Time, limit int, matchers []*labels.Matcher) (*LabelValuesRequest, error) { ms, err := toLabelMatchers(matchers) if err != nil { return nil, err @@ -200,22 +201,23 @@ func ToLabelValuesRequest(labelName model.LabelName, from, to model.Time, matche StartTimestampMs: int64(from), EndTimestampMs: int64(to), Matchers: &LabelMatchers{Matchers: ms}, + Limit: int64(limit), }, nil } // FromLabelValuesRequest unpacks a LabelValuesRequest proto -func FromLabelValuesRequest(req *LabelValuesRequest) (string, int64, int64, []*labels.Matcher, error) { +func FromLabelValuesRequest(req *LabelValuesRequest) (string, int64, int64, int, []*labels.Matcher, error) { var err error var matchers []*labels.Matcher if req.Matchers != nil { matchers, err = FromLabelMatchers(req.Matchers.Matchers) if err != nil { - return "", 0, 0, nil, err + return "", 0, 0, 0, nil, err } } - return req.LabelName, req.StartTimestampMs, req.EndTimestampMs, matchers, nil + return req.LabelName, req.StartTimestampMs, req.EndTimestampMs, int(req.Limit), matchers, nil } func toLabelMatchers(matchers []*labels.Matcher) ([]*LabelMatcher, error) { diff --git a/pkg/ingester/client/ingester.pb.go b/pkg/ingester/client/ingester.pb.go index 643c9c6c2e..594292429f 100644 --- a/pkg/ingester/client/ingester.pb.go +++ b/pkg/ingester/client/ingester.pb.go @@ -402,6 +402,7 @@ type LabelValuesRequest struct { StartTimestampMs int64 `protobuf:"varint,2,opt,name=start_timestamp_ms,json=startTimestampMs,proto3" json:"start_timestamp_ms,omitempty"` EndTimestampMs int64 `protobuf:"varint,3,opt,name=end_timestamp_ms,json=endTimestampMs,proto3" json:"end_timestamp_ms,omitempty"` Matchers *LabelMatchers `protobuf:"bytes,4,opt,name=matchers,proto3" json:"matchers,omitempty"` + Limit int64 `protobuf:"varint,5,opt,name=limit,proto3" json:"limit,omitempty"` } func (m *LabelValuesRequest) Reset() { *m = LabelValuesRequest{} } @@ -464,6 +465,13 @@ func (m *LabelValuesRequest) GetMatchers() *LabelMatchers { return nil } +func (m *LabelValuesRequest) GetLimit() int64 { + if m != nil { + return m.Limit + } + return 0 +} + type LabelValuesResponse struct { LabelValues []string `protobuf:"bytes,1,rep,name=label_values,json=labelValues,proto3" json:"label_values,omitempty"` } @@ -553,6 +561,7 @@ func (m *LabelValuesStreamResponse) GetLabelValues() []string { type LabelNamesRequest struct { StartTimestampMs int64 `protobuf:"varint,1,opt,name=start_timestamp_ms,json=startTimestampMs,proto3" json:"start_timestamp_ms,omitempty"` EndTimestampMs int64 `protobuf:"varint,2,opt,name=end_timestamp_ms,json=endTimestampMs,proto3" json:"end_timestamp_ms,omitempty"` + Limit int64 `protobuf:"varint,3,opt,name=limit,proto3" json:"limit,omitempty"` } func (m *LabelNamesRequest) Reset() { *m = LabelNamesRequest{} } @@ -601,6 +610,13 @@ func (m *LabelNamesRequest) GetEndTimestampMs() int64 { return 0 } +func (m *LabelNamesRequest) GetLimit() int64 { + if m != nil { + return m.Limit + } + return 0 +} + type LabelNamesResponse struct { LabelNames []string `protobuf:"bytes,1,rep,name=label_names,json=labelNames,proto3" json:"label_names,omitempty"` } @@ -895,6 +911,7 @@ type MetricsForLabelMatchersRequest struct { StartTimestampMs int64 `protobuf:"varint,1,opt,name=start_timestamp_ms,json=startTimestampMs,proto3" json:"start_timestamp_ms,omitempty"` EndTimestampMs int64 `protobuf:"varint,2,opt,name=end_timestamp_ms,json=endTimestampMs,proto3" json:"end_timestamp_ms,omitempty"` MatchersSet []*LabelMatchers `protobuf:"bytes,3,rep,name=matchers_set,json=matchersSet,proto3" json:"matchers_set,omitempty"` + Limit int64 `protobuf:"varint,4,opt,name=limit,proto3" json:"limit,omitempty"` } func (m *MetricsForLabelMatchersRequest) Reset() { *m = MetricsForLabelMatchersRequest{} } @@ -950,6 +967,13 @@ func (m *MetricsForLabelMatchersRequest) GetMatchersSet() []*LabelMatchers { return nil } +func (m *MetricsForLabelMatchersRequest) GetLimit() int64 { + if m != nil { + return m.Limit + } + return 0 +} + type MetricsForLabelMatchersResponse struct { Metric []*cortexpb.Metric `protobuf:"bytes,1,rep,name=metric,proto3" json:"metric,omitempty"` } @@ -1444,88 +1468,89 @@ func init() { func init() { proto.RegisterFile("ingester.proto", fileDescriptor_60f6df4f3586b478) } var fileDescriptor_60f6df4f3586b478 = []byte{ - // 1292 bytes of a gzipped FileDescriptorProto + // 1309 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x57, 0x4b, 0x6f, 0xd4, 0xd6, - 0x17, 0xb7, 0x33, 0x93, 0x49, 0xe6, 0xcc, 0x64, 0x98, 0xdc, 0x04, 0x32, 0x98, 0x3f, 0x0e, 0xf8, - 0x2f, 0xda, 0xa8, 0x2d, 0x09, 0xa4, 0xad, 0x04, 0x7d, 0xa1, 0x04, 0x02, 0x04, 0x08, 0x01, 0x67, - 0xa0, 0x55, 0xd5, 0xca, 0x72, 0x66, 0x2e, 0x13, 0x17, 0xbf, 0xb0, 0xaf, 0x11, 0x74, 0x55, 0xa9, - 0x1f, 0xa0, 0x5d, 0x76, 0xdb, 0x5d, 0xd7, 0xfd, 0x00, 0x5d, 0xb3, 0x64, 0x89, 0xaa, 0x0a, 0x95, - 0x41, 0xaa, 0xba, 0xa4, 0xdf, 0xa0, 0xf2, 0x7d, 0x78, 0x6c, 0xc7, 0x93, 0x0c, 0x12, 0xe9, 0x6e, - 0x7c, 0xce, 0xef, 0x9c, 0xfb, 0x3b, 0x8f, 0x7b, 0xcf, 0x19, 0x68, 0x58, 0x6e, 0x0f, 0x87, 0x04, - 0x07, 0x8b, 0x7e, 0xe0, 0x11, 0x0f, 0x55, 0x3a, 0x5e, 0x40, 0xf0, 0x23, 0x65, 0xb6, 0xe7, 0xf5, - 0x3c, 0x2a, 0x5a, 0x8a, 0x7f, 0x31, 0xad, 0x72, 0xbe, 0x67, 0x91, 0x9d, 0x68, 0x7b, 0xb1, 0xe3, - 0x39, 0x4b, 0x0c, 0xe8, 0x07, 0xde, 0x37, 0xb8, 0x43, 0xf8, 0xd7, 0x92, 0x7f, 0xbf, 0x27, 0x14, - 0xdb, 0xfc, 0x07, 0x33, 0xd5, 0x3e, 0x85, 0x9a, 0x8e, 0xcd, 0xae, 0x8e, 0x1f, 0x44, 0x38, 0x24, - 0x68, 0x11, 0x26, 0x1e, 0x44, 0x38, 0xb0, 0x70, 0xd8, 0x92, 0x4f, 0x94, 0x16, 0x6a, 0xcb, 0xb3, - 0x8b, 0x1c, 0x7e, 0x3b, 0xc2, 0xc1, 0x63, 0x0e, 0xd3, 0x05, 0x48, 0xbb, 0x00, 0x75, 0x66, 0x1e, - 0xfa, 0x9e, 0x1b, 0x62, 0xb4, 0x04, 0x13, 0x01, 0x0e, 0x23, 0x9b, 0x08, 0xfb, 0xc3, 0x39, 0x7b, - 0x86, 0xd3, 0x05, 0x4a, 0xbb, 0x0e, 0x53, 0x19, 0x0d, 0xfa, 0x08, 0x80, 0x58, 0x0e, 0x0e, 0x8b, - 0x48, 0xf8, 0xdb, 0x8b, 0x6d, 0xcb, 0xc1, 0x5b, 0x54, 0xb7, 0x5a, 0x7e, 0xf2, 0x7c, 0x5e, 0xd2, - 0x53, 0x68, 0xed, 0x27, 0x19, 0xea, 0x69, 0x9e, 0xe8, 0x3d, 0x40, 0x21, 0x31, 0x03, 0x62, 0x50, - 0x10, 0x31, 0x1d, 0xdf, 0x70, 0x62, 0xa7, 0xf2, 0x42, 0x49, 0x6f, 0x52, 0x4d, 0x5b, 0x28, 0x36, - 0x42, 0xb4, 0x00, 0x4d, 0xec, 0x76, 0xb3, 0xd8, 0x31, 0x8a, 0x6d, 0x60, 0xb7, 0x9b, 0x46, 0x9e, - 0x81, 0x49, 0xc7, 0x24, 0x9d, 0x1d, 0x1c, 0x84, 0xad, 0x52, 0x36, 0x4f, 0x37, 0xcc, 0x6d, 0x6c, - 0x6f, 0x30, 0xa5, 0x9e, 0xa0, 0xb4, 0x9f, 0x65, 0x98, 0x5d, 0x7b, 0x84, 0x1d, 0xdf, 0x36, 0x83, - 0xff, 0x84, 0xe2, 0xd9, 0x5d, 0x14, 0x0f, 0x17, 0x51, 0x0c, 0x53, 0x1c, 0xbf, 0x82, 0x19, 0x4a, - 0x6d, 0x8b, 0x04, 0xd8, 0x74, 0x92, 0x8a, 0x5c, 0x80, 0x5a, 0x67, 0x27, 0x72, 0xef, 0x67, 0x4a, - 0x32, 0x27, 0x9c, 0x0d, 0x0a, 0x72, 0x31, 0x06, 0xf1, 0xaa, 0xa4, 0x2d, 0xae, 0x95, 0x27, 0xc7, - 0x9a, 0x25, 0x6d, 0x0b, 0x0e, 0xe7, 0x12, 0xf0, 0x06, 0x2a, 0xfe, 0x9b, 0x0c, 0x88, 0x86, 0x73, - 0xd7, 0xb4, 0x23, 0x1c, 0x8a, 0xa4, 0x1e, 0x07, 0xb0, 0x63, 0xa9, 0xe1, 0x9a, 0x0e, 0xa6, 0xc9, - 0xac, 0xea, 0x55, 0x2a, 0xb9, 0x69, 0x3a, 0x78, 0x48, 0xce, 0xc7, 0x5e, 0x23, 0xe7, 0xa5, 0x7d, - 0x73, 0x5e, 0x3e, 0x21, 0x8f, 0x92, 0xf3, 0x73, 0x30, 0x93, 0xe1, 0xcf, 0x73, 0x72, 0x12, 0xea, - 0x2c, 0x80, 0x87, 0x54, 0x4e, 0xb3, 0x52, 0xd5, 0x6b, 0xf6, 0x00, 0xaa, 0x7d, 0x06, 0x47, 0x53, - 0x96, 0xb9, 0x9a, 0x8d, 0x60, 0x7f, 0x1f, 0xa6, 0x6f, 0x88, 0x8c, 0x84, 0x07, 0xdc, 0x8d, 0xda, - 0x87, 0xbc, 0x4c, 0xfc, 0x30, 0xce, 0x72, 0x1e, 0x6a, 0x83, 0x32, 0x09, 0x92, 0x90, 0xd4, 0x29, - 0xd4, 0x3e, 0x86, 0xd6, 0xc0, 0x2c, 0x17, 0xe2, 0xbe, 0xc6, 0x08, 0x9a, 0x77, 0x42, 0x1c, 0x6c, - 0x11, 0x93, 0x88, 0xf8, 0xb4, 0x3f, 0x64, 0x98, 0x4e, 0x09, 0xb9, 0xab, 0x53, 0xe2, 0xbd, 0xb5, - 0x3c, 0xd7, 0x08, 0x4c, 0xc2, 0x5a, 0x46, 0xd6, 0xa7, 0x12, 0xa9, 0x6e, 0x12, 0x1c, 0x77, 0x95, - 0x1b, 0x39, 0x06, 0x6f, 0xd4, 0x38, 0xd0, 0xb2, 0x5e, 0x75, 0x23, 0x87, 0x75, 0x67, 0x9c, 0x3b, - 0xd3, 0xb7, 0x8c, 0x9c, 0xa7, 0x12, 0xf5, 0xd4, 0x34, 0x7d, 0x6b, 0x3d, 0xe3, 0x6c, 0x11, 0x66, - 0x82, 0xc8, 0xc6, 0x79, 0x78, 0x99, 0xc2, 0xa7, 0x63, 0x55, 0x16, 0xff, 0x7f, 0x98, 0x32, 0x3b, - 0xc4, 0x7a, 0x88, 0xc5, 0xf9, 0xe3, 0xf4, 0xfc, 0x3a, 0x13, 0x32, 0x0a, 0xda, 0xd7, 0x30, 0x13, - 0x47, 0xb7, 0x7e, 0x29, 0x1b, 0xdf, 0x1c, 0x4c, 0x44, 0x21, 0x0e, 0x0c, 0xab, 0xcb, 0xef, 0x42, - 0x25, 0xfe, 0x5c, 0xef, 0xa2, 0xd3, 0x50, 0xee, 0x9a, 0xc4, 0xa4, 0xb1, 0xd4, 0x96, 0x8f, 0x8a, - 0x66, 0xdd, 0x95, 0x21, 0x9d, 0xc2, 0xb4, 0x2b, 0x80, 0x62, 0x55, 0x98, 0xf5, 0x7e, 0x16, 0xc6, - 0xc3, 0x58, 0xc0, 0xaf, 0xee, 0xb1, 0xb4, 0x97, 0x1c, 0x13, 0x9d, 0x21, 0xb5, 0x5f, 0x65, 0x50, - 0x37, 0x30, 0x09, 0xac, 0x4e, 0x78, 0xd9, 0x0b, 0xb2, 0x77, 0xe3, 0x80, 0xdf, 0xc5, 0x73, 0x50, - 0x17, 0x97, 0xcf, 0x08, 0x31, 0xd9, 0xfb, 0x6d, 0xac, 0x09, 0xe8, 0x16, 0x26, 0xda, 0x75, 0x98, - 0x1f, 0xca, 0x99, 0xa7, 0x62, 0x01, 0x2a, 0x0e, 0x85, 0xf0, 0x5c, 0x34, 0x07, 0xcf, 0x18, 0x33, - 0xd5, 0xb9, 0x5e, 0xbb, 0x0d, 0xa7, 0x86, 0x38, 0xcb, 0xb5, 0xf9, 0xe8, 0x2e, 0x5b, 0x70, 0x84, - 0xbb, 0xdc, 0xc0, 0xc4, 0x8c, 0x0b, 0x26, 0xba, 0x7e, 0x13, 0xe6, 0x76, 0x69, 0xb8, 0xfb, 0x0f, - 0x60, 0xd2, 0xe1, 0x32, 0x7e, 0x40, 0x2b, 0x7f, 0x40, 0x62, 0x93, 0x20, 0xb5, 0x7f, 0x64, 0x38, - 0x94, 0x7b, 0xf8, 0xe3, 0x12, 0xdc, 0x0b, 0x3c, 0xc7, 0x10, 0x9b, 0xcb, 0xa0, 0xdb, 0x1a, 0xb1, - 0x7c, 0x9d, 0x8b, 0xd7, 0xbb, 0xe9, 0x76, 0x1c, 0xcb, 0xb4, 0xa3, 0x0b, 0x15, 0x7a, 0x7f, 0xc5, - 0xc4, 0x9a, 0x19, 0x50, 0xa1, 0x29, 0xba, 0x65, 0x5a, 0xc1, 0xea, 0x4a, 0x3c, 0x04, 0x7e, 0x7f, - 0x3e, 0xff, 0x5a, 0x4b, 0x0f, 0xb3, 0x5f, 0xe9, 0x9a, 0x3e, 0xc1, 0x81, 0xce, 0x4f, 0x41, 0xef, - 0x42, 0x85, 0xcd, 0xa9, 0x56, 0x99, 0x9e, 0x37, 0x25, 0xba, 0x20, 0x3d, 0xca, 0x38, 0x44, 0xfb, - 0x41, 0x86, 0x71, 0x16, 0xe9, 0x41, 0xb5, 0xa6, 0x02, 0x93, 0xd8, 0xed, 0x78, 0x5d, 0xcb, 0xed, - 0xd1, 0x67, 0x63, 0x5c, 0x4f, 0xbe, 0x11, 0xe2, 0x37, 0x35, 0x7e, 0x1f, 0xea, 0xfc, 0x3a, 0xae, - 0xc0, 0x54, 0xa6, 0x73, 0x32, 0x6b, 0x89, 0x3c, 0xd2, 0x5a, 0x62, 0x40, 0x3d, 0xad, 0x41, 0xa7, - 0xa0, 0x4c, 0x1e, 0xfb, 0xec, 0xfd, 0x6b, 0x2c, 0x4f, 0x0b, 0x6b, 0xaa, 0x6e, 0x3f, 0xf6, 0xb1, - 0x4e, 0xd5, 0x31, 0x1b, 0x3a, 0x59, 0x59, 0xf9, 0xe8, 0x6f, 0x34, 0x0b, 0xe3, 0x74, 0xd8, 0x50, - 0xea, 0x55, 0x9d, 0x7d, 0x68, 0xdf, 0xcb, 0xd0, 0x18, 0x74, 0xca, 0x65, 0xcb, 0xc6, 0x6f, 0xa2, - 0x51, 0x14, 0x98, 0xbc, 0x67, 0xd9, 0x98, 0x72, 0x60, 0xc7, 0x25, 0xdf, 0x45, 0x99, 0x7a, 0xe7, - 0x1a, 0x54, 0x93, 0x10, 0x50, 0x15, 0xc6, 0xd7, 0x6e, 0xdf, 0x59, 0xb9, 0xd1, 0x94, 0xd0, 0x14, - 0x54, 0x6f, 0x6e, 0xb6, 0x0d, 0xf6, 0x29, 0xa3, 0x43, 0x50, 0xd3, 0xd7, 0xae, 0xac, 0x7d, 0x61, - 0x6c, 0xac, 0xb4, 0x2f, 0x5e, 0x6d, 0x8e, 0x21, 0x04, 0x0d, 0x26, 0xb8, 0xb9, 0xc9, 0x65, 0xa5, - 0xe5, 0xbf, 0x26, 0x60, 0x52, 0x70, 0x44, 0xe7, 0xa1, 0x7c, 0x2b, 0x0a, 0x77, 0xd0, 0x91, 0x41, - 0xa7, 0x7e, 0x1e, 0x58, 0x04, 0xf3, 0x9b, 0xa7, 0xcc, 0xed, 0x92, 0xb3, 0x7b, 0xa7, 0x49, 0xe8, - 0x12, 0xd4, 0x52, 0xdb, 0x16, 0x2a, 0x5c, 0xb4, 0x95, 0x63, 0x19, 0x69, 0xf6, 0x69, 0xd0, 0xa4, - 0x33, 0x32, 0xda, 0x84, 0x06, 0x55, 0x89, 0xd5, 0x2a, 0x44, 0xff, 0x13, 0x26, 0x45, 0xeb, 0xa6, - 0x72, 0x7c, 0x88, 0x36, 0xa1, 0x75, 0x15, 0x6a, 0xa9, 0xb5, 0x02, 0x29, 0x99, 0x06, 0xca, 0x6c, - 0x59, 0x03, 0x72, 0x05, 0x1b, 0x8c, 0x26, 0xa1, 0xbb, 0x7c, 0xc1, 0x48, 0x2f, 0x28, 0x7b, 0xfa, - 0x3b, 0x59, 0xa0, 0x2b, 0x08, 0x79, 0x0d, 0x60, 0xb0, 0x14, 0xa0, 0xa3, 0x19, 0xa3, 0xf4, 0x32, - 0xa3, 0x28, 0x45, 0xaa, 0x84, 0xde, 0x16, 0x34, 0xf3, 0xbb, 0xc5, 0x5e, 0xce, 0x4e, 0xec, 0x56, - 0x15, 0x70, 0x5b, 0x85, 0x6a, 0x32, 0x3c, 0x51, 0xab, 0x60, 0x9e, 0x32, 0x67, 0xc3, 0x27, 0xad, - 0x26, 0xa1, 0xcb, 0x50, 0x5f, 0xb1, 0xed, 0x51, 0xdc, 0x28, 0x69, 0x4d, 0x98, 0xf7, 0x63, 0x27, - 0xaf, 0x7e, 0x7e, 0xc4, 0xa0, 0xb7, 0x92, 0x8b, 0xbd, 0xe7, 0x10, 0x56, 0xde, 0xde, 0x17, 0x97, - 0x9c, 0xf6, 0x2d, 0x1c, 0xdf, 0x73, 0xa0, 0x8d, 0x7c, 0xe6, 0xe9, 0x7d, 0x70, 0x05, 0x59, 0x6f, - 0xc3, 0xa1, 0xdc, 0x7c, 0x43, 0x6a, 0xce, 0x4b, 0x6e, 0x24, 0x2a, 0xf3, 0x43, 0xf5, 0xc2, 0xef, - 0xea, 0x27, 0x4f, 0x5f, 0xa8, 0xd2, 0xb3, 0x17, 0xaa, 0xf4, 0xea, 0x85, 0x2a, 0x7f, 0xd7, 0x57, - 0xe5, 0x5f, 0xfa, 0xaa, 0xfc, 0xa4, 0xaf, 0xca, 0x4f, 0xfb, 0xaa, 0xfc, 0x67, 0x5f, 0x95, 0xff, - 0xee, 0xab, 0xd2, 0xab, 0xbe, 0x2a, 0xff, 0xf8, 0x52, 0x95, 0x9e, 0xbe, 0x54, 0xa5, 0x67, 0x2f, - 0x55, 0xe9, 0xcb, 0x4a, 0xc7, 0xb6, 0xb0, 0x4b, 0xb6, 0x2b, 0xf4, 0xff, 0xf5, 0xfb, 0xff, 0x06, - 0x00, 0x00, 0xff, 0xff, 0xa7, 0xb7, 0xb7, 0x0a, 0xca, 0x0f, 0x00, 0x00, + 0x17, 0xb7, 0x33, 0x8f, 0x64, 0xce, 0x4c, 0x86, 0xc9, 0x4d, 0x20, 0x83, 0xf9, 0xe3, 0x80, 0xff, + 0xa2, 0x8d, 0xda, 0x92, 0x40, 0xda, 0x4a, 0xd0, 0x17, 0x4a, 0x20, 0x40, 0x80, 0x10, 0x70, 0x02, + 0xad, 0xaa, 0x56, 0x96, 0x33, 0x73, 0x49, 0x5c, 0xfc, 0xc2, 0xbe, 0x46, 0xd0, 0x55, 0xab, 0x7e, + 0x80, 0x76, 0xd9, 0x6d, 0x77, 0xfd, 0x28, 0x2c, 0x59, 0x74, 0x81, 0xaa, 0x0a, 0x95, 0x41, 0xaa, + 0xba, 0xa4, 0xdf, 0xa0, 0xf2, 0x7d, 0xf8, 0x15, 0x27, 0x19, 0x24, 0xd2, 0x9d, 0xef, 0x39, 0xbf, + 0x73, 0xee, 0x79, 0xde, 0x73, 0x0c, 0x6d, 0xcb, 0xdd, 0xc2, 0x21, 0xc1, 0xc1, 0x9c, 0x1f, 0x78, + 0xc4, 0x43, 0xf5, 0x9e, 0x17, 0x10, 0xfc, 0x48, 0x99, 0xda, 0xf2, 0xb6, 0x3c, 0x4a, 0x9a, 0x8f, + 0xbf, 0x18, 0x57, 0x39, 0xbf, 0x65, 0x91, 0xed, 0x68, 0x73, 0xae, 0xe7, 0x39, 0xf3, 0x0c, 0xe8, + 0x07, 0xde, 0x37, 0xb8, 0x47, 0xf8, 0x69, 0xde, 0xbf, 0xbf, 0x25, 0x18, 0x9b, 0xfc, 0x83, 0x89, + 0x6a, 0x9f, 0x42, 0x53, 0xc7, 0x66, 0x5f, 0xc7, 0x0f, 0x22, 0x1c, 0x12, 0x34, 0x07, 0xa3, 0x0f, + 0x22, 0x1c, 0x58, 0x38, 0xec, 0xca, 0x27, 0x2a, 0xb3, 0xcd, 0x85, 0xa9, 0x39, 0x0e, 0xbf, 0x1d, + 0xe1, 0xe0, 0x31, 0x87, 0xe9, 0x02, 0xa4, 0x5d, 0x80, 0x16, 0x13, 0x0f, 0x7d, 0xcf, 0x0d, 0x31, + 0x9a, 0x87, 0xd1, 0x00, 0x87, 0x91, 0x4d, 0x84, 0xfc, 0xe1, 0x82, 0x3c, 0xc3, 0xe9, 0x02, 0xa5, + 0x5d, 0x87, 0xf1, 0x1c, 0x07, 0x7d, 0x04, 0x40, 0x2c, 0x07, 0x87, 0x65, 0x46, 0xf8, 0x9b, 0x73, + 0x1b, 0x96, 0x83, 0xd7, 0x29, 0x6f, 0xa9, 0xfa, 0xe4, 0xf9, 0x8c, 0xa4, 0x67, 0xd0, 0xda, 0xcf, + 0x32, 0xb4, 0xb2, 0x76, 0xa2, 0xf7, 0x00, 0x85, 0xc4, 0x0c, 0x88, 0x41, 0x41, 0xc4, 0x74, 0x7c, + 0xc3, 0x89, 0x95, 0xca, 0xb3, 0x15, 0xbd, 0x43, 0x39, 0x1b, 0x82, 0xb1, 0x1a, 0xa2, 0x59, 0xe8, + 0x60, 0xb7, 0x9f, 0xc7, 0x8e, 0x50, 0x6c, 0x1b, 0xbb, 0xfd, 0x2c, 0xf2, 0x0c, 0x8c, 0x39, 0x26, + 0xe9, 0x6d, 0xe3, 0x20, 0xec, 0x56, 0xf2, 0x71, 0xba, 0x61, 0x6e, 0x62, 0x7b, 0x95, 0x31, 0xf5, + 0x04, 0xa5, 0xfd, 0x22, 0xc3, 0xd4, 0xf2, 0x23, 0xec, 0xf8, 0xb6, 0x19, 0xfc, 0x27, 0x26, 0x9e, + 0xdd, 0x61, 0xe2, 0xe1, 0x32, 0x13, 0xc3, 0x8c, 0x8d, 0x5f, 0xc1, 0x24, 0x35, 0x6d, 0x9d, 0x04, + 0xd8, 0x74, 0x92, 0x8c, 0x5c, 0x80, 0x66, 0x6f, 0x3b, 0x72, 0xef, 0xe7, 0x52, 0x32, 0x2d, 0x94, + 0xa5, 0x09, 0xb9, 0x18, 0x83, 0x78, 0x56, 0xb2, 0x12, 0xd7, 0xaa, 0x63, 0x23, 0x9d, 0x8a, 0xb6, + 0x0e, 0x87, 0x0b, 0x01, 0x78, 0x03, 0x19, 0xff, 0x4d, 0x06, 0x44, 0xdd, 0xb9, 0x6b, 0xda, 0x11, + 0x0e, 0x45, 0x50, 0x8f, 0x03, 0xd8, 0x31, 0xd5, 0x70, 0x4d, 0x07, 0xd3, 0x60, 0x36, 0xf4, 0x06, + 0xa5, 0xdc, 0x34, 0x1d, 0xbc, 0x4b, 0xcc, 0x47, 0x5e, 0x23, 0xe6, 0x95, 0x7d, 0x63, 0x5e, 0x3d, + 0x21, 0x0f, 0x11, 0x73, 0x34, 0x05, 0x35, 0xdb, 0x72, 0x2c, 0xd2, 0xad, 0x51, 0x8d, 0xec, 0xa0, + 0x9d, 0x83, 0xc9, 0x9c, 0x57, 0x3c, 0x52, 0x27, 0xa1, 0xc5, 0xdc, 0x7a, 0x48, 0xe9, 0x34, 0x56, + 0x0d, 0xbd, 0x69, 0xa7, 0x50, 0xed, 0x33, 0x38, 0x9a, 0x91, 0x2c, 0x64, 0x72, 0x08, 0xf9, 0xef, + 0x65, 0x98, 0xb8, 0x21, 0x02, 0x15, 0x1e, 0x74, 0x91, 0x26, 0xde, 0x57, 0xb2, 0xde, 0x7f, 0xc8, + 0x73, 0xca, 0x4d, 0xe0, 0xc6, 0xcf, 0x40, 0x33, 0xcd, 0xa9, 0xb0, 0x1d, 0x92, 0xa4, 0x86, 0xda, + 0xc7, 0xd0, 0x4d, 0xc5, 0x0a, 0x9e, 0xef, 0x2b, 0x8c, 0xa0, 0x73, 0x27, 0xc4, 0xc1, 0x3a, 0x31, + 0x89, 0xf0, 0x5a, 0xfb, 0x43, 0x86, 0x89, 0x0c, 0x91, 0xab, 0x3a, 0x25, 0x1e, 0x67, 0xcb, 0x73, + 0x8d, 0xc0, 0x24, 0xac, 0xbe, 0x64, 0x7d, 0x3c, 0xa1, 0xea, 0x26, 0xc1, 0x71, 0x09, 0xba, 0x91, + 0x63, 0xf0, 0xaa, 0x8e, 0xdd, 0xaf, 0xea, 0x0d, 0x37, 0x72, 0x58, 0x29, 0xc7, 0x11, 0x35, 0x7d, + 0xcb, 0x28, 0x68, 0xaa, 0x50, 0x4d, 0x1d, 0xd3, 0xb7, 0x56, 0x72, 0xca, 0xe6, 0x60, 0x32, 0x88, + 0x6c, 0x5c, 0x84, 0x57, 0x29, 0x7c, 0x22, 0x66, 0xe5, 0xf1, 0xff, 0x87, 0x71, 0xb3, 0x47, 0xac, + 0x87, 0x58, 0xdc, 0x5f, 0xa3, 0xf7, 0xb7, 0x18, 0x91, 0x99, 0xa0, 0x7d, 0x0d, 0x93, 0xb1, 0x77, + 0x2b, 0x97, 0xf2, 0xfe, 0x4d, 0xc3, 0x68, 0x14, 0xe2, 0xc0, 0xb0, 0xfa, 0xbc, 0x71, 0xea, 0xf1, + 0x71, 0xa5, 0x8f, 0x4e, 0x43, 0xb5, 0x6f, 0x12, 0x93, 0xfa, 0xd2, 0x5c, 0x38, 0x2a, 0x2a, 0x7b, + 0x47, 0x84, 0x74, 0x0a, 0xd3, 0xae, 0x00, 0x8a, 0x59, 0x61, 0x5e, 0xfb, 0x59, 0xa8, 0x85, 0x31, + 0x81, 0xf7, 0xf9, 0xb1, 0xac, 0x96, 0x82, 0x25, 0x3a, 0x43, 0x6a, 0x4f, 0x64, 0x50, 0x57, 0x31, + 0x09, 0xac, 0x5e, 0x78, 0xd9, 0x0b, 0xf2, 0x8d, 0x74, 0xc0, 0xf5, 0x79, 0x0e, 0x5a, 0xa2, 0x53, + 0x8d, 0x10, 0x93, 0xbd, 0x1f, 0xd2, 0xa6, 0x80, 0xae, 0x63, 0x92, 0x56, 0x76, 0x35, 0x5b, 0xd9, + 0xd7, 0x61, 0x66, 0x57, 0x4f, 0x78, 0x80, 0x66, 0xa1, 0xee, 0x50, 0x08, 0x8f, 0x50, 0x27, 0x7d, + 0x09, 0x99, 0xa8, 0xce, 0xf9, 0xda, 0x6d, 0x38, 0xb5, 0x8b, 0xb2, 0x42, 0xf1, 0x0f, 0xaf, 0xb2, + 0x0b, 0x47, 0xb8, 0xca, 0x55, 0x4c, 0xcc, 0x38, 0x8d, 0xa2, 0x17, 0xd6, 0x60, 0x7a, 0x07, 0x87, + 0xab, 0xff, 0x00, 0xc6, 0x1c, 0x4e, 0xe3, 0x17, 0x74, 0x8b, 0x17, 0x24, 0x32, 0x09, 0x52, 0xfb, + 0x47, 0x86, 0x43, 0x85, 0xd9, 0x11, 0x27, 0xe6, 0x5e, 0xe0, 0x39, 0x86, 0x58, 0x7e, 0xd2, 0x1a, + 0x6c, 0xc7, 0xf4, 0x15, 0x4e, 0x5e, 0xe9, 0x67, 0x8b, 0x74, 0x24, 0x57, 0xa4, 0x2e, 0xd4, 0x69, + 0x57, 0x8b, 0xa1, 0x37, 0x99, 0x9a, 0x42, 0x43, 0x74, 0xcb, 0xb4, 0x82, 0xa5, 0xc5, 0x78, 0x8e, + 0xfc, 0xfe, 0x7c, 0xe6, 0xb5, 0xf6, 0x26, 0x26, 0xbf, 0xd8, 0x37, 0x7d, 0x82, 0x03, 0x9d, 0xdf, + 0x82, 0xde, 0x85, 0x3a, 0x1b, 0x75, 0xdd, 0x2a, 0xbd, 0x6f, 0x5c, 0xd4, 0x46, 0x76, 0x1a, 0x72, + 0x88, 0xf6, 0xa3, 0x0c, 0x35, 0xe6, 0xe9, 0x41, 0x15, 0xac, 0x02, 0x63, 0xd8, 0xed, 0x79, 0x7d, + 0xcb, 0xdd, 0xa2, 0x8f, 0x49, 0x4d, 0x4f, 0xce, 0x08, 0xf1, 0xfe, 0x8d, 0x2b, 0xb2, 0xc5, 0x9b, + 0x74, 0x11, 0xc6, 0x73, 0x95, 0x93, 0xdb, 0x6c, 0xe4, 0xa1, 0x36, 0x1b, 0x03, 0x5a, 0x59, 0x0e, + 0x3a, 0x05, 0x55, 0xf2, 0xd8, 0x67, 0xaf, 0x62, 0x7b, 0x61, 0x42, 0x48, 0x53, 0xf6, 0xc6, 0x63, + 0x1f, 0xeb, 0x94, 0x1d, 0x5b, 0x43, 0x87, 0x33, 0x4b, 0x1f, 0xfd, 0x8e, 0x9b, 0x86, 0x4e, 0x26, + 0x6a, 0x7a, 0x43, 0x67, 0x07, 0xed, 0x07, 0x19, 0xda, 0x69, 0xa5, 0x5c, 0xb6, 0x6c, 0xfc, 0x26, + 0x0a, 0x45, 0x81, 0xb1, 0x7b, 0x96, 0x8d, 0xa9, 0x0d, 0xec, 0xba, 0xe4, 0x5c, 0x16, 0xa9, 0x77, + 0xae, 0x41, 0x23, 0x71, 0x01, 0x35, 0xa0, 0xb6, 0x7c, 0xfb, 0xce, 0xe2, 0x8d, 0x8e, 0x84, 0xc6, + 0xa1, 0x71, 0x73, 0x6d, 0xc3, 0x60, 0x47, 0x19, 0x1d, 0x82, 0xa6, 0xbe, 0x7c, 0x65, 0xf9, 0x0b, + 0x63, 0x75, 0x71, 0xe3, 0xe2, 0xd5, 0xce, 0x08, 0x42, 0xd0, 0x66, 0x84, 0x9b, 0x6b, 0x9c, 0x56, + 0x59, 0xf8, 0x6b, 0x14, 0xc6, 0x84, 0x8d, 0xe8, 0x3c, 0x54, 0x6f, 0x45, 0xe1, 0x36, 0x3a, 0x92, + 0x56, 0xea, 0xe7, 0x81, 0x45, 0x30, 0xef, 0x3c, 0x65, 0x7a, 0x07, 0x9d, 0xf5, 0x9d, 0x26, 0xa1, + 0x4b, 0xd0, 0xcc, 0x2c, 0x6c, 0xa8, 0x74, 0x57, 0x57, 0x8e, 0xe5, 0xa8, 0xf9, 0xa7, 0x41, 0x93, + 0xce, 0xc8, 0x68, 0x0d, 0xda, 0x94, 0x25, 0xb6, 0xb3, 0x10, 0xfd, 0x4f, 0x88, 0x94, 0x6d, 0xac, + 0xca, 0xf1, 0x5d, 0xb8, 0x89, 0x59, 0x57, 0xa1, 0x99, 0xd9, 0x41, 0x90, 0x92, 0x2b, 0xa0, 0xdc, + 0xa2, 0x96, 0x1a, 0x57, 0xb2, 0xee, 0x68, 0x12, 0xba, 0xcb, 0x97, 0x91, 0xec, 0x36, 0xb3, 0xa7, + 0xbe, 0x93, 0x25, 0xbc, 0x12, 0x97, 0x97, 0x01, 0xd2, 0x55, 0x01, 0x1d, 0xcd, 0x09, 0x65, 0x17, + 0x1f, 0x45, 0x29, 0x63, 0x25, 0xe6, 0xad, 0x43, 0xa7, 0xb8, 0x71, 0xec, 0xa5, 0xec, 0xc4, 0x4e, + 0x56, 0x89, 0x6d, 0x4b, 0xd0, 0x48, 0x46, 0x2a, 0xea, 0x96, 0x4c, 0x59, 0xa6, 0x6c, 0xf7, 0xf9, + 0xab, 0x49, 0xe8, 0x32, 0xb4, 0x16, 0x6d, 0x7b, 0x18, 0x35, 0x4a, 0x96, 0x13, 0x16, 0xf5, 0xd8, + 0xc9, 0xab, 0x5f, 0x1c, 0x31, 0xe8, 0xad, 0xa4, 0xb1, 0xf7, 0x1c, 0xcd, 0xca, 0xdb, 0xfb, 0xe2, + 0x92, 0xdb, 0xbe, 0x85, 0xe3, 0x7b, 0x0e, 0xb4, 0xa1, 0xef, 0x3c, 0xbd, 0x0f, 0xae, 0x24, 0xea, + 0x1b, 0x70, 0xa8, 0x30, 0xdf, 0x90, 0x5a, 0xd0, 0x52, 0x18, 0x89, 0xca, 0xcc, 0xae, 0x7c, 0xa1, + 0x77, 0xe9, 0x93, 0xa7, 0x2f, 0x54, 0xe9, 0xd9, 0x0b, 0x55, 0x7a, 0xf5, 0x42, 0x95, 0xbf, 0x1b, + 0xa8, 0xf2, 0xaf, 0x03, 0x55, 0x7e, 0x32, 0x50, 0xe5, 0xa7, 0x03, 0x55, 0xfe, 0x73, 0xa0, 0xca, + 0x7f, 0x0f, 0x54, 0xe9, 0xd5, 0x40, 0x95, 0x7f, 0x7a, 0xa9, 0x4a, 0x4f, 0x5f, 0xaa, 0xd2, 0xb3, + 0x97, 0xaa, 0xf4, 0x65, 0xbd, 0x67, 0x5b, 0xd8, 0x25, 0x9b, 0x75, 0xfa, 0x8b, 0xfe, 0xfe, 0xbf, + 0x01, 0x00, 0x00, 0xff, 0xff, 0xdd, 0xd4, 0xb0, 0x94, 0x0d, 0x10, 0x00, 0x00, } func (x MatchType) String() string { @@ -1781,6 +1806,9 @@ func (this *LabelValuesRequest) Equal(that interface{}) bool { if !this.Matchers.Equal(that1.Matchers) { return false } + if this.Limit != that1.Limit { + return false + } return true } func (this *LabelValuesResponse) Equal(that interface{}) bool { @@ -1866,6 +1894,9 @@ func (this *LabelNamesRequest) Equal(that interface{}) bool { if this.EndTimestampMs != that1.EndTimestampMs { return false } + if this.Limit != that1.Limit { + return false + } return true } func (this *LabelNamesResponse) Equal(that interface{}) bool { @@ -2072,6 +2103,9 @@ func (this *MetricsForLabelMatchersRequest) Equal(that interface{}) bool { return false } } + if this.Limit != that1.Limit { + return false + } return true } func (this *MetricsForLabelMatchersResponse) Equal(that interface{}) bool { @@ -2454,7 +2488,7 @@ func (this *LabelValuesRequest) GoString() string { if this == nil { return "nil" } - s := make([]string, 0, 8) + s := make([]string, 0, 9) s = append(s, "&client.LabelValuesRequest{") s = append(s, "LabelName: "+fmt.Sprintf("%#v", this.LabelName)+",\n") s = append(s, "StartTimestampMs: "+fmt.Sprintf("%#v", this.StartTimestampMs)+",\n") @@ -2462,6 +2496,7 @@ func (this *LabelValuesRequest) GoString() string { if this.Matchers != nil { s = append(s, "Matchers: "+fmt.Sprintf("%#v", this.Matchers)+",\n") } + s = append(s, "Limit: "+fmt.Sprintf("%#v", this.Limit)+",\n") s = append(s, "}") return strings.Join(s, "") } @@ -2489,10 +2524,11 @@ func (this *LabelNamesRequest) GoString() string { if this == nil { return "nil" } - s := make([]string, 0, 6) + s := make([]string, 0, 7) s = append(s, "&client.LabelNamesRequest{") s = append(s, "StartTimestampMs: "+fmt.Sprintf("%#v", this.StartTimestampMs)+",\n") s = append(s, "EndTimestampMs: "+fmt.Sprintf("%#v", this.EndTimestampMs)+",\n") + s = append(s, "Limit: "+fmt.Sprintf("%#v", this.Limit)+",\n") s = append(s, "}") return strings.Join(s, "") } @@ -2568,13 +2604,14 @@ func (this *MetricsForLabelMatchersRequest) GoString() string { if this == nil { return "nil" } - s := make([]string, 0, 7) + s := make([]string, 0, 8) s = append(s, "&client.MetricsForLabelMatchersRequest{") s = append(s, "StartTimestampMs: "+fmt.Sprintf("%#v", this.StartTimestampMs)+",\n") s = append(s, "EndTimestampMs: "+fmt.Sprintf("%#v", this.EndTimestampMs)+",\n") if this.MatchersSet != nil { s = append(s, "MatchersSet: "+fmt.Sprintf("%#v", this.MatchersSet)+",\n") } + s = append(s, "Limit: "+fmt.Sprintf("%#v", this.Limit)+",\n") s = append(s, "}") return strings.Join(s, "") } @@ -3585,6 +3622,11 @@ func (m *LabelValuesRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if m.Limit != 0 { + i = encodeVarintIngester(dAtA, i, uint64(m.Limit)) + i-- + dAtA[i] = 0x28 + } if m.Matchers != nil { { size, err := m.Matchers.MarshalToSizedBuffer(dAtA[:i]) @@ -3701,6 +3743,11 @@ func (m *LabelNamesRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if m.Limit != 0 { + i = encodeVarintIngester(dAtA, i, uint64(m.Limit)) + i-- + dAtA[i] = 0x18 + } if m.EndTimestampMs != 0 { i = encodeVarintIngester(dAtA, i, uint64(m.EndTimestampMs)) i-- @@ -3951,6 +3998,11 @@ func (m *MetricsForLabelMatchersRequest) MarshalToSizedBuffer(dAtA []byte) (int, _ = i var l int _ = l + if m.Limit != 0 { + i = encodeVarintIngester(dAtA, i, uint64(m.Limit)) + i-- + dAtA[i] = 0x20 + } if len(m.MatchersSet) > 0 { for iNdEx := len(m.MatchersSet) - 1; iNdEx >= 0; iNdEx-- { { @@ -4500,6 +4552,9 @@ func (m *LabelValuesRequest) Size() (n int) { l = m.Matchers.Size() n += 1 + l + sovIngester(uint64(l)) } + if m.Limit != 0 { + n += 1 + sovIngester(uint64(m.Limit)) + } return n } @@ -4545,6 +4600,9 @@ func (m *LabelNamesRequest) Size() (n int) { if m.EndTimestampMs != 0 { n += 1 + sovIngester(uint64(m.EndTimestampMs)) } + if m.Limit != 0 { + n += 1 + sovIngester(uint64(m.Limit)) + } return n } @@ -4661,6 +4719,9 @@ func (m *MetricsForLabelMatchersRequest) Size() (n int) { n += 1 + l + sovIngester(uint64(l)) } } + if m.Limit != 0 { + n += 1 + sovIngester(uint64(m.Limit)) + } return n } @@ -4953,6 +5014,7 @@ func (this *LabelValuesRequest) String() string { `StartTimestampMs:` + fmt.Sprintf("%v", this.StartTimestampMs) + `,`, `EndTimestampMs:` + fmt.Sprintf("%v", this.EndTimestampMs) + `,`, `Matchers:` + strings.Replace(this.Matchers.String(), "LabelMatchers", "LabelMatchers", 1) + `,`, + `Limit:` + fmt.Sprintf("%v", this.Limit) + `,`, `}`, }, "") return s @@ -4984,6 +5046,7 @@ func (this *LabelNamesRequest) String() string { s := strings.Join([]string{`&LabelNamesRequest{`, `StartTimestampMs:` + fmt.Sprintf("%v", this.StartTimestampMs) + `,`, `EndTimestampMs:` + fmt.Sprintf("%v", this.EndTimestampMs) + `,`, + `Limit:` + fmt.Sprintf("%v", this.Limit) + `,`, `}`, }, "") return s @@ -5070,6 +5133,7 @@ func (this *MetricsForLabelMatchersRequest) String() string { `StartTimestampMs:` + fmt.Sprintf("%v", this.StartTimestampMs) + `,`, `EndTimestampMs:` + fmt.Sprintf("%v", this.EndTimestampMs) + `,`, `MatchersSet:` + repeatedStringForMatchersSet + `,`, + `Limit:` + fmt.Sprintf("%v", this.Limit) + `,`, `}`, }, "") return s @@ -6027,6 +6091,25 @@ func (m *LabelValuesRequest) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Limit", wireType) + } + m.Limit = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowIngester + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Limit |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } default: iNdEx = preIndex skippy, err := skipIngester(dAtA[iNdEx:]) @@ -6288,6 +6371,25 @@ func (m *LabelNamesRequest) Unmarshal(dAtA []byte) error { break } } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Limit", wireType) + } + m.Limit = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowIngester + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Limit |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } default: iNdEx = preIndex skippy, err := skipIngester(dAtA[iNdEx:]) @@ -6968,6 +7070,25 @@ func (m *MetricsForLabelMatchersRequest) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Limit", wireType) + } + m.Limit = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowIngester + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Limit |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } default: iNdEx = preIndex skippy, err := skipIngester(dAtA[iNdEx:]) diff --git a/pkg/ingester/client/ingester.proto b/pkg/ingester/client/ingester.proto index 3965808d6a..e9db9685c1 100644 --- a/pkg/ingester/client/ingester.proto +++ b/pkg/ingester/client/ingester.proto @@ -67,6 +67,7 @@ message LabelValuesRequest { int64 start_timestamp_ms = 2; int64 end_timestamp_ms = 3; LabelMatchers matchers = 4; + int64 limit = 5; } message LabelValuesResponse { @@ -80,6 +81,7 @@ message LabelValuesStreamResponse { message LabelNamesRequest { int64 start_timestamp_ms = 1; int64 end_timestamp_ms = 2; + int64 limit = 3; } message LabelNamesResponse { @@ -113,6 +115,7 @@ message MetricsForLabelMatchersRequest { int64 start_timestamp_ms = 1; int64 end_timestamp_ms = 2; repeated LabelMatchers matchers_set = 3; + int64 limit = 4; } message MetricsForLabelMatchersResponse { diff --git a/pkg/ingester/ingester.go b/pkg/ingester/ingester.go index e6cf5d9701..e42180bd54 100644 --- a/pkg/ingester/ingester.go +++ b/pkg/ingester/ingester.go @@ -1500,7 +1500,7 @@ func (i *Ingester) labelsValuesCommon(ctx context.Context, req *client.LabelValu return nil, cleanup, err } - labelName, startTimestampMs, endTimestampMs, matchers, err := client.FromLabelValuesRequest(req) + labelName, startTimestampMs, endTimestampMs, limit, matchers, err := client.FromLabelValuesRequest(req) if err != nil { return nil, cleanup, err } @@ -1534,11 +1534,15 @@ func (i *Ingester) labelsValuesCommon(ctx context.Context, req *client.LabelValu return nil, cleanup, err } defer c() - vals, _, err := q.LabelValues(ctx, labelName, nil, matchers...) + vals, _, err := q.LabelValues(ctx, labelName, &storage.LabelHints{Limit: limit}, matchers...) if err != nil { return nil, cleanup, err } + if limit > 0 && len(vals) > limit { + vals = vals[:limit] + } + return &client.LabelValuesResponse{ LabelValues: vals, }, cleanup, nil @@ -1601,6 +1605,8 @@ func (i *Ingester) labelNamesCommon(ctx context.Context, req *client.LabelNamesR return nil, cleanup, err } + limit := int(req.Limit) + q, err := db.Querier(mint, maxt) if err != nil { return nil, cleanup, err @@ -1615,11 +1621,15 @@ func (i *Ingester) labelNamesCommon(ctx context.Context, req *client.LabelNamesR return nil, cleanup, err } defer c() - names, _, err := q.LabelNames(ctx, nil) + names, _, err := q.LabelNames(ctx, &storage.LabelHints{Limit: limit}) if err != nil { return nil, cleanup, err } + if limit > 0 && len(names) > limit { + names = names[:limit] + } + return &client.LabelNamesResponse{ LabelNames: names, }, cleanup, nil @@ -1676,7 +1686,7 @@ func (i *Ingester) metricsForLabelMatchersCommon(ctx context.Context, req *clien } // Parse the request - _, _, matchersSet, err := client.FromMetricsForLabelMatchersRequest(req) + _, _, limit, matchersSet, err := client.FromMetricsForLabelMatchersRequest(req) if err != nil { return nil, cleanup, err } @@ -1705,6 +1715,7 @@ func (i *Ingester) metricsForLabelMatchersCommon(ctx context.Context, req *clien Start: mint, End: maxt, Func: "series", // There is no series function, this token is used for lookups that don't need samples. + Limit: limit, } if len(matchersSet) > 1 { for _, matchers := range matchersSet { @@ -1735,6 +1746,9 @@ func (i *Ingester) metricsForLabelMatchersCommon(ctx context.Context, req *clien result.Metric = append(result.Metric, &cortexpb.Metric{ Labels: cortexpb.FromLabelsToLabelAdapters(mergedSet.At().Labels()), }) + if limit > 0 && len(result.Metric) >= limit { + break + } } return result, cleanup, nil diff --git a/pkg/ingester/ingester_test.go b/pkg/ingester/ingester_test.go index 436757486a..bdfe6dcd3d 100644 --- a/pkg/ingester/ingester_test.go +++ b/pkg/ingester/ingester_test.go @@ -2204,7 +2204,7 @@ func Test_Ingester_LabelNames(t *testing.T) { {labels.Labels{{Name: labels.MetricName, Value: "test_2"}}, 2, 200000}, } - expected := []string{"__name__", "status", "route"} + expected := []string{"__name__", "route", "status"} // Create ingester i, err := prepareIngesterWithBlocksStorage(t, defaultIngesterTestConfig(t), prometheus.NewRegistry()) @@ -2226,10 +2226,27 @@ func Test_Ingester_LabelNames(t *testing.T) { require.NoError(t, err) } - // Get label names - res, err := i.LabelNames(ctx, &client.LabelNamesRequest{}) - require.NoError(t, err) - assert.ElementsMatch(t, expected, res.LabelNames) + tests := map[string]struct { + limit int + expected []string + }{ + "should return all label names if no limit is set": { + expected: expected, + }, + "should return limited label names if a limit is set": { + limit: 2, + expected: expected[:2], + }, + } + + for testName, testData := range tests { + t.Run(testName, func(t *testing.T) { + // Get label names + res, err := i.LabelNames(ctx, &client.LabelNamesRequest{Limit: int64(testData.limit)}) + require.NoError(t, err) + assert.ElementsMatch(t, testData.expected, res.LabelNames) + }) + } } func Test_Ingester_LabelValues(t *testing.T) { @@ -2270,13 +2287,31 @@ func Test_Ingester_LabelValues(t *testing.T) { require.NoError(t, err) } - // Get label values - for labelName, expectedValues := range expected { - req := &client.LabelValuesRequest{LabelName: labelName} - res, err := i.LabelValues(ctx, req) - require.NoError(t, err) - assert.ElementsMatch(t, expectedValues, res.LabelValues) + tests := map[string]struct { + limit int64 + }{ + "should return all label values if no limit is set": { + limit: 0, + }, + "should return limited label values if a limit is set": { + limit: 1, + }, } + + for testName, testData := range tests { + t.Run(testName, func(t *testing.T) { + for labelName, expectedValues := range expected { + req := &client.LabelValuesRequest{LabelName: labelName, Limit: testData.limit} + res, err := i.LabelValues(ctx, req) + require.NoError(t, err) + if testData.limit > 0 && len(expectedValues) > int(testData.limit) { + expectedValues = expectedValues[:testData.limit] + } + assert.ElementsMatch(t, expectedValues, res.LabelValues) + } + }) + } + } func Test_Ingester_LabelValue_MaxInflightQueryRequest(t *testing.T) { @@ -2635,6 +2670,7 @@ func Test_Ingester_MetricsForLabelMatchers(t *testing.T) { tests := map[string]struct { from int64 to int64 + limit int64 matchers []*client.LabelMatchers expected []*cortexpb.Metric queryIngestersWithin time.Duration @@ -2742,6 +2778,26 @@ func Test_Ingester_MetricsForLabelMatchers(t *testing.T) { {Labels: cortexpb.FromLabelsToLabelAdapters(fixtures[4].lbls)}, }, }, + "should return only limited results": { + from: math.MinInt64, + to: math.MaxInt64, + limit: 1, + matchers: []*client.LabelMatchers{ + { + Matchers: []*client.LabelMatcher{ + {Type: client.EQUAL, Name: "status", Value: "200"}, + }, + }, + { + Matchers: []*client.LabelMatcher{ + {Type: client.EQUAL, Name: model.MetricNameLabel, Value: "test_2"}, + }, + }, + }, + expected: []*cortexpb.Metric{ + {Labels: cortexpb.FromLabelsToLabelAdapters(fixtures[0].lbls)}, + }, + }, } // Create ingester @@ -2773,6 +2829,7 @@ func Test_Ingester_MetricsForLabelMatchers(t *testing.T) { StartTimestampMs: testData.from, EndTimestampMs: testData.to, MatchersSet: testData.matchers, + Limit: testData.limit, } i.cfg.QueryIngestersWithin = testData.queryIngestersWithin res, err := i.MetricsForLabelMatchers(ctx, req) diff --git a/pkg/querier/distributor_queryable.go b/pkg/querier/distributor_queryable.go index 5bd933c209..a2f5a50f46 100644 --- a/pkg/querier/distributor_queryable.go +++ b/pkg/querier/distributor_queryable.go @@ -27,12 +27,12 @@ import ( type Distributor interface { QueryStream(ctx context.Context, from, to model.Time, matchers ...*labels.Matcher) (*client.QueryStreamResponse, error) QueryExemplars(ctx context.Context, from, to model.Time, matchers ...[]*labels.Matcher) (*client.ExemplarQueryResponse, error) - LabelValuesForLabelName(ctx context.Context, from, to model.Time, label model.LabelName, matchers ...*labels.Matcher) ([]string, error) - LabelValuesForLabelNameStream(ctx context.Context, from, to model.Time, label model.LabelName, matchers ...*labels.Matcher) ([]string, error) - LabelNames(context.Context, model.Time, model.Time) ([]string, error) - LabelNamesStream(context.Context, model.Time, model.Time) ([]string, error) - MetricsForLabelMatchers(ctx context.Context, from, through model.Time, matchers ...*labels.Matcher) ([]model.Metric, error) - MetricsForLabelMatchersStream(ctx context.Context, from, through model.Time, matchers ...*labels.Matcher) ([]model.Metric, error) + LabelValuesForLabelName(ctx context.Context, from, to model.Time, label model.LabelName, hint *storage.LabelHints, matchers ...*labels.Matcher) ([]string, error) + LabelValuesForLabelNameStream(ctx context.Context, from, to model.Time, label model.LabelName, hint *storage.LabelHints, matchers ...*labels.Matcher) ([]string, error) + LabelNames(context.Context, model.Time, model.Time, *storage.LabelHints) ([]string, error) + LabelNamesStream(context.Context, model.Time, model.Time, *storage.LabelHints) ([]string, error) + MetricsForLabelMatchers(ctx context.Context, from, through model.Time, hint *storage.SelectHints, matchers ...*labels.Matcher) ([]model.Metric, error) + MetricsForLabelMatchersStream(ctx context.Context, from, through model.Time, hint *storage.SelectHints, matchers ...*labels.Matcher) ([]model.Metric, error) MetricsMetadata(ctx context.Context) ([]scrape.MetricMetadata, error) } @@ -115,9 +115,9 @@ func (q *distributorQuerier) Select(ctx context.Context, sortSeries bool, sp *st ) if q.streamingMetadata { - ms, err = q.distributor.MetricsForLabelMatchersStream(ctx, model.Time(minT), model.Time(maxT), matchers...) + ms, err = q.distributor.MetricsForLabelMatchersStream(ctx, model.Time(minT), model.Time(maxT), sp, matchers...) } else { - ms, err = q.distributor.MetricsForLabelMatchers(ctx, model.Time(minT), model.Time(maxT), matchers...) + ms, err = q.distributor.MetricsForLabelMatchers(ctx, model.Time(minT), model.Time(maxT), sp, matchers...) } if err != nil { @@ -171,9 +171,9 @@ func (q *distributorQuerier) LabelValues(ctx context.Context, name string, hints ) if q.streamingMetadata { - lvs, err = q.distributor.LabelValuesForLabelNameStream(ctx, model.Time(q.mint), model.Time(q.maxt), model.LabelName(name), matchers...) + lvs, err = q.distributor.LabelValuesForLabelNameStream(ctx, model.Time(q.mint), model.Time(q.maxt), model.LabelName(name), hints, matchers...) } else { - lvs, err = q.distributor.LabelValuesForLabelName(ctx, model.Time(q.mint), model.Time(q.maxt), model.LabelName(name), matchers...) + lvs, err = q.distributor.LabelValuesForLabelName(ctx, model.Time(q.mint), model.Time(q.maxt), model.LabelName(name), hints, matchers...) } return lvs, nil, err @@ -181,7 +181,7 @@ func (q *distributorQuerier) LabelValues(ctx context.Context, name string, hints func (q *distributorQuerier) LabelNames(ctx context.Context, hints *storage.LabelHints, matchers ...*labels.Matcher) ([]string, annotations.Annotations, error) { if len(matchers) > 0 { - return q.labelNamesWithMatchers(ctx, matchers...) + return q.labelNamesWithMatchers(ctx, hints, matchers...) } log, ctx := spanlogger.New(ctx, "distributorQuerier.LabelNames") @@ -193,16 +193,16 @@ func (q *distributorQuerier) LabelNames(ctx context.Context, hints *storage.Labe ) if q.streamingMetadata { - ln, err = q.distributor.LabelNamesStream(ctx, model.Time(q.mint), model.Time(q.maxt)) + ln, err = q.distributor.LabelNamesStream(ctx, model.Time(q.mint), model.Time(q.maxt), hints) } else { - ln, err = q.distributor.LabelNames(ctx, model.Time(q.mint), model.Time(q.maxt)) + ln, err = q.distributor.LabelNames(ctx, model.Time(q.mint), model.Time(q.maxt), hints) } return ln, nil, err } // labelNamesWithMatchers performs the LabelNames call by calling ingester's MetricsForLabelMatchers method -func (q *distributorQuerier) labelNamesWithMatchers(ctx context.Context, matchers ...*labels.Matcher) ([]string, annotations.Annotations, error) { +func (q *distributorQuerier) labelNamesWithMatchers(ctx context.Context, hints *storage.LabelHints, matchers ...*labels.Matcher) ([]string, annotations.Annotations, error) { log, ctx := spanlogger.New(ctx, "distributorQuerier.labelNamesWithMatchers") defer log.Span.Finish() @@ -212,9 +212,9 @@ func (q *distributorQuerier) labelNamesWithMatchers(ctx context.Context, matcher ) if q.streamingMetadata { - ms, err = q.distributor.MetricsForLabelMatchersStream(ctx, model.Time(q.mint), model.Time(q.maxt), matchers...) + ms, err = q.distributor.MetricsForLabelMatchersStream(ctx, model.Time(q.mint), model.Time(q.maxt), labelHintsToSelectHints(hints), matchers...) } else { - ms, err = q.distributor.MetricsForLabelMatchers(ctx, model.Time(q.mint), model.Time(q.maxt), matchers...) + ms, err = q.distributor.MetricsForLabelMatchers(ctx, model.Time(q.mint), model.Time(q.maxt), labelHintsToSelectHints(hints), matchers...) } if err != nil { @@ -280,3 +280,13 @@ func (q *distributorExemplarQuerier) Select(start, end int64, matchers ...[]*lab } return ret, nil } + +func labelHintsToSelectHints(hints *storage.LabelHints) *storage.SelectHints { + if hints == nil { + return nil + } + + return &storage.SelectHints{ + Limit: hints.Limit, + } +} diff --git a/pkg/querier/distributor_queryable_test.go b/pkg/querier/distributor_queryable_test.go index fdef2c0aaf..c5decf75e9 100644 --- a/pkg/querier/distributor_queryable_test.go +++ b/pkg/querier/distributor_queryable_test.go @@ -85,8 +85,8 @@ func TestDistributorQuerier_SelectShouldHonorQueryIngestersWithin(t *testing.T) distributor := &MockDistributor{} distributor.On("QueryStream", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&client.QueryStreamResponse{}, nil) - distributor.On("MetricsForLabelMatchers", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return([]model.Metric{}, nil) - distributor.On("MetricsForLabelMatchersStream", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return([]model.Metric{}, nil) + distributor.On("MetricsForLabelMatchers", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return([]model.Metric{}, nil) + distributor.On("MetricsForLabelMatchersStream", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return([]model.Metric{}, nil) ctx := user.InjectOrgID(context.Background(), "test") queryable := newDistributorQueryable(distributor, streamingMetadataEnabled, nil, testData.queryIngestersWithin) @@ -213,9 +213,9 @@ func TestDistributorQuerier_LabelNames(t *testing.T) { {"job": "baz", "foo": "boom"}, } d := &MockDistributor{} - d.On("MetricsForLabelMatchers", mock.Anything, model.Time(mint), model.Time(maxt), someMatchers). + d.On("MetricsForLabelMatchers", mock.Anything, model.Time(mint), model.Time(maxt), mock.Anything, someMatchers). Return(metrics, nil) - d.On("MetricsForLabelMatchersStream", mock.Anything, model.Time(mint), model.Time(maxt), someMatchers). + d.On("MetricsForLabelMatchersStream", mock.Anything, model.Time(mint), model.Time(maxt), mock.Anything, someMatchers). Return(metrics, nil) queryable := newDistributorQueryable(d, streamingEnabled, nil, 0) diff --git a/pkg/querier/querier_test.go b/pkg/querier/querier_test.go index 5175af939b..f5e3e222da 100644 --- a/pkg/querier/querier_test.go +++ b/pkg/querier/querier_test.go @@ -1078,8 +1078,8 @@ func TestQuerier_ValidateQueryTimeRange_MaxQueryLookback(t *testing.T) { t.Run("series", func(t *testing.T) { distributor := &MockDistributor{} - distributor.On("MetricsForLabelMatchers", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return([]model.Metric{}, nil) - distributor.On("MetricsForLabelMatchersStream", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return([]model.Metric{}, nil) + distributor.On("MetricsForLabelMatchers", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return([]model.Metric{}, nil) + distributor.On("MetricsForLabelMatchersStream", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return([]model.Metric{}, nil) queryable, _, _ := New(cfg, overrides, distributor, queryables, nil, log.NewNopLogger()) q, err := queryable.Querier(util.TimeToMillis(testData.queryStartTime), util.TimeToMillis(testData.queryEndTime)) @@ -1119,8 +1119,8 @@ func TestQuerier_ValidateQueryTimeRange_MaxQueryLookback(t *testing.T) { t.Run("label names", func(t *testing.T) { distributor := &MockDistributor{} - distributor.On("LabelNames", mock.Anything, mock.Anything, mock.Anything).Return([]string{}, nil) - distributor.On("LabelNamesStream", mock.Anything, mock.Anything, mock.Anything).Return([]string{}, nil) + distributor.On("LabelNames", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return([]string{}, nil) + distributor.On("LabelNamesStream", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return([]string{}, nil) queryable, _, _ := New(cfg, overrides, distributor, queryables, nil, log.NewNopLogger()) q, err := queryable.Querier(util.TimeToMillis(testData.queryStartTime), util.TimeToMillis(testData.queryEndTime)) @@ -1147,8 +1147,8 @@ func TestQuerier_ValidateQueryTimeRange_MaxQueryLookback(t *testing.T) { labels.MustNewMatcher(labels.MatchNotEqual, "route", "get_user"), } distributor := &MockDistributor{} - distributor.On("MetricsForLabelMatchers", mock.Anything, mock.Anything, mock.Anything, matchers).Return([]model.Metric{}, nil) - distributor.On("MetricsForLabelMatchersStream", mock.Anything, mock.Anything, mock.Anything, matchers).Return([]model.Metric{}, nil) + distributor.On("MetricsForLabelMatchers", mock.Anything, mock.Anything, mock.Anything, mock.Anything, matchers).Return([]model.Metric{}, nil) + distributor.On("MetricsForLabelMatchersStream", mock.Anything, mock.Anything, mock.Anything, mock.Anything, matchers).Return([]model.Metric{}, nil) queryable, _, _ := New(cfg, overrides, distributor, queryables, nil, log.NewNopLogger()) q, err := queryable.Querier(util.TimeToMillis(testData.queryStartTime), util.TimeToMillis(testData.queryEndTime)) @@ -1165,7 +1165,7 @@ func TestQuerier_ValidateQueryTimeRange_MaxQueryLookback(t *testing.T) { args := distributor.Calls[0].Arguments assert.InDelta(t, util.TimeToMillis(testData.expectedMetadataStartTime), int64(args.Get(1).(model.Time)), delta) assert.InDelta(t, util.TimeToMillis(testData.expectedMetadataEndTime), int64(args.Get(2).(model.Time)), delta) - assert.Equal(t, matchers, args.Get(3).([]*labels.Matcher)) + assert.Equal(t, matchers, args.Get(4).([]*labels.Matcher)) } else { // Ensure no query has been executed (because skipped). assert.Len(t, distributor.Calls, 0) @@ -1174,8 +1174,8 @@ func TestQuerier_ValidateQueryTimeRange_MaxQueryLookback(t *testing.T) { t.Run("label values", func(t *testing.T) { distributor := &MockDistributor{} - distributor.On("LabelValuesForLabelName", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return([]string{}, nil) - distributor.On("LabelValuesForLabelNameStream", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return([]string{}, nil) + distributor.On("LabelValuesForLabelName", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return([]string{}, nil) + distributor.On("LabelValuesForLabelNameStream", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return([]string{}, nil) queryable, _, _ := New(cfg, overrides, distributor, queryables, nil, log.NewNopLogger()) q, err := queryable.Querier(util.TimeToMillis(testData.queryStartTime), util.TimeToMillis(testData.queryEndTime)) @@ -1303,22 +1303,22 @@ func (m *errDistributor) QueryStream(ctx context.Context, from, to model.Time, m func (m *errDistributor) QueryExemplars(ctx context.Context, from, to model.Time, matchers ...[]*labels.Matcher) (*client.ExemplarQueryResponse, error) { return nil, errDistributorError } -func (m *errDistributor) LabelValuesForLabelName(context.Context, model.Time, model.Time, model.LabelName, ...*labels.Matcher) ([]string, error) { +func (m *errDistributor) LabelValuesForLabelName(context.Context, model.Time, model.Time, model.LabelName, *storage.LabelHints, ...*labels.Matcher) ([]string, error) { return nil, errDistributorError } -func (m *errDistributor) LabelValuesForLabelNameStream(context.Context, model.Time, model.Time, model.LabelName, ...*labels.Matcher) ([]string, error) { +func (m *errDistributor) LabelValuesForLabelNameStream(context.Context, model.Time, model.Time, model.LabelName, *storage.LabelHints, ...*labels.Matcher) ([]string, error) { return nil, errDistributorError } -func (m *errDistributor) LabelNames(context.Context, model.Time, model.Time) ([]string, error) { +func (m *errDistributor) LabelNames(context.Context, model.Time, model.Time, *storage.LabelHints) ([]string, error) { return nil, errDistributorError } -func (m *errDistributor) LabelNamesStream(context.Context, model.Time, model.Time) ([]string, error) { +func (m *errDistributor) LabelNamesStream(context.Context, model.Time, model.Time, *storage.LabelHints) ([]string, error) { return nil, errDistributorError } -func (m *errDistributor) MetricsForLabelMatchers(ctx context.Context, from, through model.Time, matchers ...*labels.Matcher) ([]model.Metric, error) { +func (m *errDistributor) MetricsForLabelMatchers(ctx context.Context, from, through model.Time, hints *storage.SelectHints, matchers ...*labels.Matcher) ([]model.Metric, error) { return nil, errDistributorError } -func (m *errDistributor) MetricsForLabelMatchersStream(ctx context.Context, from, through model.Time, matchers ...*labels.Matcher) ([]model.Metric, error) { +func (m *errDistributor) MetricsForLabelMatchersStream(ctx context.Context, from, through model.Time, hints *storage.SelectHints, matchers ...*labels.Matcher) ([]model.Metric, error) { return nil, errDistributorError } @@ -1354,27 +1354,27 @@ func (d *emptyDistributor) QueryExemplars(ctx context.Context, from, to model.Ti return nil, nil } -func (d *emptyDistributor) LabelValuesForLabelName(context.Context, model.Time, model.Time, model.LabelName, ...*labels.Matcher) ([]string, error) { +func (d *emptyDistributor) LabelValuesForLabelName(context.Context, model.Time, model.Time, model.LabelName, *storage.LabelHints, ...*labels.Matcher) ([]string, error) { return nil, nil } -func (d *emptyDistributor) LabelValuesForLabelNameStream(context.Context, model.Time, model.Time, model.LabelName, ...*labels.Matcher) ([]string, error) { +func (d *emptyDistributor) LabelValuesForLabelNameStream(context.Context, model.Time, model.Time, model.LabelName, *storage.LabelHints, ...*labels.Matcher) ([]string, error) { return nil, nil } -func (d *emptyDistributor) LabelNames(context.Context, model.Time, model.Time) ([]string, error) { +func (d *emptyDistributor) LabelNames(context.Context, model.Time, model.Time, *storage.LabelHints) ([]string, error) { return nil, nil } -func (d *emptyDistributor) LabelNamesStream(context.Context, model.Time, model.Time) ([]string, error) { +func (d *emptyDistributor) LabelNamesStream(context.Context, model.Time, model.Time, *storage.LabelHints) ([]string, error) { return nil, nil } -func (d *emptyDistributor) MetricsForLabelMatchers(ctx context.Context, from, through model.Time, matchers ...*labels.Matcher) ([]model.Metric, error) { +func (d *emptyDistributor) MetricsForLabelMatchers(ctx context.Context, from, through model.Time, hints *storage.SelectHints, matchers ...*labels.Matcher) ([]model.Metric, error) { return nil, nil } -func (d *emptyDistributor) MetricsForLabelMatchersStream(ctx context.Context, from, through model.Time, matchers ...*labels.Matcher) ([]model.Metric, error) { +func (d *emptyDistributor) MetricsForLabelMatchersStream(ctx context.Context, from, through model.Time, hints *storage.SelectHints, matchers ...*labels.Matcher) ([]model.Metric, error) { return nil, nil } diff --git a/pkg/querier/testutils.go b/pkg/querier/testutils.go index a335bff618..67917b0c18 100644 --- a/pkg/querier/testutils.go +++ b/pkg/querier/testutils.go @@ -7,6 +7,7 @@ import ( "github.com/prometheus/common/model" "github.com/prometheus/prometheus/model/labels" "github.com/prometheus/prometheus/scrape" + "github.com/prometheus/prometheus/storage" "github.com/prometheus/prometheus/tsdb/chunkenc" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" @@ -32,28 +33,28 @@ func (m *MockDistributor) QueryStream(ctx context.Context, from, to model.Time, args := m.Called(ctx, from, to, matchers) return args.Get(0).(*client.QueryStreamResponse), args.Error(1) } -func (m *MockDistributor) LabelValuesForLabelName(ctx context.Context, from, to model.Time, lbl model.LabelName, matchers ...*labels.Matcher) ([]string, error) { - args := m.Called(ctx, from, to, lbl, matchers) +func (m *MockDistributor) LabelValuesForLabelName(ctx context.Context, from, to model.Time, lbl model.LabelName, hints *storage.LabelHints, matchers ...*labels.Matcher) ([]string, error) { + args := m.Called(ctx, from, to, lbl, hints, matchers) return args.Get(0).([]string), args.Error(1) } -func (m *MockDistributor) LabelValuesForLabelNameStream(ctx context.Context, from, to model.Time, lbl model.LabelName, matchers ...*labels.Matcher) ([]string, error) { - args := m.Called(ctx, from, to, lbl, matchers) +func (m *MockDistributor) LabelValuesForLabelNameStream(ctx context.Context, from, to model.Time, lbl model.LabelName, hints *storage.LabelHints, matchers ...*labels.Matcher) ([]string, error) { + args := m.Called(ctx, from, to, lbl, hints, matchers) return args.Get(0).([]string), args.Error(1) } -func (m *MockDistributor) LabelNames(ctx context.Context, from, to model.Time) ([]string, error) { - args := m.Called(ctx, from, to) +func (m *MockDistributor) LabelNames(ctx context.Context, from, to model.Time, hints *storage.LabelHints) ([]string, error) { + args := m.Called(ctx, from, to, hints) return args.Get(0).([]string), args.Error(1) } -func (m *MockDistributor) LabelNamesStream(ctx context.Context, from, to model.Time) ([]string, error) { - args := m.Called(ctx, from, to) +func (m *MockDistributor) LabelNamesStream(ctx context.Context, from, to model.Time, hints *storage.LabelHints) ([]string, error) { + args := m.Called(ctx, from, to, hints) return args.Get(0).([]string), args.Error(1) } -func (m *MockDistributor) MetricsForLabelMatchers(ctx context.Context, from, to model.Time, matchers ...*labels.Matcher) ([]model.Metric, error) { - args := m.Called(ctx, from, to, matchers) +func (m *MockDistributor) MetricsForLabelMatchers(ctx context.Context, from, to model.Time, hints *storage.SelectHints, matchers ...*labels.Matcher) ([]model.Metric, error) { + args := m.Called(ctx, from, to, hints, matchers) return args.Get(0).([]model.Metric), args.Error(1) } -func (m *MockDistributor) MetricsForLabelMatchersStream(ctx context.Context, from, to model.Time, matchers ...*labels.Matcher) ([]model.Metric, error) { - args := m.Called(ctx, from, to, matchers) +func (m *MockDistributor) MetricsForLabelMatchersStream(ctx context.Context, from, to model.Time, hints *storage.SelectHints, matchers ...*labels.Matcher) ([]model.Metric, error) { + args := m.Called(ctx, from, to, hints, matchers) return args.Get(0).([]model.Metric), args.Error(1) }