From 5dba71e633fc6cdd14a68f0df2765cdec72378a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Giedrius=20Statkevi=C4=8Dius?= Date: Mon, 12 Jun 2023 21:27:47 +0300 Subject: [PATCH] *: wire new Engine/Explain fields in query-frontend (#6433) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Pass Engine/Explain fields in query-frontend codecs - Add Engine field to QFE cache key - Add e2e tests for all cases Signed-off-by: Giedrius Statkevičius --- .../cortex/querier/queryrange/query_range.go | 80 ++- .../querier/queryrange/query_range_test.go | 2 +- .../querier/queryrange/queryrange.pb.go | 540 +++++++++++++++--- .../querier/queryrange/queryrange.proto | 7 + .../querier/queryrange/results_cache_test.go | 4 +- pkg/promclient/promclient.go | 64 ++- pkg/promclient/promclient_e2e_test.go | 2 +- pkg/queryfrontend/cache.go | 2 +- pkg/queryfrontend/cache_test.go | 12 +- pkg/queryfrontend/queryinstant_codec.go | 19 +- pkg/queryfrontend/queryinstant_codec_test.go | 37 ++ pkg/queryfrontend/queryrange_codec.go | 4 + pkg/queryfrontend/request.go | 4 + pkg/receive/head_series_limiter.go | 2 +- pkg/rules/queryable.go | 2 +- pkg/store/prometheus.go | 4 +- test/e2e/native_histograms_test.go | 2 +- test/e2e/query_frontend_test.go | 115 +++- test/e2e/query_test.go | 41 +- test/e2e/store_gateway_test.go | 6 +- 20 files changed, 766 insertions(+), 183 deletions(-) diff --git a/internal/cortex/querier/queryrange/query_range.go b/internal/cortex/querier/queryrange/query_range.go index 371f8e6b92..97e5c349b1 100644 --- a/internal/cortex/querier/queryrange/query_range.go +++ b/internal/cortex/querier/queryrange/query_range.go @@ -225,12 +225,21 @@ func (prometheusCodec) MergeResponse(_ Request, responses ...Response) (Response // Merge the responses. sort.Sort(byFirstTime(promResponses)) + var explanation *Explanation + for i := range promResponses { + if promResponses[i].Data.GetExplanation() != nil { + explanation = promResponses[i].Data.GetExplanation() + break + } + } + response := PrometheusResponse{ Status: StatusSuccess, Data: PrometheusData{ - ResultType: model.ValMatrix.String(), - Result: matrixMerge(promResponses), - Stats: StatsMerge(responses), + ResultType: model.ValMatrix.String(), + Result: matrixMerge(promResponses), + Stats: StatsMerge(responses), + Explanation: explanation, }, } @@ -524,16 +533,19 @@ func (s *StringSample) UnmarshalJSON(b []byte) error { // UnmarshalJSON implements json.Unmarshaler. func (s *PrometheusInstantQueryData) UnmarshalJSON(data []byte) error { var queryData struct { - ResultType string `json:"resultType"` - Result jsoniter.RawMessage `json:"result"` - Stats *PrometheusResponseStats `json:"stats,omitempty"` + ResultType string `json:"resultType"` + Result jsoniter.RawMessage `json:"result"` + Stats *PrometheusResponseStats `json:"stats,omitempty"` + Explanation *Explanation `json:"explanation,omitempty"` } if err := json.Unmarshal(data, &queryData); err != nil { return err } + s.ResultType = queryData.ResultType s.Stats = queryData.Stats + s.Explanation = queryData.Explanation switch s.ResultType { case model.ValVector.String(): var result struct { @@ -593,46 +605,54 @@ func (s *PrometheusInstantQueryData) MarshalJSON() ([]byte, error) { switch s.ResultType { case model.ValVector.String(): res := struct { - ResultType string `json:"resultType"` - Data []*Sample `json:"result"` - Stats *PrometheusResponseStats `json:"stats,omitempty"` + ResultType string `json:"resultType"` + Data []*Sample `json:"result"` + Stats *PrometheusResponseStats `json:"stats,omitempty"` + Explanation *Explanation `json:"explanation,omitempty"` }{ - ResultType: s.ResultType, - Data: s.Result.GetVector().Samples, - Stats: s.Stats, + ResultType: s.ResultType, + Data: s.Result.GetVector().Samples, + Stats: s.Stats, + Explanation: s.Explanation, } return json.Marshal(res) case model.ValMatrix.String(): res := struct { - ResultType string `json:"resultType"` - Data []*SampleStream `json:"result"` - Stats *PrometheusResponseStats `json:"stats,omitempty"` + ResultType string `json:"resultType"` + Data []*SampleStream `json:"result"` + Stats *PrometheusResponseStats `json:"stats,omitempty"` + Explanation *Explanation `json:"explanation,omitempty"` }{ - ResultType: s.ResultType, - Data: s.Result.GetMatrix().SampleStreams, - Stats: s.Stats, + ResultType: s.ResultType, + Data: s.Result.GetMatrix().SampleStreams, + Stats: s.Stats, + Explanation: s.Explanation, } return json.Marshal(res) case model.ValScalar.String(): res := struct { - ResultType string `json:"resultType"` - Data *cortexpb.Sample `json:"result"` - Stats *PrometheusResponseStats `json:"stats,omitempty"` + ResultType string `json:"resultType"` + Data *cortexpb.Sample `json:"result"` + Stats *PrometheusResponseStats `json:"stats,omitempty"` + Explanation *Explanation `json:"explanation,omitempty"` }{ - ResultType: s.ResultType, - Data: s.Result.GetScalar(), - Stats: s.Stats, + ResultType: s.ResultType, + Data: s.Result.GetScalar(), + Stats: s.Stats, + Explanation: s.Explanation, } return json.Marshal(res) case model.ValString.String(): res := struct { - ResultType string `json:"resultType"` - Data *StringSample `json:"result"` - Stats *PrometheusResponseStats `json:"stats,omitempty"` + ResultType string `json:"resultType"` + Data *StringSample `json:"result"` + Stats *PrometheusResponseStats `json:"stats,omitempty"` + Explanation *Explanation `json:"explanation,omitempty"` }{ - ResultType: s.ResultType, - Data: s.Result.GetStringSample(), - Stats: s.Stats, + ResultType: s.ResultType, + Data: s.Result.GetStringSample(), + Stats: s.Stats, + Explanation: s.Explanation, } return json.Marshal(res) default: diff --git a/internal/cortex/querier/queryrange/query_range_test.go b/internal/cortex/querier/queryrange/query_range_test.go index 1a8597151a..fbef87361f 100644 --- a/internal/cortex/querier/queryrange/query_range_test.go +++ b/internal/cortex/querier/queryrange/query_range_test.go @@ -129,7 +129,7 @@ func TestResponseWithStats(t *testing.T) { expected *PrometheusResponse }{ { - body: `{"status":"success","data":{"resultType":"matrix","result":[{"metric":{"foo":"bar"},"values":[[1536673680,"137"],[1536673780,"137"]]}],"stats":{"samples":{"totalQueryableSamples":10,"totalQueryableSamplesPerStep":[[1536673680,5],[1536673780,5]]}}}}`, + body: `{"status":"success","data":{"resultType":"matrix","result":[{"metric":{"foo":"bar"},"values":[[1536673680,"137"],[1536673780,"137"]]}],"stats":{"samples":{"totalQueryableSamples":10,"totalQueryableSamplesPerStep":[[1536673680,5],[1536673780,5]]}},"explanation":null}}`, expected: &PrometheusResponse{ Status: "success", Data: PrometheusData{ diff --git a/internal/cortex/querier/queryrange/queryrange.pb.go b/internal/cortex/querier/queryrange/queryrange.pb.go index 1b57e8f3c2..0b97731181 100644 --- a/internal/cortex/querier/queryrange/queryrange.pb.go +++ b/internal/cortex/querier/queryrange/queryrange.pb.go @@ -336,6 +336,7 @@ type PrometheusData struct { ResultType string `protobuf:"bytes,1,opt,name=ResultType,proto3" json:"resultType"` Result []SampleStream `protobuf:"bytes,2,rep,name=Result,proto3" json:"result"` Stats *PrometheusResponseStats `protobuf:"bytes,3,opt,name=stats,proto3" json:"stats,omitempty"` + Explanation *Explanation `protobuf:"bytes,4,opt,name=Explanation,proto3" json:"explanation"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -395,6 +396,13 @@ func (m *PrometheusData) GetStats() *PrometheusResponseStats { return nil } +func (m *PrometheusData) GetExplanation() *Explanation { + if m != nil { + return m.Explanation + } + return nil +} + type PrometheusInstantQueryResponse struct { Status string `protobuf:"bytes,1,opt,name=Status,proto3" json:"status"` Data PrometheusInstantQueryData `protobuf:"bytes,2,opt,name=Data,proto3" json:"data,omitempty"` @@ -478,6 +486,7 @@ type PrometheusInstantQueryData struct { ResultType string `protobuf:"bytes,1,opt,name=ResultType,proto3" json:"resultType"` Result PrometheusInstantQueryResult `protobuf:"bytes,2,opt,name=Result,proto3" json:"result"` Stats *PrometheusResponseStats `protobuf:"bytes,3,opt,name=stats,proto3" json:"stats,omitempty"` + Explanation *Explanation `protobuf:"bytes,4,opt,name=Explanation,proto3" json:"explanation"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -537,6 +546,13 @@ func (m *PrometheusInstantQueryData) GetStats() *PrometheusResponseStats { return nil } +func (m *PrometheusInstantQueryData) GetExplanation() *Explanation { + if m != nil { + return m.Explanation + } + return nil +} + type PrometheusInstantQueryResult struct { // Types that are valid to be assigned to Result: // @@ -1445,6 +1461,61 @@ func (m *CachingOptions) GetDisabled() bool { return false } +type Explanation struct { + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name"` + Children []*Explanation `protobuf:"bytes,2,rep,name=children,proto3" json:"children"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Explanation) Reset() { *m = Explanation{} } +func (m *Explanation) String() string { return proto.CompactTextString(m) } +func (*Explanation) ProtoMessage() {} +func (*Explanation) Descriptor() ([]byte, []int) { + return fileDescriptor_9af7607b46ac39b7, []int{22} +} +func (m *Explanation) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Explanation) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Explanation.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Explanation) XXX_Merge(src proto.Message) { + xxx_messageInfo_Explanation.Merge(m, src) +} +func (m *Explanation) XXX_Size() int { + return m.Size() +} +func (m *Explanation) XXX_DiscardUnknown() { + xxx_messageInfo_Explanation.DiscardUnknown(m) +} + +var xxx_messageInfo_Explanation proto.InternalMessageInfo + +func (m *Explanation) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *Explanation) GetChildren() []*Explanation { + if m != nil { + return m.Children + } + return nil +} + func init() { proto.RegisterType((*PrometheusRequestHeader)(nil), "queryrange.PrometheusRequestHeader") proto.RegisterType((*PrometheusRequest)(nil), "queryrange.PrometheusRequest") @@ -1468,6 +1539,7 @@ func init() { proto.RegisterType((*CachedResponse)(nil), "queryrange.CachedResponse") proto.RegisterType((*Extent)(nil), "queryrange.Extent") proto.RegisterType((*CachingOptions)(nil), "queryrange.CachingOptions") + proto.RegisterType((*Explanation)(nil), "queryrange.Explanation") } func init() { @@ -1475,92 +1547,97 @@ func init() { } var fileDescriptor_9af7607b46ac39b7 = []byte{ - // 1357 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xdc, 0x57, 0xcd, 0x6f, 0x1b, 0x45, - 0x1b, 0xef, 0xda, 0xce, 0xc6, 0x7e, 0x9c, 0x37, 0x69, 0x27, 0x7d, 0xdf, 0x6e, 0xf2, 0x86, 0xac, - 0xd9, 0x22, 0x14, 0x4a, 0x6b, 0x4b, 0x41, 0xe5, 0x50, 0x89, 0x42, 0x97, 0x16, 0x52, 0xd4, 0x8f, - 0x74, 0x52, 0xf5, 0xc0, 0xa5, 0x1a, 0xdb, 0x83, 0xb3, 0xd4, 0xde, 0xdd, 0xce, 0xcc, 0x96, 0xe6, - 0xc6, 0x1f, 0xc1, 0x81, 0x23, 0x48, 0x5c, 0x90, 0xf8, 0x27, 0xb8, 0xf5, 0x88, 0x38, 0x72, 0x58, - 0x50, 0x8f, 0x3e, 0x20, 0xfe, 0x04, 0x34, 0x1f, 0xeb, 0x9d, 0x75, 0x3e, 0xaa, 0x08, 0x09, 0x21, - 0x2e, 0xf6, 0xcc, 0xf3, 0xfc, 0x9e, 0xef, 0x67, 0x9e, 0x9d, 0x81, 0x4b, 0x83, 0x84, 0x09, 0xfa, - 0xbc, 0xf7, 0x34, 0xa3, 0x2c, 0xa2, 0x4c, 0xfd, 0x1f, 0x30, 0x12, 0x8f, 0xa8, 0xb5, 0xec, 0xa6, - 0x2c, 0x11, 0x09, 0x82, 0x92, 0xb2, 0x7e, 0x7e, 0x94, 0x8c, 0x12, 0x45, 0xee, 0xc9, 0x95, 0x46, - 0xac, 0x6f, 0x8e, 0x92, 0x64, 0x34, 0xa6, 0x3d, 0xb5, 0xeb, 0x67, 0x9f, 0xf5, 0x86, 0x19, 0x23, - 0x22, 0x4a, 0x62, 0xc3, 0xdf, 0x30, 0xd6, 0xf4, 0x5f, 0xda, 0x37, 0x0b, 0xc3, 0x5d, 0x9b, 0x97, - 0x26, 0xf1, 0x81, 0x66, 0x05, 0x7b, 0x70, 0x61, 0x97, 0x25, 0x13, 0x2a, 0xf6, 0x69, 0xc6, 0x31, - 0x7d, 0x9a, 0x51, 0x2e, 0x76, 0x28, 0x19, 0x52, 0x86, 0xd6, 0xa0, 0x71, 0x8f, 0x4c, 0xa8, 0xe7, - 0x74, 0x9c, 0xad, 0x56, 0xb8, 0x30, 0xcd, 0x7d, 0xe7, 0x0a, 0x56, 0x24, 0xf4, 0x1a, 0xb8, 0x8f, - 0xc8, 0x38, 0xa3, 0xdc, 0xab, 0x75, 0xea, 0x25, 0xd3, 0x10, 0x83, 0xbc, 0x06, 0xe7, 0x0e, 0x69, - 0x45, 0x08, 0x1a, 0x29, 0x11, 0xfb, 0x5a, 0x1f, 0x56, 0x6b, 0x74, 0x1e, 0x16, 0xb8, 0x20, 0x4c, - 0x78, 0xb5, 0x8e, 0xb3, 0x55, 0xc7, 0x7a, 0x83, 0xce, 0x42, 0x9d, 0xc6, 0x43, 0xaf, 0xae, 0x68, - 0x72, 0x29, 0x65, 0xb9, 0xa0, 0xa9, 0xd7, 0x50, 0x24, 0xb5, 0x46, 0xef, 0xc1, 0xa2, 0x88, 0x26, - 0x34, 0xc9, 0x84, 0xb7, 0xd0, 0x71, 0xb6, 0xda, 0xdb, 0x6b, 0x5d, 0x1d, 0x67, 0xb7, 0x88, 0xb3, - 0x7b, 0xd3, 0x64, 0x29, 0x6c, 0xbe, 0xc8, 0xfd, 0x33, 0x5f, 0xff, 0xea, 0x3b, 0xb8, 0x90, 0x91, - 0xa6, 0x55, 0xda, 0x3d, 0x57, 0xf9, 0xa3, 0x37, 0x68, 0x07, 0x96, 0x07, 0x64, 0xb0, 0x1f, 0xc5, - 0xa3, 0xfb, 0xa9, 0x94, 0xe4, 0xde, 0xa2, 0xd2, 0xbd, 0xde, 0xb5, 0xaa, 0xf6, 0x61, 0x05, 0x11, - 0x36, 0xa4, 0x72, 0x3c, 0x27, 0x87, 0x6e, 0xc2, 0xa2, 0x4e, 0x24, 0xf7, 0x9a, 0x9d, 0xfa, 0x56, - 0x7b, 0xfb, 0xa2, 0xad, 0xe2, 0x98, 0xa4, 0x17, 0x99, 0x2c, 0x44, 0x4d, 0x82, 0x04, 0xf7, 0x5a, - 0xda, 0x4b, 0xb5, 0x09, 0x1e, 0x82, 0x67, 0x2b, 0xe0, 0x69, 0x12, 0x73, 0xfa, 0x97, 0xcb, 0xf6, - 0x7d, 0x0d, 0xd0, 0x61, 0xb5, 0x28, 0x00, 0x77, 0x4f, 0x10, 0x91, 0x71, 0xa3, 0x12, 0xa6, 0xb9, - 0xef, 0x72, 0x45, 0xc1, 0x86, 0x83, 0x3e, 0x82, 0xc6, 0x4d, 0x22, 0x88, 0x2a, 0xe3, 0x5c, 0xb2, - 0x4a, 0x8d, 0x12, 0x11, 0xfe, 0x4f, 0x26, 0x6b, 0x9a, 0xfb, 0xcb, 0x43, 0x22, 0xc8, 0xe5, 0x64, - 0x12, 0x09, 0x3a, 0x49, 0xc5, 0x01, 0x56, 0xf2, 0xe8, 0x2a, 0xb4, 0x6e, 0x31, 0x96, 0xb0, 0x87, - 0x07, 0x29, 0x55, 0xf5, 0x6f, 0x85, 0x17, 0xa6, 0xb9, 0xbf, 0x4a, 0x0b, 0xa2, 0x25, 0x51, 0x22, - 0xd1, 0x5b, 0xb0, 0xa0, 0x36, 0xaa, 0x3f, 0x5a, 0xe1, 0xea, 0x34, 0xf7, 0x57, 0x94, 0x88, 0x05, - 0xd7, 0x08, 0x74, 0xab, 0x2c, 0xcb, 0x82, 0x2a, 0xcb, 0x1b, 0xc7, 0x95, 0xc5, 0xce, 0xea, 0x7c, - 0x5d, 0x82, 0x9f, 0x1d, 0x58, 0xae, 0x46, 0x86, 0xba, 0x00, 0x98, 0xf2, 0x6c, 0x2c, 0x94, 0xf3, - 0x3a, 0x57, 0xcb, 0xd3, 0xdc, 0x07, 0x36, 0xa3, 0x62, 0x0b, 0x81, 0x3e, 0x00, 0x57, 0xef, 0x54, - 0x35, 0xda, 0xdb, 0x9e, 0xed, 0xc8, 0x1e, 0x99, 0xa4, 0x63, 0xba, 0x27, 0x18, 0x25, 0x93, 0x70, - 0xd9, 0xe4, 0xcc, 0xd5, 0x9a, 0xb0, 0x91, 0x43, 0xf7, 0x8a, 0xe6, 0xa8, 0xab, 0xb4, 0x5f, 0x3c, - 0x39, 0x12, 0x59, 0x2a, 0xae, 0x73, 0xa3, 0xa4, 0xec, 0xdc, 0xe8, 0xb6, 0xfa, 0xb1, 0x06, 0x9b, - 0xa5, 0xdc, 0xed, 0x98, 0x0b, 0x12, 0x8b, 0x07, 0x52, 0xe7, 0xa9, 0x9a, 0x01, 0x57, 0x9a, 0xe1, - 0xcd, 0xa3, 0xbd, 0xb2, 0xb5, 0xff, 0x9b, 0x1a, 0xe3, 0x77, 0x07, 0xd6, 0x8f, 0x8f, 0xf2, 0xd4, - 0x4d, 0xb2, 0x6b, 0x35, 0x89, 0xcc, 0xe6, 0xd6, 0xab, 0xb3, 0xa9, 0xf1, 0x7f, 0x5b, 0xd3, 0xfc, - 0xe1, 0xc0, 0xc6, 0x49, 0x8e, 0xa0, 0x4b, 0xe0, 0xf2, 0x01, 0x19, 0x13, 0xa6, 0xc2, 0x6d, 0x6f, - 0x9f, 0xed, 0x16, 0x5f, 0x29, 0xd3, 0xe5, 0x3b, 0x67, 0xb0, 0x41, 0xa0, 0xeb, 0xb0, 0xc4, 0x05, - 0x8b, 0xe2, 0x91, 0xe6, 0x98, 0xa0, 0xab, 0x27, 0xc3, 0xe2, 0xef, 0x9c, 0xc1, 0x15, 0x3c, 0xba, - 0x0c, 0xee, 0x33, 0x3a, 0x10, 0x09, 0x33, 0xd1, 0x21, 0x5b, 0xf2, 0x91, 0xe2, 0x48, 0x6b, 0x1a, - 0x23, 0xd1, 0x13, 0x22, 0x58, 0xf4, 0x5c, 0xb5, 0xc7, 0x1c, 0xfa, 0xae, 0xe2, 0x48, 0xb4, 0xc6, - 0x84, 0x4d, 0x30, 0xa9, 0x0c, 0xde, 0x05, 0xf7, 0x51, 0xa1, 0x61, 0x91, 0x2b, 0xcb, 0xf2, 0x3c, - 0xd4, 0xe7, 0x55, 0x68, 0xa7, 0x70, 0x01, 0x09, 0x76, 0xc0, 0xd5, 0x5a, 0xd1, 0x75, 0xf8, 0x0f, - 0xb7, 0x4e, 0x78, 0x21, 0x7d, 0xec, 0x08, 0xc0, 0x55, 0x78, 0x30, 0xae, 0x7e, 0xb6, 0xad, 0x5a, - 0xa1, 0x07, 0xb6, 0x4b, 0x32, 0xaa, 0x4b, 0xaf, 0xa8, 0xb0, 0x06, 0xeb, 0x42, 0xb7, 0xa7, 0xb9, - 0x5f, 0x88, 0x97, 0x7e, 0x7f, 0x55, 0x99, 0x0b, 0x47, 0x09, 0xa2, 0xfb, 0xf0, 0x5f, 0x91, 0x08, - 0x32, 0x56, 0x85, 0x27, 0xfd, 0x71, 0xc1, 0x55, 0x3e, 0xd4, 0xc3, 0xb5, 0x69, 0xee, 0x1f, 0x0d, - 0xc0, 0x47, 0x93, 0xd1, 0x37, 0x0e, 0x6c, 0x1c, 0xc9, 0xd9, 0xa5, 0x6c, 0x4f, 0x5e, 0x05, 0xf4, - 0xd0, 0xbc, 0x76, 0x72, 0x70, 0xf3, 0xc2, 0xca, 0x59, 0xa3, 0x21, 0xec, 0x4c, 0x73, 0xff, 0x44, - 0x1b, 0xf8, 0x44, 0x6e, 0x10, 0xc1, 0x29, 0x2d, 0xca, 0xaf, 0xf9, 0x33, 0xf9, 0xad, 0xd5, 0x59, - 0xc1, 0x7a, 0x83, 0x5e, 0x87, 0x25, 0x79, 0x29, 0xe1, 0x82, 0x4c, 0xd2, 0xc7, 0x13, 0x6e, 0xee, - 0x42, 0xed, 0x19, 0xed, 0x2e, 0x0f, 0xbe, 0xad, 0xc1, 0x92, 0xdd, 0x0f, 0xe8, 0x4b, 0x07, 0xdc, - 0x31, 0xe9, 0xd3, 0x71, 0xd1, 0x3a, 0xab, 0xe5, 0xa9, 0xba, 0x23, 0xe9, 0xbb, 0x24, 0x62, 0xe1, - 0x9e, 0x9c, 0x01, 0xbf, 0xe4, 0xfe, 0x8d, 0x51, 0x24, 0xf6, 0xb3, 0x7e, 0x77, 0x90, 0x4c, 0x7a, - 0x62, 0x9f, 0xc4, 0x09, 0xbf, 0x12, 0x25, 0x66, 0xd5, 0x8b, 0x62, 0x41, 0x59, 0x4c, 0xc6, 0xbd, - 0xb9, 0x3b, 0xa4, 0xd6, 0x73, 0x63, 0x48, 0x52, 0x41, 0x99, 0x1c, 0x24, 0x13, 0x2a, 0x58, 0x34, - 0xc0, 0xc6, 0x2e, 0xba, 0x56, 0x36, 0x9a, 0xae, 0xc5, 0xa1, 0x83, 0x5d, 0xce, 0x20, 0x15, 0x68, - 0xd9, 0x51, 0x08, 0x03, 0xec, 0x47, 0x5c, 0x24, 0x23, 0x26, 0x9b, 0xbf, 0xae, 0xc4, 0xfd, 0xc3, - 0xcd, 0xbf, 0x53, 0x60, 0x54, 0x34, 0xe7, 0x8c, 0xb6, 0xd6, 0x4c, 0x14, 0x5b, 0x5a, 0x82, 0xef, - 0x6a, 0xe0, 0x9a, 0x31, 0xf0, 0x0f, 0xc8, 0xce, 0xdb, 0xd0, 0xd6, 0xc1, 0xaa, 0xcb, 0x95, 0xaa, - 0xa9, 0x13, 0xb6, 0xa6, 0xb9, 0xaf, 0x8b, 0x8e, 0x6d, 0x2e, 0xda, 0x80, 0xd6, 0xac, 0xda, 0xe6, - 0xda, 0x5b, 0x12, 0xd0, 0x1d, 0x28, 0x23, 0x36, 0x93, 0xea, 0xff, 0x27, 0xe4, 0x4a, 0xe5, 0xc9, - 0xa9, 0xe6, 0xa9, 0x5c, 0x06, 0x1f, 0xc3, 0x92, 0x3d, 0x42, 0xab, 0x3d, 0xd9, 0x3a, 0x45, 0x4f, - 0x0a, 0x58, 0x3d, 0xa2, 0x4a, 0xd5, 0x58, 0x9c, 0xf9, 0x58, 0xde, 0xb7, 0x63, 0xa9, 0xbd, 0x3a, - 0x16, 0x7d, 0xb7, 0xb6, 0xdc, 0x4f, 0x61, 0x65, 0x0e, 0x23, 0x23, 0x18, 0x24, 0x59, 0x2c, 0x94, - 0x35, 0x07, 0xeb, 0x8d, 0x7c, 0x44, 0xf0, 0x4c, 0xdb, 0x70, 0xb0, 0x5c, 0xa2, 0xab, 0xb0, 0xd8, - 0xcf, 0x06, 0x4f, 0xa8, 0x28, 0x3a, 0xae, 0x62, 0xb9, 0xb4, 0xa9, 0x30, 0xb8, 0xc0, 0x06, 0x1c, - 0x56, 0xe6, 0x78, 0x68, 0x13, 0xa0, 0x9f, 0x64, 0xf1, 0x90, 0xb0, 0xc8, 0x8c, 0xb8, 0x05, 0x6c, - 0x51, 0xa4, 0x47, 0xe3, 0xe4, 0x0b, 0xca, 0x8c, 0x75, 0xbd, 0x91, 0xd4, 0x2c, 0x4d, 0xa9, 0xfe, - 0x36, 0x39, 0x58, 0x6f, 0x4a, 0xef, 0x1b, 0x96, 0xf7, 0xc1, 0xe7, 0xb0, 0x2c, 0x5f, 0x19, 0x74, - 0x38, 0xbb, 0x79, 0xad, 0x41, 0xfd, 0x09, 0x3d, 0x30, 0x57, 0x86, 0xc5, 0x69, 0xee, 0xcb, 0x2d, - 0x96, 0x3f, 0xf2, 0x25, 0x44, 0x9f, 0x0b, 0x1a, 0x8b, 0xe2, 0x24, 0x56, 0xbe, 0x42, 0xb7, 0x14, - 0x2b, 0x5c, 0x31, 0xa7, 0xa7, 0x80, 0xe2, 0x62, 0x11, 0xfc, 0xe0, 0x80, 0xab, 0x41, 0xc8, 0x2f, - 0xde, 0x63, 0x7a, 0x6c, 0xab, 0x7e, 0x55, 0x84, 0xe2, 0x69, 0xb6, 0xa6, 0x9f, 0x66, 0xaa, 0x1d, - 0xb4, 0x17, 0x34, 0x1e, 0xea, 0x37, 0x5a, 0x07, 0x9a, 0x82, 0x91, 0x01, 0x7d, 0x1c, 0x0d, 0xcd, - 0x75, 0xab, 0xb8, 0x1b, 0x29, 0xf2, 0xed, 0x21, 0xba, 0x0e, 0x4d, 0x66, 0xc2, 0x31, 0x4f, 0xb6, - 0xf3, 0x87, 0x9e, 0x6c, 0x37, 0xe2, 0x83, 0x70, 0x69, 0x9a, 0xfb, 0x33, 0x24, 0x9e, 0xad, 0x3e, - 0x69, 0x34, 0xeb, 0x67, 0x1b, 0xc1, 0x65, 0x9d, 0x1a, 0xeb, 0xa9, 0xb5, 0x0e, 0xcd, 0x61, 0xc4, - 0xe5, 0xd0, 0x1d, 0x2a, 0xc7, 0x9b, 0x78, 0xb6, 0x0f, 0xbd, 0x17, 0x2f, 0x37, 0x9d, 0x9f, 0x5e, - 0x6e, 0x3a, 0xbf, 0xbd, 0xdc, 0x74, 0x3e, 0xb5, 0x5e, 0xda, 0x7d, 0x57, 0xd9, 0x7c, 0xe7, 0xcf, - 0x00, 0x00, 0x00, 0xff, 0xff, 0x17, 0xc3, 0x35, 0xd6, 0xaa, 0x0f, 0x00, 0x00, + // 1427 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xdc, 0x58, 0xcd, 0x73, 0x1b, 0xc5, + 0x12, 0xcf, 0x4a, 0xf2, 0x5a, 0x6a, 0xf9, 0xd9, 0xc9, 0x38, 0xef, 0x45, 0xf6, 0xf3, 0xf3, 0xea, + 0x6d, 0x28, 0xca, 0x84, 0x44, 0xaa, 0x32, 0x15, 0x0e, 0xa9, 0x22, 0xe0, 0x25, 0x01, 0x27, 0x95, + 0x0f, 0x67, 0x9c, 0xca, 0x81, 0x4b, 0x6a, 0x24, 0x0d, 0xd2, 0x92, 0xd5, 0xee, 0x66, 0x76, 0x36, + 0xd8, 0x37, 0xfe, 0x08, 0x0e, 0x9c, 0x28, 0xa8, 0xe2, 0x42, 0x15, 0x7f, 0x01, 0x37, 0x6e, 0x39, + 0x72, 0xe6, 0xb0, 0x50, 0x39, 0xee, 0x89, 0x3f, 0x81, 0x9a, 0x8f, 0xd5, 0xce, 0xca, 0x1f, 0x29, + 0x17, 0x17, 0xe0, 0x62, 0xcd, 0xf4, 0xd7, 0x74, 0xff, 0xba, 0xa7, 0xb7, 0xc7, 0x70, 0x65, 0x18, + 0x31, 0x4e, 0x0f, 0xfa, 0xcf, 0x53, 0xca, 0x7c, 0xca, 0xe4, 0xef, 0x21, 0x23, 0xe1, 0x98, 0x1a, + 0xcb, 0x5e, 0xcc, 0x22, 0x1e, 0x21, 0x28, 0x29, 0xeb, 0x17, 0xc7, 0xd1, 0x38, 0x92, 0xe4, 0xbe, + 0x58, 0x29, 0x89, 0xf5, 0xcd, 0x71, 0x14, 0x8d, 0x03, 0xda, 0x97, 0xbb, 0x41, 0xfa, 0x69, 0x7f, + 0x94, 0x32, 0xc2, 0xfd, 0x28, 0xd4, 0xfc, 0x0d, 0x7d, 0x9a, 0xfa, 0x89, 0x07, 0x7a, 0xa1, 0xb9, + 0x6b, 0xf3, 0xda, 0x24, 0x3c, 0x54, 0x2c, 0x77, 0x1f, 0x2e, 0xed, 0xb1, 0x68, 0x4a, 0xf9, 0x84, + 0xa6, 0x09, 0xa6, 0xcf, 0x53, 0x9a, 0xf0, 0x5d, 0x4a, 0x46, 0x94, 0xa1, 0x35, 0x68, 0x3c, 0x20, + 0x53, 0xda, 0xb1, 0xba, 0xd6, 0x56, 0xcb, 0x5b, 0xc8, 0x33, 0xc7, 0xba, 0x86, 0x25, 0x09, 0xfd, + 0x0f, 0xec, 0x27, 0x24, 0x48, 0x69, 0xd2, 0xa9, 0x75, 0xeb, 0x25, 0x53, 0x13, 0xdd, 0xac, 0x06, + 0x17, 0x8e, 0x58, 0x45, 0x08, 0x1a, 0x31, 0xe1, 0x13, 0x65, 0x0f, 0xcb, 0x35, 0xba, 0x08, 0x0b, + 0x09, 0x27, 0x8c, 0x77, 0x6a, 0x5d, 0x6b, 0xab, 0x8e, 0xd5, 0x06, 0x9d, 0x87, 0x3a, 0x0d, 0x47, + 0x9d, 0xba, 0xa4, 0x89, 0xa5, 0xd0, 0x4d, 0x38, 0x8d, 0x3b, 0x0d, 0x49, 0x92, 0x6b, 0xf4, 0x1e, + 0x2c, 0x72, 0x7f, 0x4a, 0xa3, 0x94, 0x77, 0x16, 0xba, 0xd6, 0x56, 0x7b, 0x7b, 0xad, 0xa7, 0xe2, + 0xec, 0x15, 0x71, 0xf6, 0x6e, 0x69, 0x94, 0xbc, 0xe6, 0xcb, 0xcc, 0x39, 0xf7, 0xd5, 0xaf, 0x8e, + 0x85, 0x0b, 0x1d, 0x71, 0xb4, 0x84, 0xbd, 0x63, 0x4b, 0x7f, 0xd4, 0x06, 0xed, 0xc2, 0xf2, 0x90, + 0x0c, 0x27, 0x7e, 0x38, 0x7e, 0x18, 0x0b, 0xcd, 0xa4, 0xb3, 0x28, 0x6d, 0xaf, 0xf7, 0x8c, 0xac, + 0x7d, 0x58, 0x91, 0xf0, 0x1a, 0xc2, 0x38, 0x9e, 0xd3, 0x43, 0xb7, 0x60, 0x51, 0x01, 0x99, 0x74, + 0x9a, 0xdd, 0xfa, 0x56, 0x7b, 0xfb, 0xb2, 0x69, 0xe2, 0x04, 0xd0, 0x0b, 0x24, 0x0b, 0x55, 0x0d, + 0x10, 0x4f, 0x3a, 0x2d, 0xe5, 0xa5, 0xdc, 0xb8, 0x8f, 0xa1, 0x63, 0x1a, 0x48, 0xe2, 0x28, 0x4c, + 0xe8, 0x9f, 0x4e, 0xdb, 0xf7, 0x35, 0x40, 0x47, 0xcd, 0x22, 0x17, 0xec, 0x7d, 0x4e, 0x78, 0x9a, + 0x68, 0x93, 0x90, 0x67, 0x8e, 0x9d, 0x48, 0x0a, 0xd6, 0x1c, 0xf4, 0x11, 0x34, 0x6e, 0x11, 0x4e, + 0x64, 0x1a, 0xe7, 0xc0, 0x2a, 0x2d, 0x0a, 0x09, 0xef, 0x3f, 0x02, 0xac, 0x3c, 0x73, 0x96, 0x47, + 0x84, 0x93, 0xab, 0xd1, 0xd4, 0xe7, 0x74, 0x1a, 0xf3, 0x43, 0x2c, 0xf5, 0xd1, 0x75, 0x68, 0xdd, + 0x66, 0x2c, 0x62, 0x8f, 0x0f, 0x63, 0x2a, 0xf3, 0xdf, 0xf2, 0x2e, 0xe5, 0x99, 0xb3, 0x4a, 0x0b, + 0xa2, 0xa1, 0x51, 0x4a, 0xa2, 0xb7, 0x60, 0x41, 0x6e, 0x64, 0x7d, 0xb4, 0xbc, 0xd5, 0x3c, 0x73, + 0x56, 0xa4, 0x8a, 0x21, 0xae, 0x24, 0xd0, 0xed, 0x32, 0x2d, 0x0b, 0x32, 0x2d, 0x6f, 0x9c, 0x94, + 0x16, 0x13, 0xd5, 0xf9, 0xbc, 0xb8, 0x5f, 0xd7, 0x60, 0xb9, 0x1a, 0x19, 0xea, 0x01, 0x60, 0x9a, + 0xa4, 0x01, 0x97, 0xce, 0x2b, 0xac, 0x96, 0xf3, 0xcc, 0x01, 0x36, 0xa3, 0x62, 0x43, 0x02, 0x7d, + 0x00, 0xb6, 0xda, 0xc9, 0x6c, 0xb4, 0xb7, 0x3b, 0xa6, 0x23, 0xfb, 0x64, 0x1a, 0x07, 0x74, 0x9f, + 0x33, 0x4a, 0xa6, 0xde, 0xb2, 0xc6, 0xcc, 0x56, 0x96, 0xb0, 0xd6, 0x43, 0x0f, 0x8a, 0xe2, 0xa8, + 0x4b, 0xd8, 0x2f, 0x9f, 0x1e, 0x89, 0x48, 0x55, 0xa2, 0xb0, 0x91, 0x5a, 0x26, 0x36, 0x92, 0x80, + 0xee, 0x42, 0xfb, 0xf6, 0x41, 0x1c, 0x90, 0x50, 0x5e, 0x1a, 0x09, 0x66, 0x7b, 0xfb, 0x92, 0x69, + 0xd5, 0x60, 0x7b, 0x2b, 0x79, 0xe6, 0xb4, 0x69, 0x49, 0xc0, 0xa6, 0xb2, 0xfb, 0x53, 0x0d, 0x36, + 0x4b, 0x1f, 0xee, 0x84, 0x09, 0x27, 0x21, 0x7f, 0x24, 0x2c, 0x9d, 0xa9, 0xb0, 0x70, 0xa5, 0xb0, + 0xde, 0x3c, 0x3e, 0x42, 0xd3, 0xfa, 0x3f, 0xa9, 0xc8, 0x7e, 0xac, 0xc1, 0xfa, 0xc9, 0x51, 0x9e, + 0xb9, 0xe0, 0xf6, 0x8c, 0x82, 0x13, 0x68, 0x6e, 0xbd, 0x1e, 0x4d, 0x25, 0xff, 0xb7, 0x2c, 0xc0, + 0xdf, 0x2d, 0xd8, 0x38, 0x2d, 0x28, 0x74, 0x05, 0xec, 0x64, 0x48, 0x02, 0xc2, 0x24, 0x74, 0xed, + 0xed, 0xf3, 0xbd, 0xe2, 0xeb, 0xa9, 0x6f, 0xdf, 0xee, 0x39, 0xac, 0x25, 0xd0, 0x4d, 0x58, 0x4a, + 0x38, 0xf3, 0xc3, 0xb1, 0xe2, 0x68, 0x00, 0xab, 0x37, 0xd6, 0xe0, 0xef, 0x9e, 0xc3, 0x15, 0x79, + 0x74, 0x15, 0xec, 0x17, 0x74, 0xc8, 0x23, 0xa6, 0x91, 0x42, 0xa6, 0xe6, 0x13, 0xc9, 0x11, 0xa7, + 0x29, 0x19, 0x21, 0x3d, 0x25, 0x9c, 0xf9, 0x07, 0x1a, 0x81, 0x8a, 0xf4, 0x7d, 0xc9, 0x11, 0xd2, + 0x4a, 0xc6, 0x6b, 0x82, 0x4e, 0x8b, 0xfb, 0x2e, 0xd8, 0x4f, 0x0a, 0x0b, 0x8b, 0x89, 0x3c, 0x59, + 0xdc, 0xad, 0xfa, 0xbc, 0x09, 0xe5, 0x14, 0x2e, 0x44, 0xdc, 0x5d, 0xb0, 0x95, 0x55, 0x74, 0x13, + 0xfe, 0x95, 0x18, 0x9d, 0xa7, 0xd0, 0x3e, 0xb1, 0x35, 0xe1, 0xaa, 0xb8, 0x1b, 0x54, 0xc7, 0x09, + 0x23, 0xef, 0xe8, 0x91, 0xe9, 0x92, 0x88, 0xea, 0xca, 0x6b, 0xaa, 0x45, 0x09, 0xab, 0xa2, 0x69, + 0xe7, 0x99, 0x53, 0xa8, 0x97, 0x7e, 0x7f, 0x59, 0xe9, 0x31, 0xc7, 0x29, 0xa2, 0x87, 0xf0, 0x6f, + 0x1e, 0x71, 0x12, 0xc8, 0xc4, 0x93, 0x41, 0x50, 0x70, 0xa5, 0x0f, 0x75, 0x6f, 0x2d, 0xcf, 0x9c, + 0xe3, 0x05, 0xf0, 0xf1, 0x64, 0xf4, 0x8d, 0x05, 0x1b, 0xc7, 0x72, 0xf6, 0x28, 0xdb, 0x17, 0x23, + 0x8a, 0x6a, 0xe6, 0x37, 0x4e, 0x0f, 0x6e, 0x5e, 0x59, 0x3a, 0xab, 0x2d, 0x78, 0xdd, 0x3c, 0x73, + 0x4e, 0x3d, 0x03, 0x9f, 0xca, 0x75, 0x7d, 0x38, 0xe3, 0x89, 0x62, 0xca, 0x78, 0x21, 0x66, 0x00, + 0x85, 0x0a, 0x56, 0x1b, 0xf4, 0x7f, 0x58, 0x12, 0xc3, 0x52, 0xc2, 0xc9, 0x34, 0x7e, 0x3a, 0x4d, + 0xf4, 0x8c, 0xd6, 0x9e, 0xd1, 0xee, 0x27, 0xee, 0xb7, 0x35, 0x58, 0x32, 0xeb, 0x01, 0x7d, 0x61, + 0x81, 0x1d, 0x90, 0x01, 0x0d, 0x8a, 0xd2, 0x59, 0x2d, 0x6f, 0xd5, 0x3d, 0x41, 0xdf, 0x23, 0x3e, + 0xf3, 0xf6, 0x45, 0x3f, 0xf9, 0x25, 0x73, 0x76, 0xc6, 0x3e, 0x9f, 0xa4, 0x83, 0xde, 0x30, 0x9a, + 0xf6, 0xf9, 0x84, 0x84, 0x51, 0x72, 0xcd, 0x8f, 0xf4, 0xaa, 0xef, 0x87, 0x9c, 0xb2, 0x90, 0x04, + 0xfd, 0xb9, 0xd9, 0x56, 0xd9, 0xd9, 0x19, 0x91, 0x98, 0x53, 0x26, 0x9a, 0xd2, 0x94, 0x72, 0xe6, + 0x0f, 0xb1, 0x3e, 0x17, 0xdd, 0x28, 0x0b, 0x4d, 0xe5, 0xe2, 0xc8, 0xc5, 0x2e, 0xfb, 0x99, 0x0c, + 0xb4, 0xac, 0x28, 0x84, 0x01, 0x26, 0x7e, 0xc2, 0xa3, 0x31, 0x13, 0xc5, 0x5f, 0x97, 0xea, 0xce, + 0xd1, 0xe2, 0xdf, 0x2d, 0x64, 0x64, 0x34, 0x17, 0xb4, 0xb5, 0xd6, 0x4c, 0x15, 0x1b, 0x56, 0xdc, + 0xef, 0x6a, 0x60, 0xeb, 0x36, 0xf0, 0x17, 0x40, 0xe7, 0x6d, 0x68, 0xab, 0x60, 0xe5, 0xd0, 0x27, + 0x73, 0x6a, 0x79, 0xad, 0x3c, 0x73, 0x54, 0xd2, 0xb1, 0xc9, 0x45, 0x1b, 0xd0, 0x9a, 0x65, 0x5b, + 0x8f, 0xe3, 0x25, 0x01, 0xdd, 0x83, 0x32, 0x62, 0xdd, 0xa9, 0xfe, 0x7b, 0x0a, 0x56, 0x12, 0x27, + 0xab, 0x8a, 0x53, 0xb9, 0x74, 0x3f, 0x86, 0x25, 0xb3, 0x85, 0x56, 0x6b, 0xb2, 0x75, 0x86, 0x9a, + 0xe4, 0xb0, 0x7a, 0x4c, 0x96, 0xaa, 0xb1, 0x58, 0xf3, 0xb1, 0xbc, 0x6f, 0xc6, 0x52, 0x7b, 0x7d, + 0x2c, 0x6a, 0xe6, 0x37, 0xdc, 0x8f, 0x61, 0x65, 0x4e, 0x46, 0x44, 0x30, 0x8c, 0xd2, 0x90, 0xcb, + 0xd3, 0x2c, 0xac, 0x36, 0xe2, 0x71, 0x93, 0xa4, 0xea, 0x0c, 0x0b, 0x8b, 0x25, 0xba, 0x0e, 0x8b, + 0x83, 0x74, 0xf8, 0x8c, 0xf2, 0xa2, 0xe2, 0x2a, 0x27, 0x97, 0x67, 0x4a, 0x19, 0x5c, 0xc8, 0xba, + 0x09, 0xac, 0xcc, 0xf1, 0xd0, 0x26, 0xc0, 0x20, 0x4a, 0xc3, 0x11, 0x61, 0xbe, 0x6e, 0x71, 0x0b, + 0xd8, 0xa0, 0x08, 0x8f, 0x82, 0xe8, 0x73, 0xca, 0xf4, 0xe9, 0x6a, 0x23, 0xa8, 0x69, 0x1c, 0x53, + 0xf5, 0x6d, 0xb2, 0xb0, 0xda, 0x94, 0xde, 0x37, 0x0c, 0xef, 0xdd, 0xcf, 0x60, 0x59, 0xbc, 0x7e, + 0xe8, 0x68, 0x36, 0xc5, 0xad, 0x41, 0xfd, 0x19, 0x3d, 0xd4, 0xe3, 0xc7, 0x62, 0x9e, 0x39, 0x62, + 0x8b, 0xc5, 0x1f, 0xf1, 0x42, 0xa3, 0x07, 0x9c, 0x86, 0xbc, 0xb8, 0x89, 0xa8, 0xfa, 0x29, 0x17, + 0x2c, 0x6f, 0x45, 0xdf, 0x9e, 0x42, 0x14, 0x17, 0x0b, 0xf7, 0x07, 0x0b, 0x6c, 0x25, 0x84, 0x9c, + 0xe2, 0x9d, 0xa8, 0xda, 0xb6, 0xac, 0x57, 0x49, 0x28, 0x9e, 0x8c, 0x6b, 0xea, 0xc9, 0x28, 0xcb, + 0x41, 0x79, 0x41, 0xc3, 0x91, 0x7a, 0x3b, 0x76, 0xa1, 0xc9, 0x19, 0x19, 0xd2, 0xa7, 0xfe, 0x48, + 0x8f, 0x6e, 0xc5, 0x9c, 0x25, 0xc9, 0x77, 0x46, 0xe8, 0x26, 0x34, 0x99, 0x0e, 0x47, 0x3f, 0x25, + 0x2f, 0x1e, 0x79, 0x4a, 0xee, 0x84, 0x87, 0xde, 0x52, 0x9e, 0x39, 0x33, 0x49, 0x3c, 0x5b, 0xdd, + 0x6d, 0x34, 0xeb, 0xe7, 0x1b, 0xee, 0x55, 0x05, 0x8d, 0xf1, 0x04, 0x5c, 0x87, 0xe6, 0xc8, 0x4f, + 0x44, 0xd3, 0x1d, 0x49, 0xc7, 0x9b, 0x78, 0xb6, 0x77, 0xc3, 0xca, 0xa8, 0x83, 0x36, 0xa0, 0x11, + 0x96, 0xaf, 0xb6, 0x66, 0x9e, 0x39, 0x72, 0x8f, 0xe5, 0x5f, 0xb4, 0x03, 0xcd, 0xe1, 0xc4, 0x0f, + 0x46, 0x8c, 0x86, 0x1a, 0xc9, 0x13, 0x87, 0x22, 0xe9, 0x63, 0x21, 0x8c, 0x67, 0x2b, 0xaf, 0xf3, + 0xf2, 0xd5, 0xa6, 0xf5, 0xf3, 0xab, 0x4d, 0xeb, 0xb7, 0x57, 0x9b, 0xd6, 0x27, 0xc6, 0x7f, 0x1c, + 0x06, 0xb6, 0x8c, 0xf1, 0x9d, 0x3f, 0x02, 0x00, 0x00, 0xff, 0xff, 0x6b, 0x8e, 0x21, 0xfd, 0xb2, + 0x10, 0x00, 0x00, } func (m *PrometheusRequestHeader) Marshal() (dAtA []byte, err error) { @@ -1840,6 +1917,18 @@ func (m *PrometheusData) MarshalToSizedBuffer(dAtA []byte) (int, error) { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + if m.Explanation != nil { + { + size, err := m.Explanation.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQueryrange(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } if m.Stats != nil { { size, err := m.Stats.MarshalToSizedBuffer(dAtA[:i]) @@ -1972,6 +2061,18 @@ func (m *PrometheusInstantQueryData) MarshalToSizedBuffer(dAtA []byte) (int, err i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + if m.Explanation != nil { + { + size, err := m.Explanation.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQueryrange(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } if m.Stats != nil { { size, err := m.Stats.MarshalToSizedBuffer(dAtA[:i]) @@ -2786,6 +2887,54 @@ func (m *CachingOptions) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *Explanation) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Explanation) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Explanation) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if len(m.Children) > 0 { + for iNdEx := len(m.Children) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Children[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQueryrange(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = encodeVarintQueryrange(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + func encodeVarintQueryrange(dAtA []byte, offset int, v uint64) int { offset -= sovQueryrange(v) base := offset @@ -2936,6 +3085,10 @@ func (m *PrometheusData) Size() (n int) { l = m.Stats.Size() n += 1 + l + sovQueryrange(uint64(l)) } + if m.Explanation != nil { + l = m.Explanation.Size() + n += 1 + l + sovQueryrange(uint64(l)) + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -2990,6 +3143,10 @@ func (m *PrometheusInstantQueryData) Size() (n int) { l = m.Stats.Size() n += 1 + l + sovQueryrange(uint64(l)) } + if m.Explanation != nil { + l = m.Explanation.Size() + n += 1 + l + sovQueryrange(uint64(l)) + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -3355,6 +3512,28 @@ func (m *CachingOptions) Size() (n int) { return n } +func (m *Explanation) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Name) + if l > 0 { + n += 1 + l + sovQueryrange(uint64(l)) + } + if len(m.Children) > 0 { + for _, e := range m.Children { + l = e.Size() + n += 1 + l + sovQueryrange(uint64(l)) + } + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + func sovQueryrange(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } @@ -4240,6 +4419,42 @@ func (m *PrometheusData) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Explanation", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQueryrange + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQueryrange + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQueryrange + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Explanation == nil { + m.Explanation = &Explanation{} + } + if err := m.Explanation.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipQueryrange(dAtA[iNdEx:]) @@ -4606,6 +4821,42 @@ func (m *PrometheusInstantQueryData) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Explanation", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQueryrange + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQueryrange + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQueryrange + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Explanation == nil { + m.Explanation = &Explanation{} + } + if err := m.Explanation.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipQueryrange(dAtA[iNdEx:]) @@ -6333,6 +6584,123 @@ func (m *CachingOptions) Unmarshal(dAtA []byte) error { } return nil } +func (m *Explanation) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQueryrange + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Explanation: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Explanation: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQueryrange + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQueryrange + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQueryrange + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Children", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQueryrange + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQueryrange + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQueryrange + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Children = append(m.Children, &Explanation{}) + if err := m.Children[len(m.Children)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQueryrange(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQueryrange + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func skipQueryrange(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 diff --git a/internal/cortex/querier/queryrange/queryrange.proto b/internal/cortex/querier/queryrange/queryrange.proto index 6f05fc1670..77e3bbdc55 100644 --- a/internal/cortex/querier/queryrange/queryrange.proto +++ b/internal/cortex/querier/queryrange/queryrange.proto @@ -48,6 +48,7 @@ message PrometheusData { string ResultType = 1 [(gogoproto.jsontag) = "resultType"]; repeated SampleStream Result = 2 [(gogoproto.nullable) = false, (gogoproto.jsontag) = "result"]; PrometheusResponseStats stats = 3 [(gogoproto.jsontag) = "stats,omitempty"]; + Explanation Explanation = 4 [(gogoproto.jsontag) = "explanation"]; } message PrometheusInstantQueryResponse { @@ -62,6 +63,7 @@ message PrometheusInstantQueryData { string ResultType = 1 [(gogoproto.jsontag) = "resultType"]; PrometheusInstantQueryResult Result = 2 [(gogoproto.nullable) = false, (gogoproto.jsontag) = "result"]; PrometheusResponseStats stats = 3 [(gogoproto.jsontag) = "stats,omitempty"]; + Explanation Explanation = 4 [(gogoproto.jsontag) = "explanation"]; } message PrometheusInstantQueryResult { @@ -150,3 +152,8 @@ message Extent { message CachingOptions { bool disabled = 1; } + +message Explanation { + string name = 1 [(gogoproto.jsontag) = "name"]; + repeated Explanation children = 2 [(gogoproto.jsontag) = "children"]; +} diff --git a/internal/cortex/querier/queryrange/results_cache_test.go b/internal/cortex/querier/queryrange/results_cache_test.go index 6f2fca05dd..cb7d04fc95 100644 --- a/internal/cortex/querier/queryrange/results_cache_test.go +++ b/internal/cortex/querier/queryrange/results_cache_test.go @@ -24,8 +24,8 @@ import ( const ( query = "/api/v1/query_range?end=1536716898&query=sum%28container_memory_rss%29+by+%28namespace%29&start=1536673680&stats=all&step=120" - responseBody = `{"status":"success","data":{"resultType":"matrix","result":[{"metric":{"foo":"bar"},"values":[[1536673680,"137"],[1536673780,"137"]]}]}}` - histogramResponseBody = `{"status":"success","data":{"resultType":"matrix","result":[{"metric":{"fake":"histogram"},"histograms":[[1536673680,{"count":"5","sum":"18.4","buckets":[[3,"-0.001","0.001","2"],[0,"0.7071067811865475","1","1"],[0,"1","1.414213562373095","2"],[0,"2","2.82842712474619","1"],[0,"2.82842712474619","4","1"]]}]]}]}}` + responseBody = `{"status":"success","data":{"resultType":"matrix","result":[{"metric":{"foo":"bar"},"values":[[1536673680,"137"],[1536673780,"137"]]}],"explanation":null}}` + histogramResponseBody = `{"status":"success","data":{"resultType":"matrix","result":[{"metric":{"fake":"histogram"},"histograms":[[1536673680,{"count":"5","sum":"18.4","buckets":[[3,"-0.001","0.001","2"],[0,"0.7071067811865475","1","1"],[0,"1","1.414213562373095","2"],[0,"2","2.82842712474619","1"],[0,"2.82842712474619","4","1"]]}]]}],"explanation":null}}` ) var ( diff --git a/pkg/promclient/promclient.go b/pkg/promclient/promclient.go index 930b1a0cc1..ced2c2eed6 100644 --- a/pkg/promclient/promclient.go +++ b/pkg/promclient/promclient.go @@ -33,6 +33,7 @@ import ( "google.golang.org/grpc/codes" "gopkg.in/yaml.v2" + "github.com/thanos-io/thanos/internal/cortex/querier/queryrange" "github.com/thanos-io/thanos/pkg/exemplars/exemplarspb" "github.com/thanos-io/thanos/pkg/httpconfig" "github.com/thanos-io/thanos/pkg/metadata/metadatapb" @@ -360,6 +361,8 @@ type QueryOptions struct { PartialResponseStrategy storepb.PartialResponseStrategy Method string MaxSourceResolution string + Engine string + Explain bool } func (p *QueryOptions) AddTo(values url.Values) error { @@ -368,6 +371,9 @@ func (p *QueryOptions) AddTo(values url.Values) error { values.Add("max_source_resolution", p.MaxSourceResolution) } + values.Add("explain", fmt.Sprintf("%v", p.Explain)) + values.Add("engine", p.Engine) + var partialResponseValue string switch p.PartialResponseStrategy { case storepb.PartialResponseStrategy_WARN: @@ -385,15 +391,15 @@ func (p *QueryOptions) AddTo(values url.Values) error { } // QueryInstant performs an instant query using a default HTTP client and returns results in model.Vector type. -func (c *Client) QueryInstant(ctx context.Context, base *url.URL, query string, t time.Time, opts QueryOptions) (model.Vector, []string, error) { +func (c *Client) QueryInstant(ctx context.Context, base *url.URL, query string, t time.Time, opts QueryOptions) (model.Vector, []string, *queryrange.Explanation, error) { params, err := url.ParseQuery(base.RawQuery) if err != nil { - return nil, nil, errors.Wrapf(err, "parse raw query %s", base.RawQuery) + return nil, nil, nil, errors.Wrapf(err, "parse raw query %s", base.RawQuery) } params.Add("query", query) params.Add("time", t.Format(time.RFC3339Nano)) if err := opts.AddTo(params); err != nil { - return nil, nil, errors.Wrap(err, "add thanos opts query params") + return nil, nil, nil, errors.Wrap(err, "add thanos opts query params") } u := *base @@ -412,25 +418,26 @@ func (c *Client) QueryInstant(ctx context.Context, base *url.URL, query string, body, _, err := c.req2xx(ctx, &u, method) if err != nil { - return nil, nil, errors.Wrap(err, "read query instant response") + return nil, nil, nil, errors.Wrap(err, "read query instant response") } // Decode only ResultType and load Result only as RawJson since we don't know // structure of the Result yet. var m struct { Data struct { - ResultType string `json:"resultType"` - Result json.RawMessage `json:"result"` + ResultType string `json:"resultType"` + Result json.RawMessage `json:"result"` + Explanation *queryrange.Explanation `json:"explanation"` } `json:"data"` Error string `json:"error,omitempty"` ErrorType string `json:"errorType,omitempty"` - // Extra field supported by Thanos Querier. + // Extra fields supported by Thanos Querier. Warnings []string `json:"warnings"` } if err = json.Unmarshal(body, &m); err != nil { - return nil, nil, errors.Wrap(err, "unmarshal query instant response") + return nil, nil, nil, errors.Wrap(err, "unmarshal query instant response") } var vectorResult model.Vector @@ -440,29 +447,29 @@ func (c *Client) QueryInstant(ctx context.Context, base *url.URL, query string, switch m.Data.ResultType { case string(parser.ValueTypeVector): if err = json.Unmarshal(m.Data.Result, &vectorResult); err != nil { - return nil, nil, errors.Wrap(err, "decode result into ValueTypeVector") + return nil, nil, nil, errors.Wrap(err, "decode result into ValueTypeVector") } case string(parser.ValueTypeScalar): vectorResult, err = convertScalarJSONToVector(m.Data.Result) if err != nil { - return nil, nil, errors.Wrap(err, "decode result into ValueTypeScalar") + return nil, nil, nil, errors.Wrap(err, "decode result into ValueTypeScalar") } default: if m.Warnings != nil { - return nil, nil, errors.Errorf("error: %s, type: %s, warning: %s", m.Error, m.ErrorType, strings.Join(m.Warnings, ", ")) + return nil, nil, nil, errors.Errorf("error: %s, type: %s, warning: %s", m.Error, m.ErrorType, strings.Join(m.Warnings, ", ")) } if m.Error != "" { - return nil, nil, errors.Errorf("error: %s, type: %s", m.Error, m.ErrorType) + return nil, nil, nil, errors.Errorf("error: %s, type: %s", m.Error, m.ErrorType) } - return nil, nil, errors.Errorf("received status code: 200, unknown response type: '%q'", m.Data.ResultType) + return nil, nil, nil, errors.Errorf("received status code: 200, unknown response type: '%q'", m.Data.ResultType) } - return vectorResult, m.Warnings, nil + return vectorResult, m.Warnings, m.Data.Explanation, nil } // PromqlQueryInstant performs instant query and returns results in promql.Vector type that is compatible with promql package. func (c *Client) PromqlQueryInstant(ctx context.Context, base *url.URL, query string, t time.Time, opts QueryOptions) (promql.Vector, []string, error) { - vectorResult, warnings, err := c.QueryInstant(ctx, base, query, t, opts) + vectorResult, warnings, _, err := c.QueryInstant(ctx, base, query, t, opts) if err != nil { return nil, nil, err } @@ -491,17 +498,17 @@ func (c *Client) PromqlQueryInstant(ctx context.Context, base *url.URL, query st } // QueryRange performs a range query using a default HTTP client and returns results in model.Matrix type. -func (c *Client) QueryRange(ctx context.Context, base *url.URL, query string, startTime, endTime, step int64, opts QueryOptions) (model.Matrix, []string, error) { +func (c *Client) QueryRange(ctx context.Context, base *url.URL, query string, startTime, endTime, step int64, opts QueryOptions) (model.Matrix, []string, *queryrange.Explanation, error) { params, err := url.ParseQuery(base.RawQuery) if err != nil { - return nil, nil, errors.Wrapf(err, "parse raw query %s", base.RawQuery) + return nil, nil, nil, errors.Wrapf(err, "parse raw query %s", base.RawQuery) } params.Add("query", query) params.Add("start", formatTime(timestamp.Time(startTime))) params.Add("end", formatTime(timestamp.Time(endTime))) params.Add("step", strconv.FormatInt(step, 10)) if err := opts.AddTo(params); err != nil { - return nil, nil, errors.Wrap(err, "add thanos opts query params") + return nil, nil, nil, errors.Wrap(err, "add thanos opts query params") } u := *base @@ -515,25 +522,26 @@ func (c *Client) QueryRange(ctx context.Context, base *url.URL, query string, st body, _, err := c.req2xx(ctx, &u, http.MethodGet) if err != nil { - return nil, nil, errors.Wrap(err, "read query range response") + return nil, nil, nil, errors.Wrap(err, "read query range response") } // Decode only ResultType and load Result only as RawJson since we don't know // structure of the Result yet. var m struct { Data struct { - ResultType string `json:"resultType"` - Result json.RawMessage `json:"result"` + ResultType string `json:"resultType"` + Result json.RawMessage `json:"result"` + Explanation *queryrange.Explanation `json:"explanation"` } `json:"data"` Error string `json:"error,omitempty"` ErrorType string `json:"errorType,omitempty"` - // Extra field supported by Thanos Querier. + // Extra fields supported by Thanos Querier. Warnings []string `json:"warnings"` } if err = json.Unmarshal(body, &m); err != nil { - return nil, nil, errors.Wrap(err, "unmarshal query range response") + return nil, nil, nil, errors.Wrap(err, "unmarshal query range response") } var matrixResult model.Matrix @@ -542,19 +550,19 @@ func (c *Client) QueryRange(ctx context.Context, base *url.URL, query string, st switch m.Data.ResultType { case string(parser.ValueTypeMatrix): if err = json.Unmarshal(m.Data.Result, &matrixResult); err != nil { - return nil, nil, errors.Wrap(err, "decode result into ValueTypeMatrix") + return nil, nil, nil, errors.Wrap(err, "decode result into ValueTypeMatrix") } default: if m.Warnings != nil { - return nil, nil, errors.Errorf("error: %s, type: %s, warning: %s", m.Error, m.ErrorType, strings.Join(m.Warnings, ", ")) + return nil, nil, nil, errors.Errorf("error: %s, type: %s, warning: %s", m.Error, m.ErrorType, strings.Join(m.Warnings, ", ")) } if m.Error != "" { - return nil, nil, errors.Errorf("error: %s, type: %s", m.Error, m.ErrorType) + return nil, nil, nil, errors.Errorf("error: %s, type: %s", m.Error, m.ErrorType) } - return nil, nil, errors.Errorf("received status code: 200, unknown response type: '%q'", m.Data.ResultType) + return nil, nil, nil, errors.Errorf("received status code: 200, unknown response type: '%q'", m.Data.ResultType) } - return matrixResult, m.Warnings, nil + return matrixResult, m.Warnings, m.Data.Explanation, nil } // Scalar response consists of array with mixed types so it needs to be diff --git a/pkg/promclient/promclient_e2e_test.go b/pkg/promclient/promclient_e2e_test.go index 7a5aaec003..19511c69f1 100644 --- a/pkg/promclient/promclient_e2e_test.go +++ b/pkg/promclient/promclient_e2e_test.go @@ -177,7 +177,7 @@ func TestQueryRange_e2e(t *testing.T) { u, err := url.Parse(fmt.Sprintf("http://%s", p.Addr())) testutil.Ok(t, err) - res, _, err := NewDefaultClient().QueryRange( + res, _, _, err := NewDefaultClient().QueryRange( ctx, u, `{a="b"}`, diff --git a/pkg/queryfrontend/cache.go b/pkg/queryfrontend/cache.go index 4d11667490..156d26b5c3 100644 --- a/pkg/queryfrontend/cache.go +++ b/pkg/queryfrontend/cache.go @@ -33,7 +33,7 @@ func (t thanosCacheKeyGenerator) GenerateCacheKey(userID string, r queryrange.Re for ; i < len(t.resolutions) && t.resolutions[i] > tr.MaxSourceResolution; i++ { } shardInfoKey := generateShardInfoKey(tr) - return fmt.Sprintf("fe:%s:%s:%d:%d:%d:%s:%d", userID, tr.Query, tr.Step, currentInterval, i, shardInfoKey, tr.LookbackDelta) + return fmt.Sprintf("fe:%s:%s:%d:%d:%d:%s:%d:%s", userID, tr.Query, tr.Step, currentInterval, i, shardInfoKey, tr.LookbackDelta, tr.Engine) case *ThanosLabelsRequest: return fmt.Sprintf("fe:%s:%s:%s:%d", userID, tr.Label, tr.Matchers, currentInterval) case *ThanosSeriesRequest: diff --git a/pkg/queryfrontend/cache_test.go b/pkg/queryfrontend/cache_test.go index 6f2baca2bc..9cd14ff2b8 100644 --- a/pkg/queryfrontend/cache_test.go +++ b/pkg/queryfrontend/cache_test.go @@ -39,7 +39,7 @@ func TestGenerateCacheKey(t *testing.T) { Start: 0, Step: 60 * seconds, }, - expected: "fe::up:60000:0:2:-:0", + expected: "fe::up:60000:0:2:-:0:", }, { name: "10s step", @@ -48,7 +48,7 @@ func TestGenerateCacheKey(t *testing.T) { Start: 0, Step: 10 * seconds, }, - expected: "fe::up:10000:0:2:-:0", + expected: "fe::up:10000:0:2:-:0:", }, { name: "1m downsampling resolution", @@ -58,7 +58,7 @@ func TestGenerateCacheKey(t *testing.T) { Step: 10 * seconds, MaxSourceResolution: 60 * seconds, }, - expected: "fe::up:10000:0:2:-:0", + expected: "fe::up:10000:0:2:-:0:", }, { name: "5m downsampling resolution, different cache key", @@ -68,7 +68,7 @@ func TestGenerateCacheKey(t *testing.T) { Step: 10 * seconds, MaxSourceResolution: 300 * seconds, }, - expected: "fe::up:10000:0:1:-:0", + expected: "fe::up:10000:0:1:-:0:", }, { name: "1h downsampling resolution, different cache key", @@ -78,7 +78,7 @@ func TestGenerateCacheKey(t *testing.T) { Step: 10 * seconds, MaxSourceResolution: hour, }, - expected: "fe::up:10000:0:0:-:0", + expected: "fe::up:10000:0:0:-:0:", }, { name: "1h downsampling resolution with lookback delta", @@ -89,7 +89,7 @@ func TestGenerateCacheKey(t *testing.T) { MaxSourceResolution: hour, LookbackDelta: 1000, }, - expected: "fe::up:10000:0:0:-:1000", + expected: "fe::up:10000:0:0:-:1000:", }, { name: "label names, no matcher", diff --git a/pkg/queryfrontend/queryinstant_codec.go b/pkg/queryfrontend/queryinstant_codec.go index 1eec030996..67e51f0471 100644 --- a/pkg/queryfrontend/queryinstant_codec.go +++ b/pkg/queryfrontend/queryinstant_codec.go @@ -54,6 +54,15 @@ func (c queryInstantCodec) MergeResponse(req queryrange.Request, responses ...qu for _, resp := range responses { promResponses = append(promResponses, resp.(*queryrange.PrometheusInstantQueryResponse)) } + + var explanation *queryrange.Explanation + for i := range promResponses { + if promResponses[i].Data.GetExplanation() != nil { + explanation = promResponses[i].Data.GetExplanation() + break + } + } + var res queryrange.Response switch promResponses[0].Data.ResultType { case model.ValMatrix.String(): @@ -66,7 +75,8 @@ func (c queryInstantCodec) MergeResponse(req queryrange.Request, responses ...qu Matrix: matrixMerge(promResponses), }, }, - Stats: queryrange.StatsMerge(responses), + Stats: queryrange.StatsMerge(responses), + Explanation: explanation, }, } default: @@ -83,7 +93,8 @@ func (c queryInstantCodec) MergeResponse(req queryrange.Request, responses ...qu Vector: v, }, }, - Stats: queryrange.StatsMerge(responses), + Stats: queryrange.StatsMerge(responses), + Explanation: explanation, }, } } @@ -143,6 +154,8 @@ func (c queryInstantCodec) DecodeRequest(_ context.Context, r *http.Request, for result.Query = r.FormValue("query") result.Path = r.URL.Path + result.Explain = r.FormValue("explain") + result.Engine = r.FormValue("engine") for _, header := range forwardHeaders { for h, hv := range r.Header { @@ -164,6 +177,8 @@ func (c queryInstantCodec) EncodeRequest(ctx context.Context, r queryrange.Reque "query": []string{thanosReq.Query}, queryv1.DedupParam: []string{strconv.FormatBool(thanosReq.Dedup)}, queryv1.PartialResponseParam: []string{strconv.FormatBool(thanosReq.PartialResponse)}, + queryv1.QueryExplainParam: []string{thanosReq.Explain}, + queryv1.EngineParam: []string{thanosReq.Engine}, queryv1.ReplicaLabelsParam: thanosReq.ReplicaLabels, } diff --git a/pkg/queryfrontend/queryinstant_codec_test.go b/pkg/queryfrontend/queryinstant_codec_test.go index 57d7dbd71b..b525bc85e6 100644 --- a/pkg/queryfrontend/queryinstant_codec_test.go +++ b/pkg/queryfrontend/queryinstant_codec_test.go @@ -896,6 +896,43 @@ func TestDecodeResponse(t *testing.T) { expectedResponse queryrange.Response expectedErr error }{ + { + name: "with explanation", + body: `{ + "status":"success", + "data":{ + "resultType":"vector", + "result":[], + "explanation": { + "name":"[*concurrencyOperator(buff=2)]", + "children":[{"name":"[*aggregate] sum by ([])", "children": []}] + } +} +}`, + expectedResponse: &queryrange.PrometheusInstantQueryResponse{ + Status: queryrange.StatusSuccess, + Headers: headers, + Data: queryrange.PrometheusInstantQueryData{ + Explanation: &queryrange.Explanation{ + Name: "[*concurrencyOperator(buff=2)]", + Children: []*queryrange.Explanation{ + { + Name: "[*aggregate] sum by ([])", + Children: []*queryrange.Explanation{}, + }, + }, + }, + ResultType: model.ValVector.String(), + Result: queryrange.PrometheusInstantQueryResult{ + Result: &queryrange.PrometheusInstantQueryResult_Vector{ + Vector: &queryrange.Vector{ + Samples: []*queryrange.Sample{}, + }, + }, + }, + }, + }, + }, { name: "empty vector", body: `{ diff --git a/pkg/queryfrontend/queryrange_codec.go b/pkg/queryfrontend/queryrange_codec.go index 0c3718c140..8b58efc090 100644 --- a/pkg/queryfrontend/queryrange_codec.go +++ b/pkg/queryfrontend/queryrange_codec.go @@ -129,6 +129,8 @@ func (c queryRangeCodec) DecodeRequest(_ context.Context, r *http.Request, forwa } result.Query = r.FormValue("query") + result.Explain = r.FormValue(queryv1.QueryExplainParam) + result.Engine = r.FormValue(queryv1.EngineParam) result.Path = r.URL.Path for _, value := range r.Header.Values(cacheControlHeader) { @@ -159,6 +161,8 @@ func (c queryRangeCodec) EncodeRequest(ctx context.Context, r queryrange.Request "end": []string{encodeTime(thanosReq.End)}, "step": []string{encodeDurationMillis(thanosReq.Step)}, "query": []string{thanosReq.Query}, + queryv1.QueryExplainParam: []string{thanosReq.Explain}, + queryv1.EngineParam: []string{thanosReq.Engine}, queryv1.DedupParam: []string{strconv.FormatBool(thanosReq.Dedup)}, queryv1.PartialResponseParam: []string{strconv.FormatBool(thanosReq.PartialResponse)}, queryv1.ReplicaLabelsParam: thanosReq.ReplicaLabels, diff --git a/pkg/queryfrontend/request.go b/pkg/queryfrontend/request.go index 5cda2cc8e1..7c1f1f6f19 100644 --- a/pkg/queryfrontend/request.go +++ b/pkg/queryfrontend/request.go @@ -55,6 +55,8 @@ type ThanosQueryRangeRequest struct { Stats string ShardInfo *storepb.ShardInfo LookbackDelta int64 + Explain string + Engine string } // IsDedupEnabled returns true if deduplication is enabled. @@ -154,6 +156,8 @@ type ThanosQueryInstantRequest struct { Stats string ShardInfo *storepb.ShardInfo LookbackDelta int64 // in milliseconds. + Explain string + Engine string } // IsDedupEnabled returns true if deduplication is enabled. diff --git a/pkg/receive/head_series_limiter.go b/pkg/receive/head_series_limiter.go index f0e4512386..994221c2f6 100644 --- a/pkg/receive/head_series_limiter.go +++ b/pkg/receive/head_series_limiter.go @@ -106,7 +106,7 @@ func NewHeadSeriesLimit(w WriteLimitsConfig, registerer prometheus.Registerer, l func (h *headSeriesLimit) QueryMetaMonitoring(ctx context.Context) error { c := promclient.NewWithTracingClient(h.logger, h.metaMonitoringClient, httpconfig.ThanosUserAgent) - vectorRes, _, err := c.QueryInstant(ctx, h.metaMonitoringURL, h.metaMonitoringQuery, time.Now(), promclient.QueryOptions{Deduplicate: true}) + vectorRes, _, _, err := c.QueryInstant(ctx, h.metaMonitoringURL, h.metaMonitoringQuery, time.Now(), promclient.QueryOptions{Deduplicate: true}) if err != nil { h.metaMonitoringErr.Inc() return err diff --git a/pkg/rules/queryable.go b/pkg/rules/queryable.go index ccfc727b75..399a27cd25 100644 --- a/pkg/rules/queryable.go +++ b/pkg/rules/queryable.go @@ -88,7 +88,7 @@ func (q *promClientsQuerier) Select(_ bool, _ *storage.SelectHints, matchers ... promClient := q.promClients[i] endpoints := RemoveDuplicateQueryEndpoints(q.logger, q.duplicatedQuery, q.queryClients[i].Endpoints()) for _, i := range rand.Perm(len(endpoints)) { - m, warns, err := promClient.QueryRange(q.ctx, endpoints[i], query, q.mint, q.maxt, q.step, promclient.QueryOptions{ + m, warns, _, err := promClient.QueryRange(q.ctx, endpoints[i], query, q.mint, q.maxt, q.step, promclient.QueryOptions{ Deduplicate: true, Method: q.httpMethod, }) diff --git a/pkg/store/prometheus.go b/pkg/store/prometheus.go index 51114e1c13..b9febc77c5 100644 --- a/pkg/store/prometheus.go +++ b/pkg/store/prometheus.go @@ -318,13 +318,13 @@ func (p *PrometheusStore) queryPrometheus( opts := promclient.QueryOptions{} step := r.QueryHints.StepMillis / 1000 if step != 0 { - result, _, err := p.client.QueryRange(s.Context(), p.base, r.ToPromQL(), r.MinTime, r.MaxTime, step, opts) + result, _, _, err := p.client.QueryRange(s.Context(), p.base, r.ToPromQL(), r.MinTime, r.MaxTime, step, opts) if err != nil { return err } matrix = result } else { - vector, _, err := p.client.QueryInstant(s.Context(), p.base, r.ToPromQL(), timestamp.Time(r.MaxTime), opts) + vector, _, _, err := p.client.QueryInstant(s.Context(), p.base, r.ToPromQL(), timestamp.Time(r.MaxTime), opts) if err != nil { return err } diff --git a/test/e2e/native_histograms_test.go b/test/e2e/native_histograms_test.go index 33810354fd..799974a5be 100644 --- a/test/e2e/native_histograms_test.go +++ b/test/e2e/native_histograms_test.go @@ -90,7 +90,7 @@ func TestQueryNativeHistograms(t *testing.T) { t.Run("query histogram rate and compare to Prometheus result", func(t *testing.T) { query := func() string { return fmt.Sprintf("rate(%v[1m])", testHistogramMetricName) } - expected := instantQuery(t, ctx, prom1.Endpoint("http"), query, ts, promclient.QueryOptions{}, 1) + expected, _ := instantQuery(t, ctx, prom1.Endpoint("http"), query, ts, promclient.QueryOptions{}, 1) expected[0].Metric["prometheus"] = "prom-ha" expected[0].Timestamp = 0 queryAndAssert(t, ctx, querier.Endpoint("http"), query, ts, promclient.QueryOptions{Deduplicate: true}, expected) diff --git a/test/e2e/query_frontend_test.go b/test/e2e/query_frontend_test.go index c3f0b8756c..762eeecea0 100644 --- a/test/e2e/query_frontend_test.go +++ b/test/e2e/query_frontend_test.go @@ -5,6 +5,8 @@ package e2e_test import ( "context" + "fmt" + "os" "reflect" "sort" "testing" @@ -23,10 +25,119 @@ import ( "github.com/thanos-io/thanos/pkg/cacheutil" "github.com/thanos-io/thanos/pkg/promclient" "github.com/thanos-io/thanos/pkg/queryfrontend" + "github.com/thanos-io/thanos/pkg/runutil" "github.com/thanos-io/thanos/pkg/testutil/e2eutil" "github.com/thanos-io/thanos/test/e2e/e2ethanos" ) +func TestQFEEngineExplanation(t *testing.T) { + t.Parallel() + + e, err := e2e.NewDockerEnvironment("qfe-opts") + testutil.Ok(t, err) + t.Cleanup(e2ethanos.CleanScenario(t, e)) + + prom, sidecar := e2ethanos.NewPrometheusWithSidecar(e, "1", e2ethanos.DefaultPromConfig("test", 0, "", "", e2ethanos.LocalPrometheusTarget), "", e2ethanos.DefaultPrometheusImage(), "") + testutil.Ok(t, e2e.StartAndWaitReady(prom, sidecar)) + + q := e2ethanos.NewQuerierBuilder(e, "1", sidecar.InternalEndpoint("grpc")).Init() + testutil.Ok(t, e2e.StartAndWaitReady(q)) + + inMemoryCacheConfig := queryfrontend.CacheProviderConfig{ + Type: queryfrontend.INMEMORY, + Config: queryfrontend.InMemoryResponseCacheConfig{ + MaxSizeItems: 1000, + Validity: time.Hour, + }, + } + + cfg := queryfrontend.Config{} + queryFrontend := e2ethanos.NewQueryFrontend(e, "1", "http://"+q.InternalEndpoint("http"), cfg, inMemoryCacheConfig) + testutil.Ok(t, e2e.StartAndWaitReady(queryFrontend)) + + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) + t.Cleanup(cancel) + + testutil.Ok(t, q.WaitSumMetricsWithOptions(e2emon.Equals(1), []string{"thanos_store_nodes_grpc_connections"}, e2emon.WaitMissingMetrics())) + + var queriesBeforeUp = 0 + testutil.Ok(t, runutil.RetryWithLog(e2e.NewLogger(os.Stderr), 5*time.Second, ctx.Done(), func() error { + queriesBeforeUp++ + _, _, err := simpleInstantQuery(t, ctx, queryFrontend.Endpoint("http"), e2ethanos.QueryUpWithoutInstance, time.Now, promclient.QueryOptions{}, 1) + return err + })) + + t.Logf("queried %d times before prom is up", queriesBeforeUp) + + t.Run("passes query to Thanos engine", func(t *testing.T) { + queryAndAssertSeries(t, ctx, queryFrontend.Endpoint("http"), e2ethanos.QueryUpWithoutInstance, time.Now, promclient.QueryOptions{ + Deduplicate: false, + Engine: "thanos", + }, []model.Metric{ + { + "job": "myself", + "prometheus": "test", + "replica": "0", + }, + }) + + testutil.Ok(t, q.WaitSumMetricsWithOptions(e2emon.GreaterOrEqual(1), []string{"thanos_engine_queries_total"}, e2emon.WaitMissingMetrics(), e2emon.WithLabelMatchers(matchers.MustNewMatcher(matchers.MatchEqual, "fallback", "false")))) + + }) + + now := time.Now() + t.Run("passes range query to Thanos engine and returns explanation", func(t *testing.T) { + explanation := rangeQuery(t, ctx, queryFrontend.Endpoint("http"), e2ethanos.QueryUpWithoutInstance, + timestamp.FromTime(now.Add(-5*time.Minute)), + timestamp.FromTime(now), 1, promclient.QueryOptions{ + Explain: true, + Engine: "thanos", + }, func(res model.Matrix) error { + if res.Len() == 0 { + return fmt.Errorf("expected results") + } + return nil + }) + testutil.Assert(t, explanation != nil, "expected to have an explanation") + testutil.Ok(t, q.WaitSumMetricsWithOptions(e2emon.GreaterOrEqual(2), []string{"thanos_engine_queries_total"}, e2emon.WaitMissingMetrics(), e2emon.WithLabelMatchers(matchers.MustNewMatcher(matchers.MatchEqual, "fallback", "false")))) + }) + + t.Run("passes query to Prometheus engine", func(t *testing.T) { + queryAndAssertSeries(t, ctx, queryFrontend.Endpoint("http"), e2ethanos.QueryUpWithoutInstance, time.Now, promclient.QueryOptions{ + Deduplicate: false, + Engine: "prometheus", + }, []model.Metric{ + { + "job": "myself", + "prometheus": "test", + "replica": "0", + }, + }) + + testutil.Ok(t, q.WaitSumMetricsWithOptions(e2emon.GreaterOrEqual(1), []string{"thanos_engine_queries_total"}, e2emon.WaitMissingMetrics(), e2emon.WithLabelMatchers(matchers.MustNewMatcher(matchers.MatchEqual, "fallback", "false")))) + testutil.Ok(t, q.WaitSumMetricsWithOptions(e2emon.GreaterOrEqual(float64(queriesBeforeUp)+2), []string{"thanos_query_concurrent_gate_queries_total"}, e2emon.WaitMissingMetrics())) + + }) + + t.Run("explanation works with instant query", func(t *testing.T) { + _, explanation := instantQuery(t, ctx, queryFrontend.Endpoint("http"), e2ethanos.QueryUpWithoutInstance, time.Now, promclient.QueryOptions{ + Deduplicate: false, + Engine: "thanos", + Explain: true, + }, 1) + testutil.Assert(t, explanation != nil, "expected to have an explanation") + }) + + t.Run("does not return explanation if not needed", func(t *testing.T) { + _, explanation := instantQuery(t, ctx, queryFrontend.Endpoint("http"), e2ethanos.QueryUpWithoutInstance, time.Now, promclient.QueryOptions{ + Deduplicate: false, + Engine: "thanos", + Explain: false, + }, 1) + testutil.Assert(t, explanation == nil, "expected to have no explanation") + }) +} + func TestQueryFrontend(t *testing.T) { t.Parallel() @@ -800,10 +911,10 @@ func TestInstantQueryShardingWithRandomData(t *testing.T) { }, } { t.Run(tc.name, func(t *testing.T) { - resultWithoutSharding := instantQuery(t, ctx, q1.Endpoint("http"), tc.qryFunc, func() time.Time { + resultWithoutSharding, _ := instantQuery(t, ctx, q1.Endpoint("http"), tc.qryFunc, func() time.Time { return now.Time() }, queryOpts, tc.expectedSeries) - resultWithSharding := instantQuery(t, ctx, qfe.Endpoint("http"), tc.qryFunc, func() time.Time { + resultWithSharding, _ := instantQuery(t, ctx, qfe.Endpoint("http"), tc.qryFunc, func() time.Time { return now.Time() }, queryOpts, tc.expectedSeries) testutil.Equals(t, resultWithoutSharding, resultWithSharding) diff --git a/test/e2e/query_test.go b/test/e2e/query_test.go index 7431138fa2..d74c49cc48 100644 --- a/test/e2e/query_test.go +++ b/test/e2e/query_test.go @@ -45,6 +45,7 @@ import ( "github.com/thanos-io/objstore/providers/s3" "github.com/efficientgo/core/testutil" + "github.com/thanos-io/thanos/internal/cortex/querier/queryrange" "github.com/thanos-io/thanos/pkg/api/query/querypb" "github.com/thanos-io/thanos/pkg/block/metadata" "github.com/thanos-io/thanos/pkg/exemplars/exemplarspb" @@ -1363,7 +1364,7 @@ func urlParse(t testing.TB, addr string) *url.URL { return u } -func instantQuery(t testing.TB, ctx context.Context, addr string, q func() string, ts func() time.Time, opts promclient.QueryOptions, expectedSeriesLen int) model.Vector { +func instantQuery(t testing.TB, ctx context.Context, addr string, q func() string, ts func() time.Time, opts promclient.QueryOptions, expectedSeriesLen int) (model.Vector, *queryrange.Explanation) { t.Helper() var result model.Vector @@ -1374,34 +1375,37 @@ func instantQuery(t testing.TB, ctx context.Context, addr string, q func() strin "caller", "instantQuery", "msg", fmt.Sprintf("Waiting for %d results for query %s", expectedSeriesLen, q()), ) + + var explanation *queryrange.Explanation testutil.Ok(t, runutil.RetryWithLog(logger, 5*time.Second, ctx.Done(), func() error { - res, err := simpleInstantQuery(t, ctx, addr, q, ts, opts, expectedSeriesLen) + res, rexplanation, err := simpleInstantQuery(t, ctx, addr, q, ts, opts, expectedSeriesLen) if err != nil { return err } result = res + explanation = rexplanation return nil })) sortResults(result) - return result + return result, explanation } -func simpleInstantQuery(t testing.TB, ctx context.Context, addr string, q func() string, ts func() time.Time, opts promclient.QueryOptions, expectedSeriesLen int) (model.Vector, error) { - res, warnings, err := promclient.NewDefaultClient().QueryInstant(ctx, urlParse(t, "http://"+addr), q(), ts(), opts) +func simpleInstantQuery(t testing.TB, ctx context.Context, addr string, q func() string, ts func() time.Time, opts promclient.QueryOptions, expectedSeriesLen int) (model.Vector, *queryrange.Explanation, error) { + res, warnings, explanation, err := promclient.NewDefaultClient().QueryInstant(ctx, urlParse(t, "http://"+addr), q(), ts(), opts) if err != nil { - return nil, err + return nil, nil, err } if len(warnings) > 0 { - return nil, errors.Errorf("unexpected warnings %s", warnings) + return nil, nil, errors.Errorf("unexpected warnings %s", warnings) } if len(res) != expectedSeriesLen { - return nil, errors.Errorf("unexpected result size, expected %d; result %d: %v", expectedSeriesLen, len(res), res) + return nil, nil, errors.Errorf("unexpected result size, expected %d; result %d: %v", expectedSeriesLen, len(res), res) } sortResults(res) - return res, nil + return res, explanation, nil } func queryWaitAndAssert(t *testing.T, ctx context.Context, addr string, q func() string, ts func() time.Time, opts promclient.QueryOptions, expected model.Vector) { @@ -1416,7 +1420,7 @@ func queryWaitAndAssert(t *testing.T, ctx context.Context, addr string, q func() "msg", fmt.Sprintf("Waiting for %d results for query %s", len(expected), q()), ) testutil.Ok(t, runutil.RetryWithLog(logger, 10*time.Second, ctx.Done(), func() error { - res, warnings, err := promclient.NewDefaultClient().QueryInstant(ctx, urlParse(t, "http://"+addr), q(), ts(), opts) + res, warnings, _, err := promclient.NewDefaultClient().QueryInstant(ctx, urlParse(t, "http://"+addr), q(), ts(), opts) if err != nil { return err } @@ -1447,7 +1451,7 @@ func queryWaitAndAssert(t *testing.T, ctx context.Context, addr string, q func() func queryAndAssertSeries(t *testing.T, ctx context.Context, addr string, q func() string, ts func() time.Time, opts promclient.QueryOptions, expected []model.Metric) { t.Helper() - result := instantQuery(t, ctx, addr, q, ts, opts, len(expected)) + result, _ := instantQuery(t, ctx, addr, q, ts, opts, len(expected)) for i, exp := range expected { testutil.Equals(t, exp, result[i].Metric) } @@ -1457,7 +1461,7 @@ func queryAndAssert(t *testing.T, ctx context.Context, addr string, q func() str t.Helper() sortResults(expected) - result := instantQuery(t, ctx, addr, q, ts, opts, len(expected)) + result, _ := instantQuery(t, ctx, addr, q, ts, opts, len(expected)) for _, r := range result { r.Timestamp = 0 // Does not matter for us. } @@ -1520,13 +1524,14 @@ func series(t *testing.T, ctx context.Context, addr string, matchers []*labels.M } //nolint:unparam -func rangeQuery(t *testing.T, ctx context.Context, addr string, q func() string, start, end, step int64, opts promclient.QueryOptions, check func(res model.Matrix) error) { +func rangeQuery(t *testing.T, ctx context.Context, addr string, q func() string, start, end, step int64, opts promclient.QueryOptions, check func(res model.Matrix) error) *queryrange.Explanation { t.Helper() logger := log.NewLogfmtLogger(os.Stdout) logger = log.With(logger, "ts", log.DefaultTimestampUTC) + var retExplanation *queryrange.Explanation testutil.Ok(t, runutil.RetryWithLog(logger, time.Second, ctx.Done(), func() error { - res, warnings, err := promclient.NewDefaultClient().QueryRange(ctx, urlParse(t, "http://"+addr), q(), start, end, step, opts) + res, warnings, explanation, err := promclient.NewDefaultClient().QueryRange(ctx, urlParse(t, "http://"+addr), q(), start, end, step, opts) if err != nil { return err } @@ -1539,8 +1544,12 @@ func rangeQuery(t *testing.T, ctx context.Context, addr string, q func() string, return errors.Wrap(err, "result check failed") } + retExplanation = explanation + return nil })) + + return retExplanation } func queryExemplars(t *testing.T, ctx context.Context, addr, q string, start, end int64, check func(data []*exemplarspb.ExemplarData) error) { @@ -1837,7 +1846,7 @@ func TestSidecarAlignmentPushdown(t *testing.T) { var expectedRes model.Matrix testutil.Ok(t, runutil.RetryWithLog(logger, time.Second, ctx.Done(), func() error { - res, warnings, err := promclient.NewDefaultClient().QueryRange(ctx, urlParse(t, "http://"+q1.Endpoint("http")), testQuery(), + res, warnings, _, err := promclient.NewDefaultClient().QueryRange(ctx, urlParse(t, "http://"+q1.Endpoint("http")), testQuery(), timestamp.FromTime(now.Add(time.Duration(-7*24)*time.Hour)), timestamp.FromTime(now), 2419, // Taken from UI. @@ -2099,7 +2108,7 @@ func TestConnectedQueriesWithLazyProxy(t *testing.T) { testutil.Ok(t, e2e.StartAndWaitReady(prom, sidecar, querier1, querier2)) testutil.Ok(t, querier2.WaitSumMetricsWithOptions(e2emon.Equals(1), []string{"thanos_store_nodes_grpc_connections"}, e2emon.WaitMissingMetrics())) - result := instantQuery(t, context.Background(), querier2.Endpoint("http"), func() string { + result, _ := instantQuery(t, context.Background(), querier2.Endpoint("http"), func() string { return "sum(up)" }, time.Now, promclient.QueryOptions{}, 1) testutil.Equals(t, model.SampleValue(1.0), result[0].Value) diff --git a/test/e2e/store_gateway_test.go b/test/e2e/store_gateway_test.go index efcf177366..3baf94abdc 100644 --- a/test/e2e/store_gateway_test.go +++ b/test/e2e/store_gateway_test.go @@ -865,7 +865,7 @@ config: t.Run("Series() limits", func(t *testing.T) { testutil.Ok(t, runutil.RetryWithLog(log.NewLogfmtLogger(os.Stdout), 5*time.Second, ctx.Done(), func() error { - if _, _, err := promclient.NewDefaultClient().QueryInstant(ctx, urlParse(t, "http://"+q1.Endpoint("http")), testQuery, time.Now(), opts); err != nil { + if _, _, _, err := promclient.NewDefaultClient().QueryInstant(ctx, urlParse(t, "http://"+q1.Endpoint("http")), testQuery, time.Now(), opts); err != nil { e := err.Error() if strings.Contains(e, "expanded matching posting: get postings") && strings.Contains(e, "exceeded bytes limit while fetching postings: limit 1 violated") { return nil @@ -876,7 +876,7 @@ config: })) testutil.Ok(t, runutil.RetryWithLog(log.NewLogfmtLogger(os.Stdout), 5*time.Second, ctx.Done(), func() error { - if _, _, err := promclient.NewDefaultClient().QueryInstant(ctx, urlParse(t, "http://"+q2.Endpoint("http")), testQuery, time.Now(), opts); err != nil { + if _, _, _, err := promclient.NewDefaultClient().QueryInstant(ctx, urlParse(t, "http://"+q2.Endpoint("http")), testQuery, time.Now(), opts); err != nil { e := err.Error() if strings.Contains(e, "preload series") && strings.Contains(e, "exceeded bytes limit while fetching series: limit 100 violated") { return nil @@ -887,7 +887,7 @@ config: })) testutil.Ok(t, runutil.RetryWithLog(log.NewLogfmtLogger(os.Stdout), 5*time.Second, ctx.Done(), func() error { - if _, _, err := promclient.NewDefaultClient().QueryInstant(ctx, urlParse(t, "http://"+q3.Endpoint("http")), testQuery, time.Now(), opts); err != nil { + if _, _, _, err := promclient.NewDefaultClient().QueryInstant(ctx, urlParse(t, "http://"+q3.Endpoint("http")), testQuery, time.Now(), opts); err != nil { e := err.Error() if strings.Contains(e, "load chunks") && strings.Contains(e, "exceeded bytes limit while fetching chunks: limit 196627 violated") { return nil