From 277faecc41d3fe89acab170dae35321cabb25752 Mon Sep 17 00:00:00 2001 From: Trevor Whitney Date: Fri, 5 Apr 2024 13:01:44 -0600 Subject: [PATCH 01/10] feat: split detected fields queries --- pkg/logproto/logproto.pb.go | 102 +++++++++++++++++++-------- pkg/logproto/logproto.proto | 1 + pkg/querier/queryrange/codec.go | 64 ++++++++++++++++- pkg/querier/queryrange/codec_test.go | 100 +++++++++++++++++++++++++- pkg/querier/queryrange/roundtrip.go | 37 +++++++++- pkg/storage/detected/fields.go | 41 +++++++++++ pkg/storage/detected/fields_test.go | 54 ++++++++++++++ 7 files changed, 365 insertions(+), 34 deletions(-) create mode 100644 pkg/storage/detected/fields.go create mode 100644 pkg/storage/detected/fields_test.go diff --git a/pkg/logproto/logproto.pb.go b/pkg/logproto/logproto.pb.go index c478e47e89c6..a26c1ae0ba2b 100644 --- a/pkg/logproto/logproto.pb.go +++ b/pkg/logproto/logproto.pb.go @@ -2700,6 +2700,7 @@ func (m *DetectedFieldsRequest) GetStep() int64 { type DetectedFieldsResponse struct { Fields []*DetectedField `protobuf:"bytes,1,rep,name=fields,proto3" json:"fields,omitempty"` + Limit uint32 `protobuf:"varint,2,opt,name=limit,proto3" json:"limit,omitempty"` } func (m *DetectedFieldsResponse) Reset() { *m = DetectedFieldsResponse{} } @@ -2741,6 +2742,13 @@ func (m *DetectedFieldsResponse) GetFields() []*DetectedField { return nil } +func (m *DetectedFieldsResponse) GetLimit() uint32 { + if m != nil { + return m.Limit + } + return 0 +} + type DetectedField struct { Label string `protobuf:"bytes,1,opt,name=label,proto3" json:"label,omitempty"` Type DetectedFieldType `protobuf:"bytes,2,opt,name=type,proto3,casttype=DetectedFieldType" json:"type,omitempty"` @@ -3015,7 +3023,7 @@ func init() { func init() { proto.RegisterFile("pkg/logproto/logproto.proto", fileDescriptor_c28a5f14f1f4c79a) } var fileDescriptor_c28a5f14f1f4c79a = []byte{ - // 2569 bytes of a gzipped FileDescriptorProto + // 2572 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x39, 0xcb, 0x6f, 0x1b, 0xc7, 0xf9, 0x5c, 0x72, 0xf9, 0xfa, 0x48, 0xc9, 0xd2, 0x88, 0x96, 0x09, 0xda, 0x21, 0x95, 0xc1, 0xef, 0x97, 0xa8, 0xb1, 0x23, 0xc6, 0x72, 0xed, 0x3a, 0x76, 0xdd, 0xd4, 0x94, 0x62, 0x45, 0xb6, 0xfc, @@ -3148,35 +3156,35 @@ var fileDescriptor_c28a5f14f1f4c79a = []byte{ 0x70, 0x74, 0x95, 0x32, 0xda, 0x64, 0xb4, 0x75, 0xc5, 0xa4, 0x9d, 0xd6, 0x37, 0x5a, 0x3e, 0xfb, 0x4d, 0xb0, 0x54, 0xa8, 0x09, 0xc6, 0xfd, 0x4e, 0xc7, 0xb4, 0xe8, 0x46, 0xa8, 0x8b, 0x12, 0x00, 0xb8, 0x87, 0xd8, 0xe1, 0x17, 0x97, 0xcb, 0xf2, 0xc3, 0x46, 0x08, 0xe2, 0x6b, 0x38, 0x13, 0x68, - 0x18, 0xaf, 0xc3, 0x7c, 0x9c, 0x69, 0xa5, 0xa3, 0x3a, 0x64, 0xc4, 0xde, 0x11, 0xed, 0xd7, 0xc8, - 0x0e, 0xa2, 0xd0, 0xb0, 0x03, 0x53, 0x91, 0x05, 0xa1, 0x33, 0x6e, 0x23, 0xca, 0x7f, 0xca, 0x09, - 0xfa, 0x16, 0xe8, 0x6c, 0xd0, 0x53, 0x6e, 0xb3, 0x71, 0xf4, 0x8b, 0x61, 0x6d, 0x36, 0xb2, 0x6d, - 0x6b, 0xd0, 0xa3, 0x44, 0xa0, 0x70, 0xd3, 0x6a, 0x1a, 0x4e, 0xcb, 0xb4, 0x8c, 0x8e, 0xc9, 0xa4, - 0x28, 0x74, 0x12, 0x06, 0xe1, 0x5f, 0x87, 0x94, 0x26, 0xed, 0xf1, 0x90, 0x4a, 0xd3, 0x0e, 0xad, - 0x34, 0xed, 0x39, 0x4a, 0xc3, 0x3f, 0x08, 0x44, 0xec, 0x5d, 0x51, 0x89, 0xf8, 0x1d, 0x98, 0x6e, - 0x45, 0x56, 0xc6, 0x8b, 0x5a, 0xf6, 0x3e, 0x63, 0xe8, 0x78, 0x2d, 0x10, 0xb9, 0x80, 0x8c, 0x11, - 0x79, 0x4c, 0x8e, 0xc9, 0x03, 0x72, 0x7c, 0xe3, 0x35, 0xc8, 0xfb, 0x5f, 0x88, 0x50, 0x01, 0xb2, - 0x57, 0x6e, 0x92, 0x0f, 0x2f, 0x93, 0xd5, 0x99, 0x04, 0x2a, 0x42, 0xae, 0x71, 0x79, 0xe5, 0x9a, - 0x98, 0x69, 0xcb, 0x9f, 0x64, 0xbc, 0xb0, 0xea, 0xa0, 0xef, 0x42, 0x5a, 0xc6, 0xca, 0xf9, 0xe0, - 0xba, 0xe1, 0x0f, 0x31, 0x95, 0x63, 0x07, 0xe0, 0x92, 0x6f, 0x9c, 0x78, 0x4b, 0x43, 0x37, 0xa0, - 0x20, 0x80, 0xaa, 0x6d, 0x7a, 0x22, 0xde, 0xbd, 0x8c, 0x50, 0x7a, 0x65, 0xcc, 0x6a, 0x88, 0xde, - 0x05, 0x48, 0x4b, 0x11, 0xcc, 0xc7, 0x52, 0x9a, 0x11, 0xb7, 0x89, 0x34, 0x92, 0x71, 0x02, 0xbd, - 0x0d, 0xfa, 0x96, 0x61, 0x76, 0x50, 0x28, 0xa3, 0x0a, 0x75, 0x3b, 0x2b, 0xf3, 0x71, 0x70, 0xe8, - 0xd8, 0x4b, 0x7e, 0xd3, 0xf6, 0x58, 0xbc, 0x73, 0xe4, 0x6d, 0x2f, 0x1f, 0x5c, 0xf0, 0x4f, 0xbe, - 0x29, 0x5b, 0x8b, 0x5e, 0xff, 0x02, 0xbd, 0x12, 0x3d, 0x2a, 0xd6, 0xee, 0xa8, 0x54, 0xc7, 0x2d, - 0xfb, 0x04, 0x37, 0xa0, 0x10, 0xea, 0x1d, 0x84, 0xc5, 0x7a, 0xb0, 0xf1, 0x11, 0x16, 0xeb, 0x88, - 0x86, 0x03, 0x4e, 0xa0, 0x35, 0xc8, 0xf1, 0x3c, 0x54, 0x7c, 0x63, 0x38, 0x1e, 0x4f, 0x37, 0x43, - 0x69, 0x46, 0xe5, 0xc4, 0xe8, 0x45, 0x9f, 0xd0, 0xf7, 0x21, 0xbf, 0x46, 0x99, 0xf2, 0xd5, 0xc7, - 0xe2, 0xce, 0x7e, 0x84, 0xa4, 0xa2, 0x01, 0x03, 0x27, 0xd0, 0x47, 0x22, 0x25, 0x8e, 0xfa, 0x2a, - 0x54, 0x1b, 0xe3, 0x93, 0xfc, 0x7b, 0x2d, 0x8c, 0x47, 0xf0, 0x29, 0x7f, 0x18, 0xa1, 0xac, 0xa2, - 0x5a, 0x6d, 0xcc, 0x13, 0xf4, 0x29, 0xd7, 0x9e, 0xf3, 0xa5, 0x1f, 0x27, 0x96, 0xef, 0x78, 0x1f, - 0xbb, 0x57, 0x0d, 0x66, 0xa0, 0x9b, 0x30, 0x2d, 0x64, 0xe9, 0x7f, 0x0d, 0x8f, 0xd8, 0xfc, 0x81, - 0x4f, 0xef, 0x11, 0x9b, 0x3f, 0xf8, 0x09, 0x1e, 0x27, 0x1a, 0x77, 0x9e, 0x3c, 0xad, 0x26, 0x3e, - 0x7d, 0x5a, 0x4d, 0x7c, 0xfe, 0xb4, 0xaa, 0xfd, 0x78, 0xaf, 0xaa, 0xfd, 0x6e, 0xaf, 0xaa, 0x3d, - 0xde, 0xab, 0x6a, 0x4f, 0xf6, 0xaa, 0xda, 0xbf, 0xf6, 0xaa, 0xda, 0xbf, 0xf7, 0xaa, 0x89, 0xcf, - 0xf7, 0xaa, 0xda, 0xc7, 0xcf, 0xaa, 0x89, 0x27, 0xcf, 0xaa, 0x89, 0x4f, 0x9f, 0x55, 0x13, 0x3f, - 0x7c, 0xfd, 0xf9, 0xe5, 0x9f, 0x74, 0x74, 0x19, 0xf1, 0x77, 0xe6, 0xbf, 0x01, 0x00, 0x00, 0xff, - 0xff, 0x78, 0xeb, 0x77, 0xf7, 0x92, 0x21, 0x00, 0x00, + 0x18, 0xdf, 0x85, 0xf9, 0x38, 0xd3, 0x4a, 0x47, 0x75, 0xc8, 0x88, 0xbd, 0x23, 0xda, 0xaf, 0x91, + 0x1d, 0x44, 0xa1, 0x8d, 0xfe, 0x14, 0x82, 0x1d, 0x98, 0x8a, 0xa0, 0x0b, 0x34, 0x6e, 0x39, 0xca, + 0xab, 0xca, 0x09, 0xfa, 0x16, 0xe8, 0x6c, 0xd0, 0x53, 0xce, 0xb4, 0x71, 0xf4, 0x8b, 0x61, 0x6d, + 0x36, 0xb2, 0x6d, 0x6b, 0xd0, 0xa3, 0x44, 0xa0, 0x70, 0x83, 0x6b, 0x1a, 0x4e, 0xcb, 0xb4, 0x8c, + 0x8e, 0xc9, 0xa4, 0x80, 0x74, 0x12, 0x06, 0xe1, 0x5f, 0x87, 0x54, 0x29, 0xad, 0xf4, 0x90, 0xaa, + 0xd4, 0x0e, 0xad, 0x4a, 0xed, 0x39, 0xaa, 0xc4, 0x3f, 0x08, 0x04, 0xef, 0x5d, 0x51, 0x09, 0xfe, + 0x1d, 0x98, 0x6e, 0x45, 0x56, 0xc6, 0x2b, 0x40, 0x76, 0x44, 0x63, 0xe8, 0x78, 0x2d, 0x10, 0xb9, + 0x80, 0x8c, 0x11, 0x79, 0x4c, 0x8e, 0xc9, 0x03, 0x72, 0x7c, 0xe3, 0x35, 0xc8, 0xfb, 0xdf, 0x8d, + 0x50, 0x01, 0xb2, 0x57, 0x6e, 0x92, 0x0f, 0x2f, 0x93, 0xd5, 0x99, 0x04, 0x2a, 0x42, 0xae, 0x71, + 0x79, 0xe5, 0x9a, 0x98, 0x69, 0xcb, 0x9f, 0x64, 0xbc, 0x60, 0xeb, 0xa0, 0xef, 0x42, 0x5a, 0x46, + 0xd0, 0xf9, 0xe0, 0xba, 0xe1, 0xcf, 0x33, 0x95, 0x63, 0x07, 0xe0, 0x92, 0x6f, 0x9c, 0x78, 0x4b, + 0x43, 0x37, 0xa0, 0x20, 0x80, 0xaa, 0x99, 0x7a, 0x22, 0xde, 0xd3, 0x8c, 0x50, 0x7a, 0x65, 0xcc, + 0x6a, 0x88, 0xde, 0x05, 0x48, 0x4b, 0x11, 0xcc, 0xc7, 0x12, 0x9d, 0x11, 0xb7, 0x89, 0xb4, 0x97, + 0x71, 0x02, 0xbd, 0x0d, 0xfa, 0x96, 0x61, 0x76, 0x50, 0x28, 0xcf, 0x0a, 0xf5, 0x40, 0x2b, 0xf3, + 0x71, 0x70, 0xe8, 0xd8, 0x4b, 0x7e, 0x2b, 0xf7, 0x58, 0xbc, 0x9f, 0xe4, 0x6d, 0x2f, 0x1f, 0x5c, + 0xf0, 0x4f, 0xbe, 0x29, 0x1b, 0x8e, 0x5e, 0x57, 0x03, 0xbd, 0x12, 0x3d, 0x2a, 0xd6, 0x04, 0xa9, + 0x54, 0xc7, 0x2d, 0xfb, 0x04, 0x37, 0xa0, 0x10, 0xea, 0x28, 0x84, 0xc5, 0x7a, 0xb0, 0x1d, 0x12, + 0x16, 0xeb, 0x88, 0x36, 0x04, 0x4e, 0xa0, 0x35, 0xc8, 0xf1, 0xec, 0x54, 0x7c, 0x79, 0x38, 0x1e, + 0x4f, 0x42, 0x43, 0xc9, 0x47, 0xe5, 0xc4, 0xe8, 0x45, 0x9f, 0xd0, 0xf7, 0x21, 0xbf, 0x46, 0x99, + 0xf2, 0xe0, 0xc7, 0xe2, 0x21, 0x60, 0x84, 0xa4, 0xa2, 0x61, 0x04, 0x27, 0xd0, 0x47, 0x22, 0x51, + 0x8e, 0x7a, 0x30, 0x54, 0x1b, 0xe3, 0xa9, 0xfc, 0x7b, 0x2d, 0x8c, 0x47, 0xf0, 0x29, 0x7f, 0x18, + 0xa1, 0xac, 0x62, 0x5d, 0x6d, 0xcc, 0x13, 0xf4, 0x29, 0xd7, 0x9e, 0xf3, 0xfd, 0x1f, 0x27, 0x96, + 0xef, 0x78, 0x9f, 0xc0, 0x57, 0x0d, 0x66, 0xa0, 0x9b, 0x30, 0x2d, 0x64, 0xe9, 0x7f, 0x23, 0x8f, + 0xd8, 0xfc, 0x81, 0x0f, 0xf2, 0x11, 0x9b, 0x3f, 0xf8, 0x61, 0x1e, 0x27, 0x1a, 0x77, 0x9e, 0x3c, + 0xad, 0x26, 0x3e, 0x7d, 0x5a, 0x4d, 0x7c, 0xfe, 0xb4, 0xaa, 0xfd, 0x78, 0xaf, 0xaa, 0xfd, 0x6e, + 0xaf, 0xaa, 0x3d, 0xde, 0xab, 0x6a, 0x4f, 0xf6, 0xaa, 0xda, 0xbf, 0xf6, 0xaa, 0xda, 0xbf, 0xf7, + 0xaa, 0x89, 0xcf, 0xf7, 0xaa, 0xda, 0xc7, 0xcf, 0xaa, 0x89, 0x27, 0xcf, 0xaa, 0x89, 0x4f, 0x9f, + 0x55, 0x13, 0x3f, 0x7c, 0xfd, 0xf9, 0x45, 0xa1, 0x74, 0x74, 0x19, 0xf1, 0x77, 0xe6, 0xbf, 0x01, + 0x00, 0x00, 0xff, 0xff, 0xd7, 0xf4, 0x3e, 0x20, 0xa8, 0x21, 0x00, 0x00, } func (x Direction) String() string { @@ -4800,6 +4808,9 @@ func (this *DetectedFieldsResponse) Equal(that interface{}) bool { return false } } + if this.Limit != that1.Limit { + return false + } return true } func (this *DetectedField) Equal(that interface{}) bool { @@ -5560,11 +5571,12 @@ func (this *DetectedFieldsResponse) GoString() string { if this == nil { return "nil" } - s := make([]string, 0, 5) + s := make([]string, 0, 6) s = append(s, "&logproto.DetectedFieldsResponse{") if this.Fields != nil { s = append(s, "Fields: "+fmt.Sprintf("%#v", this.Fields)+",\n") } + s = append(s, "Limit: "+fmt.Sprintf("%#v", this.Limit)+",\n") s = append(s, "}") return strings.Join(s, "") } @@ -8420,6 +8432,11 @@ func (m *DetectedFieldsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) _ = i var l int _ = l + if m.Limit != 0 { + i = encodeVarintLogproto(dAtA, i, uint64(m.Limit)) + i-- + dAtA[i] = 0x10 + } if len(m.Fields) > 0 { for iNdEx := len(m.Fields) - 1; iNdEx >= 0; iNdEx-- { { @@ -9581,6 +9598,9 @@ func (m *DetectedFieldsResponse) Size() (n int) { n += 1 + l + sovLogproto(uint64(l)) } } + if m.Limit != 0 { + n += 1 + sovLogproto(uint64(m.Limit)) + } return n } @@ -10317,6 +10337,7 @@ func (this *DetectedFieldsResponse) String() string { repeatedStringForFields += "}" s := strings.Join([]string{`&DetectedFieldsResponse{`, `Fields:` + repeatedStringForFields + `,`, + `Limit:` + fmt.Sprintf("%v", this.Limit) + `,`, `}`, }, "") return s @@ -16884,6 +16905,25 @@ func (m *DetectedFieldsResponse) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 2: + 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 ErrIntOverflowLogproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Limit |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } default: iNdEx = preIndex skippy, err := skipLogproto(dAtA[iNdEx:]) diff --git a/pkg/logproto/logproto.proto b/pkg/logproto/logproto.proto index b4986750a2ab..38860f740045 100644 --- a/pkg/logproto/logproto.proto +++ b/pkg/logproto/logproto.proto @@ -452,6 +452,7 @@ message DetectedFieldsRequest { message DetectedFieldsResponse { repeated DetectedField fields = 1; + uint32 limit = 2; } message DetectedField { diff --git a/pkg/querier/queryrange/codec.go b/pkg/querier/queryrange/codec.go index 2d36eafc0a5d..fdcca169f2d4 100644 --- a/pkg/querier/queryrange/codec.go +++ b/pkg/querier/queryrange/codec.go @@ -17,6 +17,7 @@ import ( "golang.org/x/exp/maps" "github.com/grafana/loki/v3/pkg/storage/chunk/cache/resultscache" + "github.com/grafana/loki/v3/pkg/storage/detected" "github.com/grafana/loki/v3/pkg/storage/stores/index/seriesvolume" "github.com/grafana/dskit/httpgrpc" @@ -1541,6 +1542,22 @@ func (Codec) MergeResponse(responses ...queryrangebase.Response) (queryrangebase Response: seriesvolume.Merge(resps, resp0.Response.Limit), Headers: headers, }, nil + case *DetectedFieldsResponse: + resp0 := responses[0].(*DetectedFieldsResponse) + headers := resp0.Headers + limit := resp0.Response.Limit + + fields := []*logproto.DetectedField{} + for _, r := range responses { + fields = append(fields, r.(*DetectedFieldsResponse).Response.Fields...) + } + + return &DetectedFieldsResponse{ + Response: &logproto.DetectedFieldsResponse{ + Fields: detected.MergeFields(fields, limit), + }, + Headers: headers, + }, nil default: return nil, fmt.Errorf("unknown response type (%T) in merging responses", responses[0]) } @@ -1735,8 +1752,12 @@ func ParamsFromRequest(req queryrangebase.Request) (logql.Params, error) { return ¶msStatsWrapper{ IndexStatsRequest: r, }, nil + case *DetectedFieldsRequest: + return ¶msDetectedFieldsWrapper{ + DetectedFieldsRequest: r, + }, nil default: - return nil, fmt.Errorf("expected one of the *LokiRequest, *LokiInstantRequest, *LokiSeriesRequest, *LokiLabelNamesRequest, got (%T)", r) + return nil, fmt.Errorf("expected one of the *LokiRequest, *LokiInstantRequest, *LokiSeriesRequest, *LokiLabelNamesRequest, *DetectedFieldsRequest, got (%T)", r) } } @@ -1904,6 +1925,47 @@ func (p paramsStatsWrapper) Shards() []string { return make([]string, 0) } +type paramsDetectedFieldsWrapper struct { + *DetectedFieldsRequest +} + +func (p paramsDetectedFieldsWrapper) QueryString() string { + return p.GetQuery() +} + +func (p paramsDetectedFieldsWrapper) GetExpression() syntax.Expr { + expr, err := syntax.ParseExpr(p.GetQuery()) + if err != nil { + return nil + } + + return expr +} + +func (p paramsDetectedFieldsWrapper) Start() time.Time { + return p.GetStartTs() +} + +func (p paramsDetectedFieldsWrapper) End() time.Time { + return p.GetEndTs() +} + +func (p paramsDetectedFieldsWrapper) Step() time.Duration { + return time.Duration(p.GetStep() * 1e6) +} + +func (p paramsDetectedFieldsWrapper) Interval() time.Duration { + return 0 +} + +func (p paramsDetectedFieldsWrapper) Direction() logproto.Direction { + return logproto.BACKWARD +} +func (p paramsDetectedFieldsWrapper) Limit() uint32 { return p.DetectedFieldsRequest.LineLimit } +func (p paramsDetectedFieldsWrapper) Shards() []string { + return make([]string, 0) +} + func httpResponseHeadersToPromResponseHeaders(httpHeaders http.Header) []queryrangebase.PrometheusResponseHeader { var promHeaders []queryrangebase.PrometheusResponseHeader for h, hv := range httpHeaders { diff --git a/pkg/querier/queryrange/codec_test.go b/pkg/querier/queryrange/codec_test.go index b334ab5b0691..29c0f6606876 100644 --- a/pkg/querier/queryrange/codec_test.go +++ b/pkg/querier/queryrange/codec_test.go @@ -1013,7 +1013,7 @@ func Test_codec_EncodeResponse(t *testing.T) { }, }, { - "loki legacy", "/api/promt/query", + "loki legacy", "/api/prompt/query", &LokiResponse{ Status: loghttp.QueryStatusSuccess, Direction: logproto.FORWARD, @@ -1622,6 +1622,104 @@ func Test_codec_MergeResponse(t *testing.T) { } } +func Test_codec_MergeResponse_DetectedFieldsResponse(t *testing.T) { + t.Run("merges the responses, taking the highest cardinality", func(t *testing.T) { + responses := []queryrangebase.Response{ + &DetectedFieldsResponse{ + Response: &logproto.DetectedFieldsResponse{ + Fields: []*logproto.DetectedField{{ + Label: "foo", + Type: logproto.DetectedFieldString, + Cardinality: 1, + }}, + Limit: 2, + }, + }, + &DetectedFieldsResponse{ + Response: &logproto.DetectedFieldsResponse{ + Fields: []*logproto.DetectedField{{ + Label: "foo", + Type: logproto.DetectedFieldString, + Cardinality: 3, + }}, + Limit: 2, + }, + }, + } + + got, err := DefaultCodec.MergeResponse(responses...) + require.Nil(t, err) + response := got.(*DetectedFieldsResponse).Response + require.Equal(t, 1, len(response.Fields)) + + foo := response.Fields[0] + require.Equal(t, foo.Label, "foo") + require.Equal(t, foo.Type, logproto.DetectedFieldString) + require.Equal(t, foo.Cardinality, uint64(3)) + }) + + t.Run("merges the responses, enforcing the limit", func(t *testing.T) { + responses := []queryrangebase.Response{ + &DetectedFieldsResponse{ + Response: &logproto.DetectedFieldsResponse{ + Fields: []*logproto.DetectedField{ + { + Label: "foo", + Type: logproto.DetectedFieldString, + Cardinality: 1, + }, + { + Label: "bar", + Type: logproto.DetectedFieldInt, + Cardinality: 42, + }, + }, + Limit: 2, + }, + }, + &DetectedFieldsResponse{ + Response: &logproto.DetectedFieldsResponse{ + Fields: []*logproto.DetectedField{ + { + Label: "foo", + Type: logproto.DetectedFieldString, + Cardinality: 27, + }, { + Label: "baz", + Type: logproto.DetectedFieldBoolean, + Cardinality: 3, + }, + }, + Limit: 2, + }, + }, + } + + got, err := DefaultCodec.MergeResponse(responses...) + require.Nil(t, err) + response := got.(*DetectedFieldsResponse).Response + require.Equal(t, 2, len(response.Fields)) + + var foo *logproto.DetectedField + var baz *logproto.DetectedField + for _, f := range response.Fields { + if f.Label == "foo" { + foo = f + } + if f.Label == "baz" { + baz = f + } + } + + require.Equal(t, foo.Label, "foo") + require.Equal(t, foo.Type, logproto.DetectedFieldString) + require.Equal(t, 27, int(foo.Cardinality)) + + require.Nil(t, baz) + }) + +} + type badResponse struct{} func (badResponse) Reset() {} diff --git a/pkg/querier/queryrange/roundtrip.go b/pkg/querier/queryrange/roundtrip.go index 3d64c50231d0..a3f3a97c7047 100644 --- a/pkg/querier/queryrange/roundtrip.go +++ b/pkg/querier/queryrange/roundtrip.go @@ -235,6 +235,11 @@ func NewMiddleware( return nil, nil, err } + detectedFieldsTripperware, err := NewDetectedFieldsTripperware(cfg, engineOpts, log, limits, schema, codec, iqo, resultsCache, metrics, indexStatsTripperware, metricsNamespace) + if err != nil { + return nil, nil, err + } + return base.MiddlewareFunc(func(next base.Handler) base.Handler { var ( metricRT = metricsTripperware.Wrap(next) @@ -245,7 +250,7 @@ func NewMiddleware( instantRT = instantMetricTripperware.Wrap(next) statsRT = indexStatsTripperware.Wrap(next) seriesVolumeRT = seriesVolumeTripperware.Wrap(next) - detectedFieldsRT = next //TODO(twhitney): add middlewares for detected fields + detectedFieldsRT = detectedFieldsTripperware.Wrap(next) detectedLabelsRT = next // TODO(shantanu): add middlewares ) @@ -1095,3 +1100,33 @@ func sharedIndexTripperware( return base.MergeMiddlewares(middlewares...).Wrap(next) }), nil } + +// NewDetectedFieldsTripperware creates a new frontend tripperware responsible for handling detected field requests, which are basically log filter requests with a bit more processing. +func NewDetectedFieldsTripperware(cfg Config, engineOpts logql.EngineOpts, log log.Logger, limits Limits, schema config.SchemaConfig, merger base.Merger, iqo util.IngesterQueryOptions, c cache.Cache, metrics *Metrics, indexStatsTripperware base.Middleware, metricsNamespace string) (base.Middleware, error) { + return base.MiddlewareFunc(func(next base.Handler) base.Handler { + statsHandler := indexStatsTripperware.Wrap(next) + + queryRangeMiddleware := []base.Middleware{ + StatsCollectorMiddleware(), + NewLimitsMiddleware(limits), + NewQuerySizeLimiterMiddleware(schema.Configs, engineOpts, log, limits, statsHandler), + base.InstrumentMiddleware("split_by_interval", metrics.InstrumentMiddlewareMetrics), + SplitByIntervalMiddleware(schema.Configs, limits, merger, newDefaultSplitter(limits, iqo), metrics.SplitByMetrics), + } + + // The sharding middleware takes care of enforcing this limit for both shardable and non-shardable queries. + // If we are not using sharding, we enforce the limit by adding this middleware after time splitting. + queryRangeMiddleware = append(queryRangeMiddleware, + NewQuerierSizeLimiterMiddleware(schema.Configs, engineOpts, log, limits, statsHandler), + ) + + if cfg.MaxRetries > 0 { + queryRangeMiddleware = append( + queryRangeMiddleware, base.InstrumentMiddleware("retry", metrics.InstrumentMiddlewareMetrics), + base.NewRetryMiddleware(log, cfg.MaxRetries, metrics.RetryMiddlewareMetrics, metricsNamespace), + ) + } + + return NewLimitedRoundTripper(next, limits, schema.Configs, queryRangeMiddleware...) + }), nil +} diff --git a/pkg/storage/detected/fields.go b/pkg/storage/detected/fields.go new file mode 100644 index 000000000000..2292a6dbff56 --- /dev/null +++ b/pkg/storage/detected/fields.go @@ -0,0 +1,41 @@ +package detected + +import ( + "github.com/grafana/loki/v3/pkg/logproto" +) + +func MergeFields(fields []*logproto.DetectedField, limit uint32) []*logproto.DetectedField { + mergedFields := make(map[string]*logproto.DetectedField, limit) + foundFields := uint32(0) + + for _, field := range fields { + if field == nil { + continue + } + + //TODO(twhitney): this will take the first N up to limit, is there a better + //way to rank the fields to make sure we get the most interesting ones? + if _, ok := mergedFields[field.Label]; !ok && foundFields < limit { + mergedFields[field.Label] = field + foundFields++ + continue + } + + //seeing the same field again, update the cardinality if it's higher + //this is an estimation, as the true cardinality could be greater + //than either of the seen values, but will never be less + if f, ok := mergedFields[field.Label]; ok { + curCard, newCard := f.Cardinality, field.Cardinality + if newCard > curCard { + f.Cardinality = newCard + } + } + } + + result := make([]*logproto.DetectedField, 0, limit) + for _, field := range mergedFields { + result = append(result, field) + } + + return result +} diff --git a/pkg/storage/detected/fields_test.go b/pkg/storage/detected/fields_test.go new file mode 100644 index 000000000000..a5958ea1fdbf --- /dev/null +++ b/pkg/storage/detected/fields_test.go @@ -0,0 +1,54 @@ +package detected + +import ( + "testing" + + "github.com/grafana/loki/v3/pkg/logproto" + "github.com/stretchr/testify/assert" +) + +func Test_MergeFields(t *testing.T) { + fields := []*logproto.DetectedField{ + { + Label: "foo", + Type: logproto.DetectedFieldString, + Cardinality: 1, + }, + { + Label: "bar", + Type: logproto.DetectedFieldBoolean, + Cardinality: 2, + }, + { + Label: "foo", + Type: logproto.DetectedFieldString, + Cardinality: 3, + }, + } + + t.Run("merges fields, taking the highest cardinality", func(t *testing.T) { + limit := uint32(3) + result := MergeFields(fields, limit) + assert.Equal(t, 2, len(result)) + var foo *logproto.DetectedField + + for _, field := range result { + if field.Label == "foo" { + foo = field + } + } + + assert.Equal(t, logproto.DetectedFieldString, foo.Type) + assert.Equal(t, uint64(3), foo.Cardinality) + }) + + t.Run("returns up to limit number of fields", func(t *testing.T) { + limit := uint32(1) + result := MergeFields(fields, limit) + assert.Equal(t, 1, len(result)) + + limit = uint32(4) + result = MergeFields(fields, limit) + assert.Equal(t, 2, len(result)) + }) +} From 86ab02e9ceecf3c87965f70a75c6893d7a4116cd Mon Sep 17 00:00:00 2001 From: Trevor Whitney Date: Wed, 10 Apr 2024 18:39:19 -0400 Subject: [PATCH 02/10] fix: limit and step parsing --- pkg/querier/querier.go | 16 +++++++-------- pkg/querier/queryrange/codec.go | 30 +++++++++++++++++++++-------- pkg/querier/queryrange/roundtrip.go | 9 ++++++--- 3 files changed, 36 insertions(+), 19 deletions(-) diff --git a/pkg/querier/querier.go b/pkg/querier/querier.go index f4ff897e5ab8..11143cf2fc23 100644 --- a/pkg/querier/querier.go +++ b/pkg/querier/querier.go @@ -1020,7 +1020,7 @@ func (q *SingleTenantQuerier) DetectedFields(ctx context.Context, req *logproto. // TODO(twhitney): converting from a step to a duration should be abstracted and reused, // doing this in a few places now. - streams, err := streamsForFieldDetection(iters, req.LineLimit, time.Duration(req.Step*1e6)) + streams, err := streamsForFieldDetection(iters, req.LineLimit, time.Duration(req.Step)) if err != nil { return nil, err } @@ -1100,7 +1100,6 @@ func parseDetectedFields(ctx context.Context, limit uint32, streams logqlmodel.S fieldCount := uint32(0) for _, stream := range streams { - level.Debug(spanlogger.FromContext(ctx)).Log( "detected_fields", "true", "msg", fmt.Sprintf("looking for detected fields in stream %d with %d lines", stream.Hash, len(stream.Entries))) @@ -1108,12 +1107,15 @@ func parseDetectedFields(ctx context.Context, limit uint32, streams logqlmodel.S for _, entry := range stream.Entries { detected := parseLine(entry.Line) for k, vals := range detected { - if fieldCount >= limit { - return detectedFields + df, ok := detectedFields[k] + if !ok && fieldCount < limit { + df = newParsedFields() + detectedFields[k] = df + fieldCount++ } - if _, ok := detectedFields[k]; !ok { - detectedFields[k] = newParsedFields() + if df == nil { + continue } for _, v := range vals { @@ -1128,8 +1130,6 @@ func parseDetectedFields(ctx context.Context, limit uint32, streams logqlmodel.S level.Debug(spanlogger.FromContext(ctx)).Log( "detected_fields", "true", "msg", fmt.Sprintf("detected field %s with %d values", k, len(vals))) - - fieldCount++ } } } diff --git a/pkg/querier/queryrange/codec.go b/pkg/querier/queryrange/codec.go index 4aea1bf52fe9..313c280b3164 100644 --- a/pkg/querier/queryrange/codec.go +++ b/pkg/querier/queryrange/codec.go @@ -973,9 +973,12 @@ func (c Codec) EncodeRequest(ctx context.Context, r queryrangebase.Request) (*ht return req.WithContext(ctx), nil case *DetectedFieldsRequest: params := url.Values{ - "start": []string{fmt.Sprintf("%d", request.Start.UnixNano())}, - "end": []string{fmt.Sprintf("%d", request.End.UnixNano())}, - "query": []string{request.GetQuery()}, + "query": []string{request.GetQuery()}, + "start": []string{fmt.Sprintf("%d", request.Start.UnixNano())}, + "end": []string{fmt.Sprintf("%d", request.End.UnixNano())}, + "line_limit": []string{fmt.Sprintf("%d", request.GetLineLimit())}, + "field_limit": []string{fmt.Sprintf("%d", request.GetFieldLimit())}, + "step": []string{fmt.Sprintf("%d", request.GetStep())}, } u := &url.URL{ @@ -2133,12 +2136,15 @@ type DetectedFieldsRequest struct { path string } -func NewDetectedFieldsRequest(start, end time.Time, query, path string) *DetectedFieldsRequest { +func NewDetectedFieldsRequest(start, end time.Time, lineLimit, fieldLimit uint32, step int64, query, path string) *DetectedFieldsRequest { return &DetectedFieldsRequest{ DetectedFieldsRequest: logproto.DetectedFieldsRequest{ - Start: start, - End: end, - Query: query, + Start: start, + End: end, + Query: query, + LineLimit: lineLimit, + FieldLimit: fieldLimit, + Step: step, }, path: path, } @@ -2165,7 +2171,15 @@ func (r *DetectedFieldsRequest) GetStartTs() time.Time { } func (r *DetectedFieldsRequest) GetStep() int64 { - return 0 + return r.Step +} + +func (r *DetectedFieldsRequest) GetLineLimit() uint32 { + return r.LineLimit +} + +func (r *DetectedFieldsRequest) GetFieldLimit() uint32 { + return r.FieldLimit } func (r *DetectedFieldsRequest) Path() string { diff --git a/pkg/querier/queryrange/roundtrip.go b/pkg/querier/queryrange/roundtrip.go index 6d7af01161d0..27da39fccf7f 100644 --- a/pkg/querier/queryrange/roundtrip.go +++ b/pkg/querier/queryrange/roundtrip.go @@ -377,11 +377,14 @@ func (r roundTripper) Do(ctx context.Context, req base.Request) (base.Response, case *DetectedFieldsRequest: level.Info(logger).Log( "msg", "executing query", - "type", "detected fields", - "query", op.Query, + "type", "detected_fields", + "end", op.End, + "field_limit", op.FieldLimit, "length", op.End.Sub(op.Start), + "line_limit", op.LineLimit, + "query", op.Query, "start", op.Start, - "end", op.End, + "step", op.Step, ) return r.detectedFields.Do(ctx, req) From ef7998c6be0ba4cb9c86f24a0834261a6669a780 Mon Sep 17 00:00:00 2001 From: Trevor Whitney Date: Wed, 10 Apr 2024 18:43:13 -0400 Subject: [PATCH 03/10] fix: revert accidental typo fix --- pkg/querier/queryrange/codec_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/querier/queryrange/codec_test.go b/pkg/querier/queryrange/codec_test.go index 29c0f6606876..4e3a950f0300 100644 --- a/pkg/querier/queryrange/codec_test.go +++ b/pkg/querier/queryrange/codec_test.go @@ -1013,7 +1013,7 @@ func Test_codec_EncodeResponse(t *testing.T) { }, }, { - "loki legacy", "/api/prompt/query", + "loki legacy", "/api/promt/query", &LokiResponse{ Status: loghttp.QueryStatusSuccess, Direction: logproto.FORWARD, From afcc922d1c3529be0d2762b8fff3c8842872c24f Mon Sep 17 00:00:00 2001 From: Trevor Whitney Date: Wed, 10 Apr 2024 18:52:50 -0400 Subject: [PATCH 04/10] chore: remove unnecessary extra map lookup --- pkg/storage/detected/fields.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pkg/storage/detected/fields.go b/pkg/storage/detected/fields.go index 2292a6dbff56..cda3a318e7d6 100644 --- a/pkg/storage/detected/fields.go +++ b/pkg/storage/detected/fields.go @@ -15,7 +15,8 @@ func MergeFields(fields []*logproto.DetectedField, limit uint32) []*logproto.Det //TODO(twhitney): this will take the first N up to limit, is there a better //way to rank the fields to make sure we get the most interesting ones? - if _, ok := mergedFields[field.Label]; !ok && foundFields < limit { + f, ok := mergedFields[field.Label] + if !ok && foundFields < limit { mergedFields[field.Label] = field foundFields++ continue @@ -24,7 +25,7 @@ func MergeFields(fields []*logproto.DetectedField, limit uint32) []*logproto.Det //seeing the same field again, update the cardinality if it's higher //this is an estimation, as the true cardinality could be greater //than either of the seen values, but will never be less - if f, ok := mergedFields[field.Label]; ok { + if ok { curCard, newCard := f.Cardinality, field.Cardinality if newCard > curCard { f.Cardinality = newCard From 6328566c0ae28f5a50ba262e8a0252e5efe196f2 Mon Sep 17 00:00:00 2001 From: Trevor Whitney Date: Thu, 11 Apr 2024 09:54:58 -0400 Subject: [PATCH 05/10] fix: remove unused param --- pkg/querier/queryrange/roundtrip.go | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/pkg/querier/queryrange/roundtrip.go b/pkg/querier/queryrange/roundtrip.go index 27da39fccf7f..228f20a51405 100644 --- a/pkg/querier/queryrange/roundtrip.go +++ b/pkg/querier/queryrange/roundtrip.go @@ -235,7 +235,17 @@ func NewMiddleware( return nil, nil, err } - detectedFieldsTripperware, err := NewDetectedFieldsTripperware(cfg, engineOpts, log, limits, schema, codec, iqo, resultsCache, metrics, indexStatsTripperware, metricsNamespace) + detectedFieldsTripperware, err := NewDetectedFieldsTripperware( + cfg, + engineOpts, + log, + limits, + schema, + codec, + iqo, + metrics, + indexStatsTripperware, + metricsNamespace) if err != nil { return nil, nil, err } @@ -1107,7 +1117,18 @@ func sharedIndexTripperware( } // NewDetectedFieldsTripperware creates a new frontend tripperware responsible for handling detected field requests, which are basically log filter requests with a bit more processing. -func NewDetectedFieldsTripperware(cfg Config, engineOpts logql.EngineOpts, log log.Logger, limits Limits, schema config.SchemaConfig, merger base.Merger, iqo util.IngesterQueryOptions, c cache.Cache, metrics *Metrics, indexStatsTripperware base.Middleware, metricsNamespace string) (base.Middleware, error) { +func NewDetectedFieldsTripperware( + cfg Config, + engineOpts logql.EngineOpts, + log log.Logger, + limits Limits, + schema config.SchemaConfig, + merger base.Merger, + iqo util.IngesterQueryOptions, + metrics *Metrics, + indexStatsTripperware base.Middleware, + metricsNamespace string, +) (base.Middleware, error) { return base.MiddlewareFunc(func(next base.Handler) base.Handler { statsHandler := indexStatsTripperware.Wrap(next) From bceb81bc6fb2b875075f5b4812af6b065725e81b Mon Sep 17 00:00:00 2001 From: Trevor Whitney Date: Thu, 11 Apr 2024 16:15:33 -0400 Subject: [PATCH 06/10] fix: formatting --- pkg/storage/detected/fields_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pkg/storage/detected/fields_test.go b/pkg/storage/detected/fields_test.go index a5958ea1fdbf..694491244731 100644 --- a/pkg/storage/detected/fields_test.go +++ b/pkg/storage/detected/fields_test.go @@ -3,8 +3,9 @@ package detected import ( "testing" - "github.com/grafana/loki/v3/pkg/logproto" "github.com/stretchr/testify/assert" + + "github.com/grafana/loki/v3/pkg/logproto" ) func Test_MergeFields(t *testing.T) { From d8ca580fe611d3ceb942db707afdbab982c62e8d Mon Sep 17 00:00:00 2001 From: Trevor Whitney Date: Tue, 16 Apr 2024 13:51:33 -0600 Subject: [PATCH 07/10] feat(detected_fields): merge sketches for more accurate cardinality --- cmd/loki/loki-local-config.yaml | 3 + pkg/ingester/ingester.go | 3 +- pkg/logproto/logproto.pb.go | 415 +++++++++++--------- pkg/logproto/logproto.proto | 5 +- pkg/loki/modules.go | 7 +- pkg/lokifrontend/frontend/v2/frontend.go | 8 + pkg/querier/http.go | 3 +- pkg/querier/multi_tenant_querier.go | 3 +- pkg/querier/querier.go | 18 +- pkg/querier/queryrange/codec.go | 16 +- pkg/querier/queryrange/split_by_interval.go | 5 + pkg/querier/queryrange/splitters.go | 14 + pkg/storage/detected/fields.go | 79 +++- 13 files changed, 378 insertions(+), 201 deletions(-) diff --git a/cmd/loki/loki-local-config.yaml b/cmd/loki/loki-local-config.yaml index e448dfd9f1fa..03b579647753 100644 --- a/cmd/loki/loki-local-config.yaml +++ b/cmd/loki/loki-local-config.yaml @@ -36,6 +36,9 @@ schema_config: ruler: alertmanager_url: http://localhost:9093 +frontend: + encoding: protobuf + # By default, Loki will send anonymous, but uniquely-identifiable usage and configuration # analytics to Grafana Labs. These statistics are sent to https://stats.grafana.org/ # diff --git a/pkg/ingester/ingester.go b/pkg/ingester/ingester.go index f99c7fe51b97..bdc90e452ba6 100644 --- a/pkg/ingester/ingester.go +++ b/pkg/ingester/ingester.go @@ -1365,7 +1365,7 @@ func adjustQueryStartTime(maxLookBackPeriod time.Duration, start, now time.Time) return start } -func (i *Ingester) GetDetectedFields(_ context.Context, _ *logproto.DetectedFieldsRequest) (*logproto.DetectedFieldsResponse, error) { +func (i *Ingester) GetDetectedFields(_ context.Context, r *logproto.DetectedFieldsRequest) (*logproto.DetectedFieldsResponse, error) { return &logproto.DetectedFieldsResponse{ Fields: []*logproto.DetectedField{ { @@ -1374,6 +1374,7 @@ func (i *Ingester) GetDetectedFields(_ context.Context, _ *logproto.DetectedFiel Cardinality: 1, }, }, + FieldLimit: r.GetFieldLimit(), }, nil } diff --git a/pkg/logproto/logproto.pb.go b/pkg/logproto/logproto.pb.go index a26c1ae0ba2b..300fd59539b9 100644 --- a/pkg/logproto/logproto.pb.go +++ b/pkg/logproto/logproto.pb.go @@ -2699,8 +2699,8 @@ func (m *DetectedFieldsRequest) GetStep() int64 { } type DetectedFieldsResponse struct { - Fields []*DetectedField `protobuf:"bytes,1,rep,name=fields,proto3" json:"fields,omitempty"` - Limit uint32 `protobuf:"varint,2,opt,name=limit,proto3" json:"limit,omitempty"` + Fields []*DetectedField `protobuf:"bytes,1,rep,name=fields,proto3" json:"fields,omitempty"` + FieldLimit uint32 `protobuf:"varint,2,opt,name=fieldLimit,proto3" json:"fieldLimit,omitempty"` } func (m *DetectedFieldsResponse) Reset() { *m = DetectedFieldsResponse{} } @@ -2742,17 +2742,20 @@ func (m *DetectedFieldsResponse) GetFields() []*DetectedField { return nil } -func (m *DetectedFieldsResponse) GetLimit() uint32 { +func (m *DetectedFieldsResponse) GetFieldLimit() uint32 { if m != nil { - return m.Limit + return m.FieldLimit } return 0 } +// TODO: make the detected field include the serialized sketch +// we only want cardinality in the JSON response type DetectedField struct { Label string `protobuf:"bytes,1,opt,name=label,proto3" json:"label,omitempty"` Type DetectedFieldType `protobuf:"bytes,2,opt,name=type,proto3,casttype=DetectedFieldType" json:"type,omitempty"` Cardinality uint64 `protobuf:"varint,3,opt,name=cardinality,proto3" json:"cardinality,omitempty"` + Sketch []byte `protobuf:"bytes,4,opt,name=sketch,proto3" json:"-"` } func (m *DetectedField) Reset() { *m = DetectedField{} } @@ -2808,6 +2811,13 @@ func (m *DetectedField) GetCardinality() uint64 { return 0 } +func (m *DetectedField) GetSketch() []byte { + if m != nil { + return m.Sketch + } + return nil +} + type DetectedLabelsRequest struct { Start *time.Time `protobuf:"bytes,1,opt,name=start,proto3,stdtime" json:"start,omitempty"` End *time.Time `protobuf:"bytes,2,opt,name=end,proto3,stdtime" json:"end,omitempty"` @@ -3023,168 +3033,169 @@ func init() { func init() { proto.RegisterFile("pkg/logproto/logproto.proto", fileDescriptor_c28a5f14f1f4c79a) } var fileDescriptor_c28a5f14f1f4c79a = []byte{ - // 2572 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x39, 0xcb, 0x6f, 0x1b, 0xc7, - 0xf9, 0x5c, 0x72, 0xf9, 0xfa, 0x48, 0xc9, 0xd2, 0x88, 0x96, 0x09, 0xda, 0x21, 0x95, 0xc1, 0xef, - 0x97, 0xa8, 0xb1, 0x23, 0xc6, 0x72, 0xed, 0x3a, 0x76, 0xdd, 0xd4, 0x94, 0x62, 0x45, 0xb6, 0xfc, - 0xc8, 0x48, 0x71, 0xd2, 0xa2, 0x86, 0xb1, 0x22, 0x47, 0xd4, 0xc2, 0xe4, 0x2e, 0xbd, 0x3b, 0xb4, - 0xcd, 0x5b, 0xff, 0x81, 0xa2, 0x01, 0x7a, 0x68, 0x7b, 0x29, 0x50, 0xa0, 0x40, 0x8b, 0x14, 0xbd, - 0x14, 0x3d, 0x16, 0xed, 0xa5, 0x07, 0xf7, 0xe6, 0xde, 0x82, 0x1c, 0xd8, 0x5a, 0xbe, 0x14, 0x3a, - 0x05, 0xe8, 0x2d, 0xa7, 0x62, 0x1e, 0xfb, 0x14, 0x59, 0x87, 0x8a, 0x83, 0xc0, 0x17, 0x72, 0xe6, - 0x9b, 0x6f, 0xbe, 0x99, 0xef, 0x31, 0xdf, 0x6b, 0xe1, 0x78, 0xef, 0x5e, 0xbb, 0xde, 0xb1, 0xdb, - 0x3d, 0xc7, 0x66, 0xb6, 0x3f, 0x58, 0x12, 0xbf, 0x28, 0xe7, 0xcd, 0x2b, 0xa5, 0xb6, 0xdd, 0xb6, - 0x25, 0x0e, 0x1f, 0xc9, 0xf5, 0x4a, 0xad, 0x6d, 0xdb, 0xed, 0x0e, 0xad, 0x8b, 0xd9, 0x76, 0x7f, - 0xa7, 0xce, 0xcc, 0x2e, 0x75, 0x99, 0xd1, 0xed, 0x29, 0x84, 0x05, 0x45, 0xfd, 0x7e, 0xa7, 0x6b, - 0xb7, 0x68, 0xa7, 0xee, 0x32, 0x83, 0xb9, 0xf2, 0x57, 0x61, 0xcc, 0x71, 0x8c, 0x5e, 0xdf, 0xdd, - 0x15, 0x3f, 0x12, 0x88, 0xff, 0xa4, 0xc1, 0xd1, 0x0d, 0x63, 0x9b, 0x76, 0xb6, 0xec, 0xdb, 0x46, - 0xa7, 0x4f, 0x5d, 0x42, 0xdd, 0x9e, 0x6d, 0xb9, 0x14, 0xad, 0x40, 0xa6, 0xc3, 0x17, 0xdc, 0xb2, - 0xb6, 0x90, 0x5a, 0x2c, 0x2c, 0x9f, 0x5c, 0xf2, 0xaf, 0x3c, 0x72, 0x83, 0x84, 0xba, 0xef, 0x5a, - 0xcc, 0x19, 0x10, 0xb5, 0xb5, 0x72, 0x1b, 0x0a, 0x21, 0x30, 0x9a, 0x81, 0xd4, 0x3d, 0x3a, 0x28, - 0x6b, 0x0b, 0xda, 0x62, 0x9e, 0xf0, 0x21, 0x3a, 0x0d, 0xe9, 0x07, 0x9c, 0x4c, 0x39, 0xb9, 0xa0, - 0x2d, 0x16, 0x96, 0x8f, 0x07, 0x87, 0x7c, 0x60, 0x99, 0xf7, 0xfb, 0x54, 0xec, 0x56, 0x07, 0x49, - 0xcc, 0x0b, 0xc9, 0xf3, 0x1a, 0x3e, 0x09, 0xb3, 0x07, 0xd6, 0xd1, 0x3c, 0x64, 0x04, 0x86, 0xbc, - 0x71, 0x9e, 0xa8, 0x19, 0x2e, 0x01, 0xda, 0x64, 0x0e, 0x35, 0xba, 0xc4, 0x60, 0xfc, 0xbe, 0xf7, - 0xfb, 0xd4, 0x65, 0xf8, 0x3a, 0xcc, 0x45, 0xa0, 0x8a, 0xed, 0x73, 0x50, 0x70, 0x03, 0xb0, 0xe2, - 0xbd, 0x14, 0x5c, 0x2b, 0xd8, 0x43, 0xc2, 0x88, 0xf8, 0x57, 0x1a, 0x40, 0xb0, 0x86, 0xaa, 0x00, - 0x72, 0xf5, 0x3d, 0xc3, 0xdd, 0x15, 0x0c, 0xeb, 0x24, 0x04, 0x41, 0xa7, 0x60, 0x36, 0x98, 0xdd, - 0xb0, 0x37, 0x77, 0x0d, 0xa7, 0x25, 0x64, 0xa0, 0x93, 0x83, 0x0b, 0x08, 0x81, 0xee, 0x18, 0x8c, - 0x96, 0x53, 0x0b, 0xda, 0x62, 0x8a, 0x88, 0x31, 0xe7, 0x96, 0x51, 0xcb, 0xb0, 0x58, 0x59, 0x17, - 0xe2, 0x54, 0x33, 0x0e, 0xe7, 0xfa, 0xa5, 0x6e, 0x39, 0xbd, 0xa0, 0x2d, 0x4e, 0x11, 0x35, 0xc3, - 0x9f, 0xa4, 0xa0, 0xf8, 0x7e, 0x9f, 0x3a, 0x03, 0x25, 0x00, 0x54, 0x85, 0x9c, 0x4b, 0x3b, 0xb4, - 0xc9, 0x6c, 0x47, 0x6a, 0xa4, 0x91, 0x2c, 0x6b, 0xc4, 0x87, 0xa1, 0x12, 0xa4, 0x3b, 0x66, 0xd7, - 0x64, 0xe2, 0x5a, 0x53, 0x44, 0x4e, 0xd0, 0x05, 0x48, 0xbb, 0xcc, 0x70, 0x98, 0xb8, 0x4b, 0x61, - 0xb9, 0xb2, 0x24, 0x0d, 0x73, 0xc9, 0x33, 0xcc, 0xa5, 0x2d, 0xcf, 0x30, 0x1b, 0xb9, 0xc7, 0xc3, - 0x5a, 0xe2, 0xe3, 0x7f, 0xd6, 0x34, 0x22, 0xb7, 0xa0, 0x73, 0x90, 0xa2, 0x56, 0x4b, 0xdc, 0xf7, - 0xcb, 0xee, 0xe4, 0x1b, 0xd0, 0x69, 0xc8, 0xb7, 0x4c, 0x87, 0x36, 0x99, 0x69, 0x5b, 0x82, 0xab, - 0xe9, 0xe5, 0xb9, 0x40, 0x23, 0xab, 0xde, 0x12, 0x09, 0xb0, 0xd0, 0x29, 0xc8, 0xb8, 0x5c, 0x74, - 0x6e, 0x39, 0xcb, 0x6d, 0xa1, 0x51, 0xda, 0x1f, 0xd6, 0x66, 0x24, 0xe4, 0x94, 0xdd, 0x35, 0x19, - 0xed, 0xf6, 0xd8, 0x80, 0x28, 0x1c, 0xf4, 0x06, 0x64, 0x5b, 0xb4, 0x43, 0xb9, 0xc2, 0x73, 0x42, - 0xe1, 0x33, 0x21, 0xf2, 0x62, 0x81, 0x78, 0x08, 0xe8, 0x0e, 0xe8, 0xbd, 0x8e, 0x61, 0x95, 0xf3, - 0x82, 0x8b, 0xe9, 0x00, 0xf1, 0x56, 0xc7, 0xb0, 0x1a, 0x6f, 0x7f, 0x36, 0xac, 0x9d, 0x6d, 0x9b, - 0x6c, 0xb7, 0xbf, 0xbd, 0xd4, 0xb4, 0xbb, 0xf5, 0xb6, 0x63, 0xec, 0x18, 0x96, 0x51, 0xef, 0xd8, - 0xf7, 0xcc, 0xfa, 0x83, 0x33, 0x75, 0xfe, 0x06, 0xef, 0xf7, 0xa9, 0x63, 0x52, 0xa7, 0xce, 0xc9, - 0x2c, 0x09, 0x95, 0xf0, 0xad, 0x44, 0x90, 0xbd, 0xaa, 0xe7, 0x32, 0x33, 0x59, 0xfc, 0x34, 0x09, - 0x68, 0xd3, 0xe8, 0xf6, 0x3a, 0x74, 0x22, 0x95, 0xf9, 0xca, 0x49, 0x1e, 0x5a, 0x39, 0xa9, 0x49, - 0x95, 0x13, 0x48, 0x5a, 0x9f, 0x4c, 0xd2, 0xe9, 0x2f, 0x2b, 0xe9, 0xcc, 0xd7, 0x22, 0x69, 0x5c, - 0x06, 0x9d, 0xcf, 0xb8, 0x53, 0x72, 0x8c, 0x87, 0x42, 0x9e, 0x45, 0xc2, 0x87, 0x78, 0x03, 0x32, - 0xf2, 0x2e, 0xa8, 0x12, 0x17, 0x78, 0xf4, 0x7d, 0x04, 0xc2, 0x4e, 0x79, 0x62, 0x9c, 0x09, 0xc4, - 0x98, 0x12, 0x02, 0xc2, 0x7f, 0xd6, 0x60, 0x4a, 0x69, 0x51, 0xf9, 0x98, 0x6d, 0xc8, 0xca, 0x37, - 0xee, 0xf9, 0x97, 0x63, 0x71, 0xff, 0x72, 0xb9, 0x65, 0xf4, 0x18, 0x75, 0x1a, 0xf5, 0xc7, 0xc3, - 0x9a, 0xf6, 0xd9, 0xb0, 0xf6, 0xfa, 0x38, 0x46, 0x3d, 0x9f, 0xee, 0xf9, 0x25, 0x8f, 0x30, 0x3a, - 0x29, 0x6e, 0xc7, 0x5c, 0x65, 0x0a, 0x47, 0x96, 0x64, 0x28, 0x58, 0xb7, 0xda, 0xd4, 0xe5, 0x94, - 0x75, 0xae, 0x45, 0x22, 0x71, 0x38, 0x9b, 0x0f, 0x0d, 0xc7, 0x32, 0xad, 0xb6, 0x5b, 0x4e, 0x09, - 0xdf, 0xe9, 0xcf, 0xf1, 0x2f, 0x34, 0x98, 0x8b, 0x98, 0xa2, 0x62, 0xe2, 0x3c, 0x64, 0x5c, 0x2e, - 0x5d, 0x8f, 0x87, 0x90, 0x22, 0x37, 0x05, 0xbc, 0x31, 0xad, 0x2e, 0x9f, 0x91, 0x73, 0xa2, 0xf0, - 0x5f, 0xdc, 0xd5, 0xfe, 0xa6, 0x41, 0x51, 0x04, 0x00, 0xef, 0x7d, 0x20, 0xd0, 0x2d, 0xa3, 0x4b, - 0x95, 0xaa, 0xc4, 0x38, 0x14, 0x15, 0xf8, 0x71, 0x39, 0x2f, 0x2a, 0x4c, 0xea, 0xc8, 0xb4, 0x43, - 0x3b, 0x32, 0x2d, 0x78, 0x2b, 0x25, 0x48, 0x73, 0x93, 0x1c, 0x08, 0x27, 0x96, 0x27, 0x72, 0x82, - 0x5f, 0x87, 0x29, 0xc5, 0x85, 0x12, 0xed, 0xb8, 0x40, 0xd6, 0x85, 0x8c, 0xd4, 0x04, 0xfa, 0x3f, - 0xc8, 0xfb, 0x09, 0x80, 0xe0, 0x36, 0xd5, 0xc8, 0xec, 0x0f, 0x6b, 0x49, 0xe6, 0x92, 0x60, 0x01, - 0xd5, 0xc2, 0xc1, 0x55, 0x6b, 0xe4, 0xf7, 0x87, 0x35, 0x09, 0x50, 0xa1, 0x14, 0x9d, 0x00, 0x7d, - 0x97, 0xc7, 0x27, 0x2e, 0x02, 0xbd, 0x91, 0xdb, 0x1f, 0xd6, 0xc4, 0x9c, 0x88, 0x5f, 0xbc, 0x06, - 0xc5, 0x0d, 0xda, 0x36, 0x9a, 0x03, 0x75, 0x68, 0xc9, 0x23, 0xc7, 0x0f, 0xd4, 0x3c, 0x1a, 0xaf, - 0x42, 0xd1, 0x3f, 0xf1, 0x6e, 0xd7, 0x55, 0xaf, 0xa1, 0xe0, 0xc3, 0xae, 0xbb, 0xf8, 0x97, 0x1a, - 0x28, 0x1b, 0x40, 0x38, 0x94, 0x55, 0x70, 0xff, 0x05, 0xfb, 0xc3, 0x9a, 0x82, 0x78, 0x49, 0x03, - 0xba, 0x08, 0x59, 0x57, 0x9c, 0xc8, 0x89, 0xc5, 0x4d, 0x4b, 0x2c, 0x34, 0x8e, 0x70, 0x13, 0xd9, - 0x1f, 0xd6, 0x3c, 0x44, 0xe2, 0x0d, 0xd0, 0x52, 0x24, 0xf0, 0x4a, 0xc6, 0xa6, 0xf7, 0x87, 0xb5, - 0x10, 0x34, 0x1c, 0x88, 0xf1, 0x17, 0x1a, 0x14, 0xb6, 0x0c, 0xd3, 0x37, 0xa1, 0xb2, 0xa7, 0xa2, - 0xc0, 0xbf, 0x4a, 0x00, 0xb7, 0xc4, 0x16, 0xed, 0x18, 0x83, 0x2b, 0xb6, 0x23, 0xe8, 0x4e, 0x11, - 0x7f, 0x1e, 0xc4, 0x4a, 0x7d, 0x64, 0xac, 0x4c, 0x4f, 0xee, 0x8e, 0xbf, 0x5e, 0xe7, 0x77, 0x55, - 0xcf, 0x25, 0x67, 0x52, 0xf8, 0x0f, 0x1a, 0x14, 0x25, 0xf3, 0xca, 0xf2, 0x7e, 0x04, 0x19, 0x29, - 0x1b, 0xc1, 0xfe, 0xff, 0x70, 0x4c, 0x27, 0x27, 0x71, 0x4a, 0x8a, 0x26, 0x7a, 0x07, 0xa6, 0x5b, - 0x8e, 0xdd, 0xeb, 0xd1, 0xd6, 0xa6, 0x72, 0x7f, 0xc9, 0xb8, 0xfb, 0x5b, 0x0d, 0xaf, 0x93, 0x18, - 0x3a, 0xfe, 0xbb, 0x06, 0x53, 0xca, 0x99, 0x28, 0x75, 0xf9, 0x22, 0xd6, 0x0e, 0x1d, 0xf1, 0x92, - 0x93, 0x46, 0xbc, 0x79, 0xc8, 0xb4, 0x1d, 0xbb, 0xdf, 0xf3, 0x1c, 0x92, 0x9a, 0x4d, 0x16, 0x09, - 0xf1, 0x55, 0x98, 0xf6, 0x58, 0x19, 0xe3, 0x51, 0x2b, 0x71, 0x8f, 0xba, 0xde, 0xa2, 0x16, 0x33, - 0x77, 0x4c, 0xdf, 0x47, 0x2a, 0x7c, 0xfc, 0x53, 0x0d, 0x66, 0xe2, 0x28, 0x68, 0x35, 0x96, 0xc0, - 0xbf, 0x36, 0x9e, 0x5c, 0x38, 0x77, 0xf7, 0x48, 0xab, 0x0c, 0xfe, 0xec, 0xf3, 0x32, 0xf8, 0x52, - 0xd8, 0xc9, 0xe4, 0x95, 0x57, 0xc0, 0x3f, 0xd7, 0x60, 0x2a, 0xa2, 0x4b, 0x74, 0x1e, 0xf4, 0x1d, - 0xc7, 0xee, 0x4e, 0xa4, 0x28, 0xb1, 0x03, 0x7d, 0x1b, 0x92, 0xcc, 0x9e, 0x48, 0x4d, 0x49, 0x66, - 0x73, 0x2d, 0x29, 0xf6, 0x53, 0x32, 0x3f, 0x96, 0x33, 0x7c, 0x16, 0xf2, 0x82, 0xa1, 0x5b, 0x86, - 0xe9, 0x8c, 0x0c, 0x18, 0xa3, 0x19, 0xba, 0x08, 0x47, 0xa4, 0x33, 0x1c, 0xbd, 0xb9, 0x38, 0x6a, - 0x73, 0xd1, 0xdb, 0x7c, 0x1c, 0xd2, 0x2b, 0xbb, 0x7d, 0xeb, 0x1e, 0xdf, 0xd2, 0x32, 0x98, 0xe1, - 0x6d, 0xe1, 0x63, 0x7c, 0x14, 0xe6, 0xf8, 0x1b, 0xa4, 0x8e, 0xbb, 0x62, 0xf7, 0x2d, 0xe6, 0xd5, - 0x27, 0xa7, 0xa0, 0x14, 0x05, 0x2b, 0x2b, 0x29, 0x41, 0xba, 0xc9, 0x01, 0x82, 0xc6, 0x14, 0x91, - 0x13, 0xfc, 0x1b, 0x0d, 0xd0, 0x1a, 0x65, 0xe2, 0x94, 0xf5, 0x55, 0xff, 0x79, 0x54, 0x20, 0xd7, - 0x35, 0x58, 0x73, 0x97, 0x3a, 0xae, 0x97, 0xbf, 0x78, 0xf3, 0x6f, 0x22, 0x59, 0xc4, 0xa7, 0x61, - 0x2e, 0x72, 0x4b, 0xc5, 0x53, 0x05, 0x72, 0x4d, 0x05, 0x53, 0x21, 0xcf, 0x9f, 0xe3, 0x3f, 0x26, - 0x21, 0x27, 0x36, 0x10, 0xba, 0x83, 0x4e, 0x43, 0x61, 0xc7, 0xb4, 0xda, 0xd4, 0xe9, 0x39, 0xa6, - 0x12, 0x81, 0xde, 0x38, 0xb2, 0x3f, 0xac, 0x85, 0xc1, 0x24, 0x3c, 0x41, 0x6f, 0x42, 0xb6, 0xef, - 0x52, 0xe7, 0xae, 0x29, 0x5f, 0x7a, 0xbe, 0x51, 0xda, 0x1b, 0xd6, 0x32, 0x1f, 0xb8, 0xd4, 0x59, - 0x5f, 0xe5, 0xc1, 0xa7, 0x2f, 0x46, 0x44, 0xfe, 0xb7, 0xd0, 0x35, 0x65, 0xa6, 0x22, 0x81, 0x6b, - 0x7c, 0x87, 0x5f, 0x3f, 0xe6, 0xea, 0x7a, 0x8e, 0xdd, 0xa5, 0x6c, 0x97, 0xf6, 0xdd, 0x7a, 0xd3, - 0xee, 0x76, 0x6d, 0xab, 0x2e, 0x2a, 0x6e, 0xc1, 0x34, 0x8f, 0xa0, 0x7c, 0xbb, 0xb2, 0xdc, 0x2d, - 0xc8, 0xb2, 0x5d, 0xc7, 0xee, 0xb7, 0x77, 0x45, 0x60, 0x48, 0x35, 0x2e, 0x4c, 0x4e, 0xcf, 0xa3, - 0x40, 0xbc, 0x01, 0x7a, 0x95, 0x4b, 0x8b, 0x36, 0xef, 0xb9, 0xfd, 0xae, 0xac, 0xf1, 0x1a, 0xe9, - 0xfd, 0x61, 0x4d, 0x7b, 0x93, 0xf8, 0x60, 0xfc, 0x93, 0x24, 0xd4, 0x42, 0xa5, 0xf1, 0x15, 0xdb, - 0xb9, 0x4e, 0x99, 0x63, 0x36, 0x6f, 0x18, 0x5d, 0xea, 0xd9, 0x46, 0x0d, 0x0a, 0x5d, 0x01, 0xbc, - 0x1b, 0x7a, 0x02, 0xd0, 0xf5, 0xf1, 0xd0, 0x2b, 0x00, 0xe2, 0xcd, 0xc8, 0x75, 0xf9, 0x1a, 0xf2, - 0x02, 0x22, 0x96, 0x57, 0x22, 0x92, 0xaa, 0x4f, 0xc8, 0x99, 0x92, 0xd0, 0x7a, 0x5c, 0x42, 0x13, - 0xd3, 0xf1, 0xc5, 0x12, 0xb6, 0xf5, 0x74, 0xd4, 0xd6, 0xf1, 0x3f, 0x34, 0xa8, 0x6e, 0x78, 0x37, - 0x3f, 0xa4, 0x38, 0x3c, 0x7e, 0x93, 0x2f, 0x88, 0xdf, 0xd4, 0x57, 0xe3, 0x17, 0x57, 0x01, 0x36, - 0x4c, 0x8b, 0x5e, 0x31, 0x3b, 0x8c, 0x3a, 0x23, 0xaa, 0x98, 0x9f, 0xa5, 0x02, 0x97, 0x40, 0xe8, - 0x8e, 0xc7, 0xe7, 0x4a, 0xc8, 0x0f, 0xbf, 0x08, 0x36, 0x92, 0x2f, 0x50, 0x6d, 0xa9, 0x98, 0x8b, - 0xb2, 0x20, 0xbb, 0x23, 0xd8, 0x93, 0x21, 0x35, 0xd2, 0x88, 0x09, 0x78, 0x6f, 0x7c, 0x4f, 0x1d, - 0x7e, 0xee, 0x39, 0x19, 0x91, 0x68, 0x8f, 0xd5, 0xdd, 0x81, 0xc5, 0x8c, 0x47, 0xa1, 0xfd, 0xc4, - 0x3b, 0x04, 0x19, 0x2a, 0xe9, 0x4a, 0x8f, 0x4c, 0xba, 0x2e, 0xa9, 0x63, 0xbe, 0x52, 0xd5, 0x79, - 0x29, 0xf0, 0x80, 0x42, 0x29, 0xca, 0x03, 0xbe, 0x06, 0xba, 0x43, 0x77, 0xbc, 0x50, 0x8d, 0x82, - 0x93, 0x7d, 0x4c, 0xb1, 0x8e, 0xff, 0xa2, 0xc1, 0xcc, 0x1a, 0x65, 0xd1, 0x24, 0xe8, 0x25, 0x52, - 0x29, 0x7e, 0x0f, 0x66, 0x43, 0xf7, 0x57, 0xdc, 0x9f, 0x89, 0x65, 0x3e, 0x47, 0x03, 0xfe, 0xd7, - 0xad, 0x16, 0x7d, 0xa4, 0x0a, 0xca, 0x68, 0xd2, 0x73, 0x0b, 0x0a, 0xa1, 0x45, 0x74, 0x39, 0x96, - 0xee, 0xcc, 0xc5, 0xfa, 0x95, 0x3c, 0x64, 0x37, 0x4a, 0x8a, 0x27, 0x59, 0x36, 0xaa, 0x64, 0xd6, - 0x4f, 0x0d, 0x36, 0x01, 0x09, 0x75, 0x09, 0xb2, 0xe1, 0xe0, 0x24, 0xa0, 0xd7, 0xfc, 0xbc, 0xc7, - 0x9f, 0xa3, 0x57, 0x41, 0x77, 0xec, 0x87, 0x5e, 0x1e, 0x3b, 0x15, 0x1c, 0x49, 0xec, 0x87, 0x44, - 0x2c, 0xe1, 0x8b, 0x90, 0x22, 0xf6, 0x43, 0x54, 0x05, 0x70, 0x0c, 0xab, 0x4d, 0x6f, 0xfb, 0x15, - 0x54, 0x91, 0x84, 0x20, 0x63, 0x12, 0x87, 0x15, 0x98, 0x0d, 0xdf, 0x48, 0xaa, 0x7b, 0x09, 0xb2, - 0xef, 0xf7, 0xc3, 0xe2, 0x2a, 0xc5, 0xc4, 0x25, 0x0b, 0x75, 0x0f, 0x89, 0xdb, 0x0c, 0x04, 0x70, - 0x74, 0x02, 0xf2, 0xcc, 0xd8, 0xee, 0xd0, 0x1b, 0x81, 0x9b, 0x0b, 0x00, 0x7c, 0x95, 0x17, 0x7f, - 0xb7, 0x43, 0x19, 0x50, 0x00, 0x40, 0x6f, 0xc0, 0x4c, 0x70, 0xe7, 0x5b, 0x0e, 0xdd, 0x31, 0x1f, - 0x09, 0x0d, 0x17, 0xc9, 0x01, 0x38, 0x5a, 0x84, 0x23, 0x01, 0x6c, 0x53, 0x64, 0x1a, 0xba, 0x40, - 0x8d, 0x83, 0xb9, 0x6c, 0x04, 0xbb, 0xef, 0xde, 0xef, 0x1b, 0x1d, 0xf1, 0xf8, 0x8a, 0x24, 0x04, - 0xc1, 0x7f, 0xd5, 0x60, 0x56, 0xaa, 0x9a, 0x19, 0xec, 0xa5, 0xb4, 0xfa, 0xdf, 0x6a, 0x80, 0xc2, - 0x1c, 0x28, 0xd3, 0xfa, 0xff, 0x70, 0x23, 0x88, 0xa7, 0x32, 0x05, 0x51, 0xd3, 0x4a, 0x50, 0xd0, - 0xcb, 0xc1, 0x90, 0x11, 0xe9, 0x90, 0x2c, 0xae, 0x75, 0x59, 0x34, 0x4b, 0x08, 0x51, 0xff, 0xbc, - 0xd6, 0xdf, 0x1e, 0x30, 0xea, 0xaa, 0x92, 0x57, 0xd4, 0xfa, 0x02, 0x40, 0xe4, 0x1f, 0x3f, 0x8b, - 0x5a, 0x4c, 0x58, 0x8d, 0x1e, 0x9c, 0xa5, 0x40, 0xc4, 0x1b, 0xe0, 0xdf, 0x27, 0x61, 0xea, 0xb6, - 0xdd, 0xe9, 0x07, 0x81, 0xf1, 0x65, 0x0a, 0x18, 0x91, 0x3a, 0x3c, 0xed, 0xd5, 0xe1, 0x08, 0x74, - 0x97, 0xd1, 0x9e, 0xb0, 0xac, 0x14, 0x11, 0x63, 0x84, 0xa1, 0xc8, 0x0c, 0xa7, 0x4d, 0x99, 0xac, - 0x6e, 0xca, 0x19, 0x91, 0x76, 0x46, 0x60, 0x68, 0x01, 0x0a, 0x46, 0xbb, 0xed, 0xd0, 0xb6, 0xc1, - 0x68, 0x63, 0x50, 0xce, 0x8a, 0xc3, 0xc2, 0x20, 0xfc, 0x11, 0x4c, 0x7b, 0xc2, 0x52, 0x2a, 0x7d, - 0x0b, 0xb2, 0x0f, 0x04, 0x64, 0x44, 0x5f, 0x4c, 0xa2, 0x2a, 0x37, 0xe6, 0xa1, 0x45, 0xfb, 0xec, - 0xde, 0x9d, 0xf1, 0x55, 0xc8, 0x48, 0x74, 0x74, 0x22, 0x5c, 0xa3, 0xc8, 0x26, 0x0d, 0x9f, 0xab, - 0x82, 0x03, 0x43, 0x46, 0x12, 0x52, 0x8a, 0x17, 0xb6, 0x21, 0x21, 0x44, 0xfd, 0xe3, 0xff, 0x68, - 0x70, 0x74, 0x95, 0x32, 0xda, 0x64, 0xb4, 0x75, 0xc5, 0xa4, 0x9d, 0xd6, 0x37, 0x5a, 0x3e, 0xfb, - 0x4d, 0xb0, 0x54, 0xa8, 0x09, 0xc6, 0xfd, 0x4e, 0xc7, 0xb4, 0xe8, 0x46, 0xa8, 0x8b, 0x12, 0x00, - 0xb8, 0x87, 0xd8, 0xe1, 0x17, 0x97, 0xcb, 0xf2, 0xc3, 0x46, 0x08, 0xe2, 0x6b, 0x38, 0x13, 0x68, - 0x18, 0xdf, 0x85, 0xf9, 0x38, 0xd3, 0x4a, 0x47, 0x75, 0xc8, 0x88, 0xbd, 0x23, 0xda, 0xaf, 0x91, - 0x1d, 0x44, 0xa1, 0x8d, 0xfe, 0x14, 0x82, 0x1d, 0x98, 0x8a, 0xa0, 0x0b, 0x34, 0x6e, 0x39, 0xca, - 0xab, 0xca, 0x09, 0xfa, 0x16, 0xe8, 0x6c, 0xd0, 0x53, 0xce, 0xb4, 0x71, 0xf4, 0x8b, 0x61, 0x6d, - 0x36, 0xb2, 0x6d, 0x6b, 0xd0, 0xa3, 0x44, 0xa0, 0x70, 0x83, 0x6b, 0x1a, 0x4e, 0xcb, 0xb4, 0x8c, - 0x8e, 0xc9, 0xa4, 0x80, 0x74, 0x12, 0x06, 0xe1, 0x5f, 0x87, 0x54, 0x29, 0xad, 0xf4, 0x90, 0xaa, - 0xd4, 0x0e, 0xad, 0x4a, 0xed, 0x39, 0xaa, 0xc4, 0x3f, 0x08, 0x04, 0xef, 0x5d, 0x51, 0x09, 0xfe, - 0x1d, 0x98, 0x6e, 0x45, 0x56, 0xc6, 0x2b, 0x40, 0x76, 0x44, 0x63, 0xe8, 0x78, 0x2d, 0x10, 0xb9, - 0x80, 0x8c, 0x11, 0x79, 0x4c, 0x8e, 0xc9, 0x03, 0x72, 0x7c, 0xe3, 0x35, 0xc8, 0xfb, 0xdf, 0x8d, - 0x50, 0x01, 0xb2, 0x57, 0x6e, 0x92, 0x0f, 0x2f, 0x93, 0xd5, 0x99, 0x04, 0x2a, 0x42, 0xae, 0x71, - 0x79, 0xe5, 0x9a, 0x98, 0x69, 0xcb, 0x9f, 0x64, 0xbc, 0x60, 0xeb, 0xa0, 0xef, 0x42, 0x5a, 0x46, - 0xd0, 0xf9, 0xe0, 0xba, 0xe1, 0xcf, 0x33, 0x95, 0x63, 0x07, 0xe0, 0x92, 0x6f, 0x9c, 0x78, 0x4b, - 0x43, 0x37, 0xa0, 0x20, 0x80, 0xaa, 0x99, 0x7a, 0x22, 0xde, 0xd3, 0x8c, 0x50, 0x7a, 0x65, 0xcc, - 0x6a, 0x88, 0xde, 0x05, 0x48, 0x4b, 0x11, 0xcc, 0xc7, 0x12, 0x9d, 0x11, 0xb7, 0x89, 0xb4, 0x97, - 0x71, 0x02, 0xbd, 0x0d, 0xfa, 0x96, 0x61, 0x76, 0x50, 0x28, 0xcf, 0x0a, 0xf5, 0x40, 0x2b, 0xf3, - 0x71, 0x70, 0xe8, 0xd8, 0x4b, 0x7e, 0x2b, 0xf7, 0x58, 0xbc, 0x9f, 0xe4, 0x6d, 0x2f, 0x1f, 0x5c, - 0xf0, 0x4f, 0xbe, 0x29, 0x1b, 0x8e, 0x5e, 0x57, 0x03, 0xbd, 0x12, 0x3d, 0x2a, 0xd6, 0x04, 0xa9, - 0x54, 0xc7, 0x2d, 0xfb, 0x04, 0x37, 0xa0, 0x10, 0xea, 0x28, 0x84, 0xc5, 0x7a, 0xb0, 0x1d, 0x12, - 0x16, 0xeb, 0x88, 0x36, 0x04, 0x4e, 0xa0, 0x35, 0xc8, 0xf1, 0xec, 0x54, 0x7c, 0x79, 0x38, 0x1e, - 0x4f, 0x42, 0x43, 0xc9, 0x47, 0xe5, 0xc4, 0xe8, 0x45, 0x9f, 0xd0, 0xf7, 0x21, 0xbf, 0x46, 0x99, - 0xf2, 0xe0, 0xc7, 0xe2, 0x21, 0x60, 0x84, 0xa4, 0xa2, 0x61, 0x04, 0x27, 0xd0, 0x47, 0x22, 0x51, - 0x8e, 0x7a, 0x30, 0x54, 0x1b, 0xe3, 0xa9, 0xfc, 0x7b, 0x2d, 0x8c, 0x47, 0xf0, 0x29, 0x7f, 0x18, - 0xa1, 0xac, 0x62, 0x5d, 0x6d, 0xcc, 0x13, 0xf4, 0x29, 0xd7, 0x9e, 0xf3, 0xfd, 0x1f, 0x27, 0x96, - 0xef, 0x78, 0x9f, 0xc0, 0x57, 0x0d, 0x66, 0xa0, 0x9b, 0x30, 0x2d, 0x64, 0xe9, 0x7f, 0x23, 0x8f, - 0xd8, 0xfc, 0x81, 0x0f, 0xf2, 0x11, 0x9b, 0x3f, 0xf8, 0x61, 0x1e, 0x27, 0x1a, 0x77, 0x9e, 0x3c, - 0xad, 0x26, 0x3e, 0x7d, 0x5a, 0x4d, 0x7c, 0xfe, 0xb4, 0xaa, 0xfd, 0x78, 0xaf, 0xaa, 0xfd, 0x6e, - 0xaf, 0xaa, 0x3d, 0xde, 0xab, 0x6a, 0x4f, 0xf6, 0xaa, 0xda, 0xbf, 0xf6, 0xaa, 0xda, 0xbf, 0xf7, - 0xaa, 0x89, 0xcf, 0xf7, 0xaa, 0xda, 0xc7, 0xcf, 0xaa, 0x89, 0x27, 0xcf, 0xaa, 0x89, 0x4f, 0x9f, - 0x55, 0x13, 0x3f, 0x7c, 0xfd, 0xf9, 0x45, 0xa1, 0x74, 0x74, 0x19, 0xf1, 0x77, 0xe6, 0xbf, 0x01, - 0x00, 0x00, 0xff, 0xff, 0xd7, 0xf4, 0x3e, 0x20, 0xa8, 0x21, 0x00, 0x00, + // 2590 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x1a, 0x4d, 0x6f, 0x1b, 0xc7, + 0x95, 0x4b, 0x2e, 0xbf, 0x1e, 0x29, 0x59, 0x1e, 0xd1, 0x36, 0x41, 0xdb, 0xa4, 0x32, 0x68, 0x13, + 0x35, 0x76, 0xc4, 0x58, 0x69, 0xd2, 0xc4, 0x69, 0x9a, 0x9a, 0x52, 0xac, 0xc8, 0x51, 0x1c, 0x67, + 0xa4, 0x38, 0x69, 0xd1, 0x20, 0x58, 0x91, 0x23, 0x6a, 0x21, 0x72, 0x97, 0xde, 0x1d, 0xc6, 0xe1, + 0xad, 0x7f, 0xa0, 0x68, 0x8a, 0x1e, 0xda, 0x5e, 0x0a, 0x14, 0x28, 0xd0, 0x22, 0x45, 0x2f, 0x45, + 0x8f, 0x45, 0x7b, 0xe9, 0x21, 0xbd, 0xa5, 0xb7, 0x20, 0x07, 0xb6, 0x56, 0x2e, 0x85, 0x4e, 0x01, + 0x7a, 0xcb, 0xa9, 0x98, 0xaf, 0xfd, 0x12, 0x59, 0x87, 0x8a, 0x83, 0xc0, 0x17, 0x71, 0xe6, 0xcd, + 0x9b, 0x37, 0xf3, 0x3e, 0xe6, 0x7d, 0xad, 0xe0, 0xfc, 0xe0, 0xa0, 0xdb, 0xec, 0xb9, 0xdd, 0x81, + 0xe7, 0x32, 0x37, 0x18, 0xac, 0x88, 0xbf, 0xa8, 0xa0, 0xe7, 0xb5, 0x4a, 0xd7, 0xed, 0xba, 0x12, + 0x87, 0x8f, 0xe4, 0x7a, 0xad, 0xd1, 0x75, 0xdd, 0x6e, 0x8f, 0x36, 0xc5, 0x6c, 0x77, 0xb8, 0xd7, + 0x64, 0x76, 0x9f, 0xfa, 0xcc, 0xea, 0x0f, 0x14, 0xc2, 0x92, 0xa2, 0x7e, 0xa7, 0xd7, 0x77, 0x3b, + 0xb4, 0xd7, 0xf4, 0x99, 0xc5, 0x7c, 0xf9, 0x57, 0x61, 0x2c, 0x72, 0x8c, 0xc1, 0xd0, 0xdf, 0x17, + 0x7f, 0x24, 0x10, 0xff, 0xd9, 0x80, 0x33, 0x5b, 0xd6, 0x2e, 0xed, 0xed, 0xb8, 0xb7, 0xad, 0xde, + 0x90, 0xfa, 0x84, 0xfa, 0x03, 0xd7, 0xf1, 0x29, 0x5a, 0x83, 0x5c, 0x8f, 0x2f, 0xf8, 0x55, 0x63, + 0x29, 0xb3, 0x5c, 0x5a, 0xbd, 0xb4, 0x12, 0x5c, 0x79, 0xe2, 0x06, 0x09, 0xf5, 0x5f, 0x72, 0x98, + 0x37, 0x22, 0x6a, 0x6b, 0xed, 0x36, 0x94, 0x22, 0x60, 0xb4, 0x00, 0x99, 0x03, 0x3a, 0xaa, 0x1a, + 0x4b, 0xc6, 0x72, 0x91, 0xf0, 0x21, 0xba, 0x02, 0xd9, 0x77, 0x39, 0x99, 0x6a, 0x7a, 0xc9, 0x58, + 0x2e, 0xad, 0x9e, 0x0f, 0x0f, 0x79, 0xc3, 0xb1, 0xef, 0x0c, 0xa9, 0xd8, 0xad, 0x0e, 0x92, 0x98, + 0x57, 0xd3, 0xcf, 0x1a, 0xf8, 0x12, 0x9c, 0x3e, 0xb6, 0x8e, 0xce, 0x42, 0x4e, 0x60, 0xc8, 0x1b, + 0x17, 0x89, 0x9a, 0xe1, 0x0a, 0xa0, 0x6d, 0xe6, 0x51, 0xab, 0x4f, 0x2c, 0xc6, 0xef, 0x7b, 0x67, + 0x48, 0x7d, 0x86, 0x5f, 0x85, 0xc5, 0x18, 0x54, 0xb1, 0xfd, 0x0c, 0x94, 0xfc, 0x10, 0xac, 0x78, + 0xaf, 0x84, 0xd7, 0x0a, 0xf7, 0x90, 0x28, 0x22, 0xfe, 0xb5, 0x01, 0x10, 0xae, 0xa1, 0x3a, 0x80, + 0x5c, 0x7d, 0xd9, 0xf2, 0xf7, 0x05, 0xc3, 0x26, 0x89, 0x40, 0xd0, 0x65, 0x38, 0x1d, 0xce, 0x6e, + 0xba, 0xdb, 0xfb, 0x96, 0xd7, 0x11, 0x32, 0x30, 0xc9, 0xf1, 0x05, 0x84, 0xc0, 0xf4, 0x2c, 0x46, + 0xab, 0x99, 0x25, 0x63, 0x39, 0x43, 0xc4, 0x98, 0x73, 0xcb, 0xa8, 0x63, 0x39, 0xac, 0x6a, 0x0a, + 0x71, 0xaa, 0x19, 0x87, 0x73, 0xfd, 0x52, 0xbf, 0x9a, 0x5d, 0x32, 0x96, 0xe7, 0x88, 0x9a, 0xe1, + 0x0f, 0x32, 0x50, 0x7e, 0x7d, 0x48, 0xbd, 0x91, 0x12, 0x00, 0xaa, 0x43, 0xc1, 0xa7, 0x3d, 0xda, + 0x66, 0xae, 0x27, 0x35, 0xd2, 0x4a, 0x57, 0x0d, 0x12, 0xc0, 0x50, 0x05, 0xb2, 0x3d, 0xbb, 0x6f, + 0x33, 0x71, 0xad, 0x39, 0x22, 0x27, 0xe8, 0x2a, 0x64, 0x7d, 0x66, 0x79, 0x4c, 0xdc, 0xa5, 0xb4, + 0x5a, 0x5b, 0x91, 0x86, 0xb9, 0xa2, 0x0d, 0x73, 0x65, 0x47, 0x1b, 0x66, 0xab, 0xf0, 0xe1, 0xb8, + 0x91, 0x7a, 0xff, 0x5f, 0x0d, 0x83, 0xc8, 0x2d, 0xe8, 0x19, 0xc8, 0x50, 0xa7, 0x23, 0xee, 0xfb, + 0x45, 0x77, 0xf2, 0x0d, 0xe8, 0x0a, 0x14, 0x3b, 0xb6, 0x47, 0xdb, 0xcc, 0x76, 0x1d, 0xc1, 0xd5, + 0xfc, 0xea, 0x62, 0xa8, 0x91, 0x75, 0xbd, 0x44, 0x42, 0x2c, 0x74, 0x19, 0x72, 0x3e, 0x17, 0x9d, + 0x5f, 0xcd, 0x73, 0x5b, 0x68, 0x55, 0x8e, 0xc6, 0x8d, 0x05, 0x09, 0xb9, 0xec, 0xf6, 0x6d, 0x46, + 0xfb, 0x03, 0x36, 0x22, 0x0a, 0x07, 0x3d, 0x0e, 0xf9, 0x0e, 0xed, 0x51, 0xae, 0xf0, 0x82, 0x50, + 0xf8, 0x42, 0x84, 0xbc, 0x58, 0x20, 0x1a, 0x01, 0xbd, 0x0d, 0xe6, 0xa0, 0x67, 0x39, 0xd5, 0xa2, + 0xe0, 0x62, 0x3e, 0x44, 0xbc, 0xd5, 0xb3, 0x9c, 0xd6, 0x73, 0x9f, 0x8c, 0x1b, 0x4f, 0x77, 0x6d, + 0xb6, 0x3f, 0xdc, 0x5d, 0x69, 0xbb, 0xfd, 0x66, 0xd7, 0xb3, 0xf6, 0x2c, 0xc7, 0x6a, 0xf6, 0xdc, + 0x03, 0xbb, 0xf9, 0xee, 0x53, 0x4d, 0xfe, 0x06, 0xef, 0x0c, 0xa9, 0x67, 0x53, 0xaf, 0xc9, 0xc9, + 0xac, 0x08, 0x95, 0xf0, 0xad, 0x44, 0x90, 0xbd, 0x61, 0x16, 0x72, 0x0b, 0x79, 0x7c, 0x2f, 0x0d, + 0x68, 0xdb, 0xea, 0x0f, 0x7a, 0x74, 0x26, 0x95, 0x05, 0xca, 0x49, 0x9f, 0x58, 0x39, 0x99, 0x59, + 0x95, 0x13, 0x4a, 0xda, 0x9c, 0x4d, 0xd2, 0xd9, 0x2f, 0x2a, 0xe9, 0xdc, 0x57, 0x22, 0x69, 0x5c, + 0x05, 0x93, 0xcf, 0xb8, 0x53, 0xf2, 0xac, 0xbb, 0x42, 0x9e, 0x65, 0xc2, 0x87, 0x78, 0x0b, 0x72, + 0xf2, 0x2e, 0xa8, 0x96, 0x14, 0x78, 0xfc, 0x7d, 0x84, 0xc2, 0xce, 0x68, 0x31, 0x2e, 0x84, 0x62, + 0xcc, 0x08, 0x01, 0xe1, 0xbf, 0x18, 0x30, 0xa7, 0xb4, 0xa8, 0x7c, 0xcc, 0x2e, 0xe4, 0xe5, 0x1b, + 0xd7, 0xfe, 0xe5, 0x5c, 0xd2, 0xbf, 0x5c, 0xeb, 0x58, 0x03, 0x46, 0xbd, 0x56, 0xf3, 0xc3, 0x71, + 0xc3, 0xf8, 0x64, 0xdc, 0x78, 0x6c, 0x1a, 0xa3, 0xda, 0xa7, 0x6b, 0xbf, 0xa4, 0x09, 0xa3, 0x4b, + 0xe2, 0x76, 0xcc, 0x57, 0xa6, 0x70, 0x6a, 0x45, 0x86, 0x82, 0x4d, 0xa7, 0x4b, 0x7d, 0x4e, 0xd9, + 0xe4, 0x5a, 0x24, 0x12, 0x87, 0xb3, 0x79, 0xd7, 0xf2, 0x1c, 0xdb, 0xe9, 0xfa, 0xd5, 0x8c, 0xf0, + 0x9d, 0xc1, 0x1c, 0xff, 0xd2, 0x80, 0xc5, 0x98, 0x29, 0x2a, 0x26, 0x9e, 0x85, 0x9c, 0xcf, 0xa5, + 0xab, 0x79, 0x88, 0x28, 0x72, 0x5b, 0xc0, 0x5b, 0xf3, 0xea, 0xf2, 0x39, 0x39, 0x27, 0x0a, 0xff, + 0xc1, 0x5d, 0xed, 0xef, 0x06, 0x94, 0x45, 0x00, 0xd0, 0xef, 0x03, 0x81, 0xe9, 0x58, 0x7d, 0xaa, + 0x54, 0x25, 0xc6, 0x91, 0xa8, 0xc0, 0x8f, 0x2b, 0xe8, 0xa8, 0x30, 0xab, 0x23, 0x33, 0x4e, 0xec, + 0xc8, 0x8c, 0xf0, 0xad, 0x54, 0x20, 0xcb, 0x4d, 0x72, 0x24, 0x9c, 0x58, 0x91, 0xc8, 0x09, 0x7e, + 0x0c, 0xe6, 0x14, 0x17, 0x4a, 0xb4, 0xd3, 0x02, 0x59, 0x1f, 0x72, 0x52, 0x13, 0xe8, 0x1b, 0x50, + 0x0c, 0x12, 0x00, 0xc1, 0x6d, 0xa6, 0x95, 0x3b, 0x1a, 0x37, 0xd2, 0xcc, 0x27, 0xe1, 0x02, 0x6a, + 0x44, 0x83, 0xab, 0xd1, 0x2a, 0x1e, 0x8d, 0x1b, 0x12, 0xa0, 0x42, 0x29, 0xba, 0x00, 0xe6, 0x3e, + 0x8f, 0x4f, 0x5c, 0x04, 0x66, 0xab, 0x70, 0x34, 0x6e, 0x88, 0x39, 0x11, 0x7f, 0xf1, 0x06, 0x94, + 0xb7, 0x68, 0xd7, 0x6a, 0x8f, 0xd4, 0xa1, 0x15, 0x4d, 0x8e, 0x1f, 0x68, 0x68, 0x1a, 0x8f, 0x40, + 0x39, 0x38, 0xf1, 0x9d, 0xbe, 0xaf, 0x5e, 0x43, 0x29, 0x80, 0xbd, 0xea, 0xe3, 0x5f, 0x19, 0xa0, + 0x6c, 0x00, 0xe1, 0x48, 0x56, 0xc1, 0xfd, 0x17, 0x1c, 0x8d, 0x1b, 0x0a, 0xa2, 0x93, 0x06, 0xf4, + 0x3c, 0xe4, 0x7d, 0x71, 0x22, 0x27, 0x96, 0x34, 0x2d, 0xb1, 0xd0, 0x3a, 0xc5, 0x4d, 0xe4, 0x68, + 0xdc, 0xd0, 0x88, 0x44, 0x0f, 0xd0, 0x4a, 0x2c, 0xf0, 0x4a, 0xc6, 0xe6, 0x8f, 0xc6, 0x8d, 0x08, + 0x34, 0x1a, 0x88, 0xf1, 0xe7, 0x06, 0x94, 0x76, 0x2c, 0x3b, 0x30, 0xa1, 0xaa, 0x56, 0x51, 0xe8, + 0x5f, 0x25, 0x80, 0x5b, 0x62, 0x87, 0xf6, 0xac, 0xd1, 0x75, 0xd7, 0x13, 0x74, 0xe7, 0x48, 0x30, + 0x0f, 0x63, 0xa5, 0x39, 0x31, 0x56, 0x66, 0x67, 0x77, 0xc7, 0x5f, 0xad, 0xf3, 0xbb, 0x61, 0x16, + 0xd2, 0x0b, 0x19, 0xfc, 0x47, 0x03, 0xca, 0x92, 0x79, 0x65, 0x79, 0x3f, 0x82, 0x9c, 0x94, 0x8d, + 0x60, 0xff, 0xff, 0x38, 0xa6, 0x4b, 0xb3, 0x38, 0x25, 0x45, 0x13, 0xbd, 0x08, 0xf3, 0x1d, 0xcf, + 0x1d, 0x0c, 0x68, 0x67, 0x5b, 0xb9, 0xbf, 0x74, 0xd2, 0xfd, 0xad, 0x47, 0xd7, 0x49, 0x02, 0x1d, + 0xff, 0xc3, 0x80, 0x39, 0xe5, 0x4c, 0x94, 0xba, 0x02, 0x11, 0x1b, 0x27, 0x8e, 0x78, 0xe9, 0x59, + 0x23, 0xde, 0x59, 0xc8, 0x75, 0x3d, 0x77, 0x38, 0xd0, 0x0e, 0x49, 0xcd, 0x66, 0x8b, 0x84, 0xf8, + 0x06, 0xcc, 0x6b, 0x56, 0xa6, 0x78, 0xd4, 0x5a, 0xd2, 0xa3, 0x6e, 0x76, 0xa8, 0xc3, 0xec, 0x3d, + 0x3b, 0xf0, 0x91, 0x0a, 0x1f, 0xff, 0xd4, 0x80, 0x85, 0x24, 0x0a, 0x5a, 0x4f, 0x24, 0xf0, 0x8f, + 0x4e, 0x27, 0x17, 0xcd, 0xdd, 0x35, 0x69, 0x95, 0xc1, 0x3f, 0x7d, 0xbf, 0x0c, 0xbe, 0x12, 0x75, + 0x32, 0x45, 0xe5, 0x15, 0xf0, 0x2f, 0x0c, 0x98, 0x8b, 0xe9, 0x12, 0x3d, 0x0b, 0xe6, 0x9e, 0xe7, + 0xf6, 0x67, 0x52, 0x94, 0xd8, 0x81, 0xbe, 0x0d, 0x69, 0xe6, 0xce, 0xa4, 0xa6, 0x34, 0x73, 0xb9, + 0x96, 0x14, 0xfb, 0x19, 0x99, 0x1f, 0xcb, 0x19, 0x7e, 0x1a, 0x8a, 0x82, 0xa1, 0x5b, 0x96, 0xed, + 0x4d, 0x0c, 0x18, 0x93, 0x19, 0x7a, 0x1e, 0x4e, 0x49, 0x67, 0x38, 0x79, 0x73, 0x79, 0xd2, 0xe6, + 0xb2, 0xde, 0x7c, 0x1e, 0xb2, 0x6b, 0xfb, 0x43, 0xe7, 0x80, 0x6f, 0xe9, 0x58, 0xcc, 0xd2, 0x5b, + 0xf8, 0x18, 0x9f, 0x81, 0x45, 0xfe, 0x06, 0xa9, 0xe7, 0xaf, 0xb9, 0x43, 0x87, 0xe9, 0xfa, 0xe4, + 0x32, 0x54, 0xe2, 0x60, 0x65, 0x25, 0x15, 0xc8, 0xb6, 0x39, 0x40, 0xd0, 0x98, 0x23, 0x72, 0x82, + 0x7f, 0x6b, 0x00, 0xda, 0xa0, 0x4c, 0x9c, 0xb2, 0xb9, 0x1e, 0x3c, 0x8f, 0x1a, 0x14, 0xfa, 0x16, + 0x6b, 0xef, 0x53, 0xcf, 0xd7, 0xf9, 0x8b, 0x9e, 0x7f, 0x1d, 0xc9, 0x22, 0xbe, 0x02, 0x8b, 0xb1, + 0x5b, 0x2a, 0x9e, 0x6a, 0x50, 0x68, 0x2b, 0x98, 0x0a, 0x79, 0xc1, 0x1c, 0xff, 0x29, 0x0d, 0x05, + 0xb1, 0x81, 0xd0, 0x3d, 0x74, 0x05, 0x4a, 0x7b, 0xb6, 0xd3, 0xa5, 0xde, 0xc0, 0xb3, 0x95, 0x08, + 0xcc, 0xd6, 0xa9, 0xa3, 0x71, 0x23, 0x0a, 0x26, 0xd1, 0x09, 0x7a, 0x02, 0xf2, 0x43, 0x9f, 0x7a, + 0xef, 0xd8, 0xf2, 0xa5, 0x17, 0x5b, 0x95, 0xc3, 0x71, 0x23, 0xf7, 0x86, 0x4f, 0xbd, 0xcd, 0x75, + 0x1e, 0x7c, 0x86, 0x62, 0x44, 0xe4, 0x6f, 0x07, 0xbd, 0xa2, 0xcc, 0x54, 0x24, 0x70, 0xad, 0xef, + 0xf0, 0xeb, 0x27, 0x5c, 0xdd, 0xc0, 0x73, 0xfb, 0x94, 0xed, 0xd3, 0xa1, 0xdf, 0x6c, 0xbb, 0xfd, + 0xbe, 0xeb, 0x34, 0x45, 0xc5, 0x2d, 0x98, 0xe6, 0x11, 0x94, 0x6f, 0x57, 0x96, 0xbb, 0x03, 0x79, + 0xb6, 0xef, 0xb9, 0xc3, 0xee, 0xbe, 0x08, 0x0c, 0x99, 0xd6, 0xd5, 0xd9, 0xe9, 0x69, 0x0a, 0x44, + 0x0f, 0xd0, 0x23, 0x5c, 0x5a, 0xb4, 0x7d, 0xe0, 0x0f, 0xfb, 0xb2, 0xc6, 0x6b, 0x65, 0x8f, 0xc6, + 0x0d, 0xe3, 0x09, 0x12, 0x80, 0xf1, 0x4f, 0xd2, 0xd0, 0x88, 0x94, 0xc6, 0xd7, 0x5d, 0xef, 0x55, + 0xca, 0x3c, 0xbb, 0x7d, 0xd3, 0xea, 0x53, 0x6d, 0x1b, 0x0d, 0x28, 0xf5, 0x05, 0xf0, 0x9d, 0xc8, + 0x13, 0x80, 0x7e, 0x80, 0x87, 0x2e, 0x02, 0x88, 0x37, 0x23, 0xd7, 0xe5, 0x6b, 0x28, 0x0a, 0x88, + 0x58, 0x5e, 0x8b, 0x49, 0xaa, 0x39, 0x23, 0x67, 0x4a, 0x42, 0x9b, 0x49, 0x09, 0xcd, 0x4c, 0x27, + 0x10, 0x4b, 0xd4, 0xd6, 0xb3, 0x71, 0x5b, 0xc7, 0xff, 0x34, 0xa0, 0xbe, 0xa5, 0x6f, 0x7e, 0x42, + 0x71, 0x68, 0x7e, 0xd3, 0x0f, 0x88, 0xdf, 0xcc, 0x97, 0xe3, 0x17, 0xd7, 0x01, 0xb6, 0x6c, 0x87, + 0x5e, 0xb7, 0x7b, 0x8c, 0x7a, 0x13, 0xaa, 0x98, 0x9f, 0x67, 0x42, 0x97, 0x40, 0xe8, 0x9e, 0xe6, + 0x73, 0x2d, 0xe2, 0x87, 0x1f, 0x04, 0x1b, 0xe9, 0x07, 0xa8, 0xb6, 0x4c, 0xc2, 0x45, 0x39, 0x90, + 0xdf, 0x13, 0xec, 0xc9, 0x90, 0x1a, 0x6b, 0xc4, 0x84, 0xbc, 0xb7, 0xbe, 0xa7, 0x0e, 0x7f, 0xe6, + 0x3e, 0x19, 0x91, 0x68, 0x8f, 0x35, 0xfd, 0x91, 0xc3, 0xac, 0xf7, 0x22, 0xfb, 0x89, 0x3e, 0x04, + 0x59, 0x2a, 0xe9, 0xca, 0x4e, 0x4c, 0xba, 0x5e, 0x50, 0xc7, 0x7c, 0xa9, 0xaa, 0xf3, 0x85, 0xd0, + 0x03, 0x0a, 0xa5, 0x28, 0x0f, 0xf8, 0x28, 0x98, 0x1e, 0xdd, 0xd3, 0xa1, 0x1a, 0x85, 0x27, 0x07, + 0x98, 0x62, 0x1d, 0xff, 0xd5, 0x80, 0x85, 0x0d, 0xca, 0xe2, 0x49, 0xd0, 0x43, 0xa4, 0x52, 0xfc, + 0x32, 0x9c, 0x8e, 0xdc, 0x5f, 0x71, 0xff, 0x54, 0x22, 0xf3, 0x39, 0x13, 0xf2, 0xbf, 0xe9, 0x74, + 0xe8, 0x7b, 0xaa, 0xa0, 0x8c, 0x27, 0x3d, 0xb7, 0xa0, 0x14, 0x59, 0x44, 0xd7, 0x12, 0xe9, 0xce, + 0x62, 0xa2, 0x5f, 0xc9, 0x43, 0x76, 0xab, 0xa2, 0x78, 0x92, 0x65, 0xa3, 0x4a, 0x66, 0x83, 0xd4, + 0x60, 0x1b, 0x90, 0x50, 0x97, 0x20, 0x1b, 0x0d, 0x4e, 0x02, 0xfa, 0x4a, 0x90, 0xf7, 0x04, 0x73, + 0xf4, 0x08, 0x98, 0x9e, 0x7b, 0x57, 0xe7, 0xb1, 0x73, 0xe1, 0x91, 0xc4, 0xbd, 0x4b, 0xc4, 0x12, + 0x7e, 0x1e, 0x32, 0xc4, 0xbd, 0x8b, 0xea, 0x00, 0x9e, 0xe5, 0x74, 0xe9, 0xed, 0xa0, 0x82, 0x2a, + 0x93, 0x08, 0x64, 0x4a, 0xe2, 0xb0, 0x06, 0xa7, 0xa3, 0x37, 0x92, 0xea, 0x5e, 0x81, 0xfc, 0xeb, + 0xc3, 0xa8, 0xb8, 0x2a, 0x09, 0x71, 0xc9, 0x42, 0x5d, 0x23, 0x71, 0x9b, 0x81, 0x10, 0x8e, 0x2e, + 0x40, 0x91, 0x59, 0xbb, 0x3d, 0x7a, 0x33, 0x74, 0x73, 0x21, 0x80, 0xaf, 0xf2, 0xe2, 0xef, 0x76, + 0x24, 0x03, 0x0a, 0x01, 0xe8, 0x71, 0x58, 0x08, 0xef, 0x7c, 0xcb, 0xa3, 0x7b, 0xf6, 0x7b, 0x42, + 0xc3, 0x65, 0x72, 0x0c, 0x8e, 0x96, 0xe1, 0x54, 0x08, 0xdb, 0x16, 0x99, 0x86, 0x29, 0x50, 0x93, + 0x60, 0x2e, 0x1b, 0xc1, 0xee, 0x4b, 0x77, 0x86, 0x56, 0x4f, 0x3c, 0xbe, 0x32, 0x89, 0x40, 0xf0, + 0xdf, 0x0c, 0x38, 0x2d, 0x55, 0xcd, 0x2c, 0xf6, 0x50, 0x5a, 0xfd, 0xef, 0x0c, 0x40, 0x51, 0x0e, + 0x94, 0x69, 0x7d, 0x33, 0xda, 0x08, 0xe2, 0xa9, 0x4c, 0x49, 0xd4, 0xb4, 0x12, 0x14, 0xf6, 0x72, + 0x30, 0xe4, 0x44, 0x3a, 0x24, 0x8b, 0x6b, 0x53, 0x16, 0xcd, 0x12, 0x42, 0xd4, 0x2f, 0xaf, 0xf5, + 0x77, 0x47, 0x8c, 0xfa, 0xaa, 0xe4, 0x15, 0xb5, 0xbe, 0x00, 0x10, 0xf9, 0xc3, 0xcf, 0xa2, 0x0e, + 0x13, 0x56, 0x63, 0x86, 0x67, 0x29, 0x10, 0xd1, 0x03, 0xfc, 0x87, 0x34, 0xcc, 0xdd, 0x76, 0x7b, + 0xc3, 0x30, 0x30, 0x3e, 0x4c, 0x01, 0x23, 0x56, 0x87, 0x67, 0x75, 0x1d, 0x8e, 0xc0, 0xf4, 0x19, + 0x1d, 0x08, 0xcb, 0xca, 0x10, 0x31, 0x46, 0x18, 0xca, 0xcc, 0xf2, 0xba, 0x94, 0xc9, 0xea, 0xa6, + 0x9a, 0x13, 0x69, 0x67, 0x0c, 0x86, 0x96, 0xa0, 0x64, 0x75, 0xbb, 0x1e, 0xed, 0x5a, 0x8c, 0xb6, + 0x46, 0xd5, 0xbc, 0x38, 0x2c, 0x0a, 0xc2, 0x6f, 0xc1, 0xbc, 0x16, 0x96, 0x52, 0xe9, 0x93, 0x90, + 0x7f, 0x57, 0x40, 0x26, 0xf4, 0xc5, 0x24, 0xaa, 0x72, 0x63, 0x1a, 0x2d, 0xde, 0x67, 0xd7, 0x77, + 0xc6, 0x37, 0x20, 0x27, 0xd1, 0xd1, 0x85, 0x68, 0x8d, 0x22, 0x9b, 0x34, 0x7c, 0xae, 0x0a, 0x0e, + 0x0c, 0x39, 0x49, 0x48, 0x29, 0x5e, 0xd8, 0x86, 0x84, 0x10, 0xf5, 0x8b, 0xff, 0x6b, 0xc0, 0x99, + 0x75, 0xca, 0x68, 0x9b, 0xd1, 0xce, 0x75, 0x9b, 0xf6, 0x3a, 0x5f, 0x6b, 0xf9, 0x1c, 0x34, 0xc1, + 0x32, 0x91, 0x26, 0x18, 0xf7, 0x3b, 0x3d, 0xdb, 0xa1, 0x5b, 0x91, 0x2e, 0x4a, 0x08, 0xe0, 0x1e, + 0x62, 0x8f, 0x5f, 0x5c, 0x2e, 0xcb, 0x0f, 0x1b, 0x11, 0x48, 0xa0, 0xe1, 0x5c, 0xa8, 0x61, 0x6c, + 0xc3, 0xd9, 0x24, 0xd3, 0x4a, 0x47, 0x4d, 0xc8, 0x89, 0xbd, 0x13, 0xda, 0xaf, 0xb1, 0x1d, 0x44, + 0xa1, 0x25, 0x8e, 0x4f, 0x27, 0x8f, 0xc7, 0x3f, 0xe3, 0xd5, 0x6e, 0x74, 0xa7, 0x50, 0x2a, 0x37, + 0x22, 0xe5, 0x60, 0xe5, 0x04, 0x7d, 0x0b, 0x4c, 0x36, 0x1a, 0x28, 0xbf, 0xda, 0x3a, 0xf3, 0xf9, + 0xb8, 0x71, 0x3a, 0xb6, 0x6d, 0x67, 0x34, 0xa0, 0x44, 0xa0, 0x70, 0xdb, 0x6b, 0x5b, 0x5e, 0xc7, + 0x76, 0xac, 0x9e, 0xcd, 0xa4, 0xac, 0x4c, 0x12, 0x05, 0xa1, 0x8b, 0x90, 0xf3, 0x0f, 0x28, 0x6b, + 0xcb, 0xcc, 0xb9, 0xac, 0x8b, 0x00, 0x05, 0xc4, 0xbf, 0x89, 0x28, 0x5d, 0xda, 0xf3, 0x09, 0x95, + 0x6e, 0x9c, 0x58, 0xe9, 0xc6, 0x7d, 0x94, 0x8e, 0x7f, 0x10, 0xaa, 0x48, 0x5f, 0x51, 0xa9, 0xe8, + 0x45, 0x98, 0xef, 0xc4, 0x56, 0xa6, 0xab, 0x4a, 0xf6, 0x4e, 0x13, 0xe8, 0x78, 0x23, 0xd4, 0x88, + 0x80, 0x4c, 0xd1, 0x48, 0x42, 0xcc, 0xe9, 0x63, 0x62, 0x7e, 0xfc, 0x51, 0x28, 0x06, 0x5f, 0x98, + 0x50, 0x09, 0xf2, 0xd7, 0x5f, 0x23, 0x6f, 0x5e, 0x23, 0xeb, 0x0b, 0x29, 0x54, 0x86, 0x42, 0xeb, + 0xda, 0xda, 0x2b, 0x62, 0x66, 0xac, 0x7e, 0x90, 0xd3, 0x61, 0xd9, 0x43, 0xdf, 0x85, 0xac, 0x8c, + 0xb5, 0x67, 0xc3, 0xeb, 0x46, 0x3f, 0xe4, 0xd4, 0xce, 0x1d, 0x83, 0x4b, 0xbe, 0x71, 0xea, 0x49, + 0x03, 0xdd, 0x84, 0x92, 0x00, 0xaa, 0xb6, 0xeb, 0x85, 0x64, 0xf7, 0x33, 0x46, 0xe9, 0xe2, 0x94, + 0xd5, 0x08, 0xbd, 0xab, 0x90, 0x95, 0x22, 0x38, 0x9b, 0x48, 0x89, 0x26, 0xdc, 0x26, 0xd6, 0x88, + 0xc6, 0x29, 0xf4, 0x1c, 0x98, 0x3b, 0x96, 0xdd, 0x43, 0x91, 0x8c, 0x2c, 0xd2, 0x2d, 0xad, 0x9d, + 0x4d, 0x82, 0x23, 0xc7, 0xbe, 0x10, 0x34, 0x7d, 0xcf, 0x25, 0x3b, 0x4f, 0x7a, 0x7b, 0xf5, 0xf8, + 0x42, 0x70, 0xf2, 0x6b, 0xb2, 0x35, 0xa9, 0xfb, 0x1f, 0xe8, 0x62, 0xfc, 0xa8, 0x44, 0xbb, 0xa4, + 0x56, 0x9f, 0xb6, 0x1c, 0x10, 0xdc, 0x82, 0x52, 0xa4, 0xf7, 0x10, 0x15, 0xeb, 0xf1, 0xc6, 0x49, + 0x54, 0xac, 0x13, 0x1a, 0x16, 0x38, 0x85, 0x36, 0xa0, 0xc0, 0xf3, 0x58, 0xf1, 0x8d, 0xe2, 0x7c, + 0x32, 0x5d, 0x8d, 0xa4, 0x29, 0xb5, 0x0b, 0x93, 0x17, 0x03, 0x42, 0xdf, 0x87, 0xe2, 0x06, 0x65, + 0xca, 0xd7, 0x9f, 0x4b, 0x06, 0x8b, 0x09, 0x92, 0x8a, 0x07, 0x1c, 0x9c, 0x42, 0x6f, 0x89, 0x94, + 0x3a, 0xee, 0xeb, 0x50, 0x63, 0x8a, 0x4f, 0x0b, 0xee, 0xb5, 0x34, 0x1d, 0x21, 0xa0, 0xfc, 0x66, + 0x8c, 0xb2, 0x8a, 0x8a, 0x8d, 0x29, 0x4f, 0x30, 0xa0, 0xdc, 0xb8, 0xcf, 0x7f, 0x0a, 0xe0, 0xd4, + 0xea, 0xdb, 0xfa, 0x63, 0xf9, 0xba, 0xc5, 0x2c, 0xf4, 0x1a, 0xcc, 0x0b, 0x59, 0x06, 0x5f, 0xd3, + 0x63, 0x36, 0x7f, 0xec, 0xd3, 0x7d, 0xcc, 0xe6, 0x8f, 0x7f, 0xc2, 0xc7, 0xa9, 0xd6, 0xdb, 0x1f, + 0xdd, 0xab, 0xa7, 0x3e, 0xbe, 0x57, 0x4f, 0x7d, 0x76, 0xaf, 0x6e, 0xfc, 0xf8, 0xb0, 0x6e, 0xfc, + 0xfe, 0xb0, 0x6e, 0x7c, 0x78, 0x58, 0x37, 0x3e, 0x3a, 0xac, 0x1b, 0xff, 0x3e, 0xac, 0x1b, 0xff, + 0x39, 0xac, 0xa7, 0x3e, 0x3b, 0xac, 0x1b, 0xef, 0x7f, 0x5a, 0x4f, 0x7d, 0xf4, 0x69, 0x3d, 0xf5, + 0xf1, 0xa7, 0xf5, 0xd4, 0x0f, 0x1f, 0xbb, 0x7f, 0xf9, 0x28, 0x1d, 0x5d, 0x4e, 0xfc, 0x3c, 0xf5, + 0xbf, 0x00, 0x00, 0x00, 0xff, 0xff, 0x90, 0x3c, 0x9c, 0xf7, 0xd2, 0x21, 0x00, 0x00, } func (x Direction) String() string { @@ -4808,7 +4819,7 @@ func (this *DetectedFieldsResponse) Equal(that interface{}) bool { return false } } - if this.Limit != that1.Limit { + if this.FieldLimit != that1.FieldLimit { return false } return true @@ -4841,6 +4852,9 @@ func (this *DetectedField) Equal(that interface{}) bool { if this.Cardinality != that1.Cardinality { return false } + if !bytes.Equal(this.Sketch, that1.Sketch) { + return false + } return true } func (this *DetectedLabelsRequest) Equal(that interface{}) bool { @@ -5576,7 +5590,7 @@ func (this *DetectedFieldsResponse) GoString() string { if this.Fields != nil { s = append(s, "Fields: "+fmt.Sprintf("%#v", this.Fields)+",\n") } - s = append(s, "Limit: "+fmt.Sprintf("%#v", this.Limit)+",\n") + s = append(s, "FieldLimit: "+fmt.Sprintf("%#v", this.FieldLimit)+",\n") s = append(s, "}") return strings.Join(s, "") } @@ -5584,11 +5598,12 @@ func (this *DetectedField) GoString() string { if this == nil { return "nil" } - s := make([]string, 0, 7) + s := make([]string, 0, 8) s = append(s, "&logproto.DetectedField{") s = append(s, "Label: "+fmt.Sprintf("%#v", this.Label)+",\n") s = append(s, "Type: "+fmt.Sprintf("%#v", this.Type)+",\n") s = append(s, "Cardinality: "+fmt.Sprintf("%#v", this.Cardinality)+",\n") + s = append(s, "Sketch: "+fmt.Sprintf("%#v", this.Sketch)+",\n") s = append(s, "}") return strings.Join(s, "") } @@ -8432,8 +8447,8 @@ func (m *DetectedFieldsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) _ = i var l int _ = l - if m.Limit != 0 { - i = encodeVarintLogproto(dAtA, i, uint64(m.Limit)) + if m.FieldLimit != 0 { + i = encodeVarintLogproto(dAtA, i, uint64(m.FieldLimit)) i-- dAtA[i] = 0x10 } @@ -8474,6 +8489,13 @@ func (m *DetectedField) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if len(m.Sketch) > 0 { + i -= len(m.Sketch) + copy(dAtA[i:], m.Sketch) + i = encodeVarintLogproto(dAtA, i, uint64(len(m.Sketch))) + i-- + dAtA[i] = 0x22 + } if m.Cardinality != 0 { i = encodeVarintLogproto(dAtA, i, uint64(m.Cardinality)) i-- @@ -9598,8 +9620,8 @@ func (m *DetectedFieldsResponse) Size() (n int) { n += 1 + l + sovLogproto(uint64(l)) } } - if m.Limit != 0 { - n += 1 + sovLogproto(uint64(m.Limit)) + if m.FieldLimit != 0 { + n += 1 + sovLogproto(uint64(m.FieldLimit)) } return n } @@ -9621,6 +9643,10 @@ func (m *DetectedField) Size() (n int) { if m.Cardinality != 0 { n += 1 + sovLogproto(uint64(m.Cardinality)) } + l = len(m.Sketch) + if l > 0 { + n += 1 + l + sovLogproto(uint64(l)) + } return n } @@ -10337,7 +10363,7 @@ func (this *DetectedFieldsResponse) String() string { repeatedStringForFields += "}" s := strings.Join([]string{`&DetectedFieldsResponse{`, `Fields:` + repeatedStringForFields + `,`, - `Limit:` + fmt.Sprintf("%v", this.Limit) + `,`, + `FieldLimit:` + fmt.Sprintf("%v", this.FieldLimit) + `,`, `}`, }, "") return s @@ -10350,6 +10376,7 @@ func (this *DetectedField) String() string { `Label:` + fmt.Sprintf("%v", this.Label) + `,`, `Type:` + fmt.Sprintf("%v", this.Type) + `,`, `Cardinality:` + fmt.Sprintf("%v", this.Cardinality) + `,`, + `Sketch:` + fmt.Sprintf("%v", this.Sketch) + `,`, `}`, }, "") return s @@ -16907,9 +16934,9 @@ func (m *DetectedFieldsResponse) Unmarshal(dAtA []byte) error { iNdEx = postIndex case 2: if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Limit", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field FieldLimit", wireType) } - m.Limit = 0 + m.FieldLimit = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowLogproto @@ -16919,7 +16946,7 @@ func (m *DetectedFieldsResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Limit |= uint32(b&0x7F) << shift + m.FieldLimit |= uint32(b&0x7F) << shift if b < 0x80 { break } @@ -17060,6 +17087,40 @@ func (m *DetectedField) Unmarshal(dAtA []byte) error { break } } + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Sketch", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLogproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthLogproto + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthLogproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Sketch = append(m.Sketch[:0], dAtA[iNdEx:postIndex]...) + if m.Sketch == nil { + m.Sketch = []byte{} + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipLogproto(dAtA[iNdEx:]) diff --git a/pkg/logproto/logproto.proto b/pkg/logproto/logproto.proto index 38860f740045..a903271751f2 100644 --- a/pkg/logproto/logproto.proto +++ b/pkg/logproto/logproto.proto @@ -452,13 +452,16 @@ message DetectedFieldsRequest { message DetectedFieldsResponse { repeated DetectedField fields = 1; - uint32 limit = 2; + uint32 fieldLimit = 2; } +// TODO: make the detected field include the serialized sketch +// we only want cardinality in the JSON response message DetectedField { string label = 1; string type = 2 [(gogoproto.casttype) = "DetectedFieldType"]; uint64 cardinality = 3; + bytes sketch = 4 [ (gogoproto.jsontag) = "-" ]; //serialized hyperloglog sketch } message DetectedLabelsRequest { diff --git a/pkg/loki/modules.go b/pkg/loki/modules.go index 882c0d40d130..0a93058019ff 100644 --- a/pkg/loki/modules.go +++ b/pkg/loki/modules.go @@ -1085,7 +1085,6 @@ func (t *Loki) initQueryFrontend() (_ services.Service, err error) { t.Server.HTTP.Path("/loki/api/v1/labels").Methods("GET", "POST").Handler(frontendHandler) t.Server.HTTP.Path("/loki/api/v1/label/{name}/values").Methods("GET", "POST").Handler(frontendHandler) t.Server.HTTP.Path("/loki/api/v1/series").Methods("GET", "POST").Handler(frontendHandler) - t.Server.HTTP.Path("/loki/api/v1/detected_fields").Methods("GET", "POST").Handler(frontendHandler) t.Server.HTTP.Path("/loki/api/v1/patterns").Methods("GET", "POST").Handler(frontendHandler) t.Server.HTTP.Path("/loki/api/v1/detected_labels").Methods("GET", "POST").Handler(frontendHandler) t.Server.HTTP.Path("/loki/api/v1/index/stats").Methods("GET", "POST").Handler(frontendHandler) @@ -1105,6 +1104,12 @@ func (t *Loki) initQueryFrontend() (_ services.Service, err error) { t.Server.HTTP.Path("/api/prom/tail").Methods("GET", "POST").Handler(defaultHandler) } + // We don't marshal the hyperloglog sketch in the detected fields response to JSON, so this endpoint + // only works correctly in V2 with protobuf encoding enabled. + if frontendV2 != nil && frontendV2.IsProtobufEncoded() { + t.Server.HTTP.Path("/loki/api/v1/detected_fields").Methods("GET", "POST").Handler(frontendHandler) + } + if t.frontend == nil { return services.NewIdleService(nil, func(_ error) error { if t.stopper != nil { diff --git a/pkg/lokifrontend/frontend/v2/frontend.go b/pkg/lokifrontend/frontend/v2/frontend.go index 531157302073..898003993dde 100644 --- a/pkg/lokifrontend/frontend/v2/frontend.go +++ b/pkg/lokifrontend/frontend/v2/frontend.go @@ -446,6 +446,14 @@ func (f *Frontend) CheckReady(_ context.Context) error { return errors.New(msg) } +func (f *Frontend) IsProtobufEncoded() bool { + return f.cfg.Encoding == EncodingProtobuf +} + +func (f *Frontend) IsJSONEncoded() bool { + return f.cfg.Encoding == EncodingJSON +} + const stripeSize = 1 << 6 type requestsInProgress struct { diff --git a/pkg/querier/http.go b/pkg/querier/http.go index 5d9f216a1463..d4e629153203 100644 --- a/pkg/querier/http.go +++ b/pkg/querier/http.go @@ -386,7 +386,8 @@ func (q *QuerierAPI) DetectedFieldsHandler(ctx context.Context, req *logproto.De "msg", "queried store for detected fields that does not support it, no response from querier.DetectedFields", ) return &logproto.DetectedFieldsResponse{ - Fields: []*logproto.DetectedField{}, + Fields: []*logproto.DetectedField{}, + FieldLimit: req.GetFieldLimit(), }, nil } return resp, nil diff --git a/pkg/querier/multi_tenant_querier.go b/pkg/querier/multi_tenant_querier.go index 12cb412a61cf..54897200036d 100644 --- a/pkg/querier/multi_tenant_querier.go +++ b/pkg/querier/multi_tenant_querier.go @@ -278,7 +278,8 @@ func (q *MultiTenantQuerier) DetectedFields(ctx context.Context, req *logproto.D ) return &logproto.DetectedFieldsResponse{ - Fields: []*logproto.DetectedField{}, + Fields: []*logproto.DetectedField{}, + FieldLimit: req.GetFieldLimit(), }, nil } diff --git a/pkg/querier/querier.go b/pkg/querier/querier.go index 11143cf2fc23..d6fe43163acc 100644 --- a/pkg/querier/querier.go +++ b/pkg/querier/querier.go @@ -1027,20 +1027,32 @@ func (q *SingleTenantQuerier) DetectedFields(ctx context.Context, req *logproto. detectedFields := parseDetectedFields(ctx, req.FieldLimit, streams) + //TODO: detected field needs to contain the sketch + // make sure response to frontend is GRPC + //only want cardinality in JSON fields := make([]*logproto.DetectedField, len(detectedFields)) fieldCount := 0 for k, v := range detectedFields { + sketch, err := v.sketch.MarshalBinary() + if err != nil { + level.Warn(q.logger).Log("msg", "failed to marshal hyperloglog sketch", "err", err) + continue + } + fields[fieldCount] = &logproto.DetectedField{ Label: k, Type: v.fieldType, Cardinality: v.Estimate(), + Sketch: sketch, } fieldCount++ } + //TODO: detected fields response needs to include the sketch return &logproto.DetectedFieldsResponse{ - Fields: fields, + Fields: fields, + FieldLimit: req.GetFieldLimit(), }, nil } @@ -1066,6 +1078,10 @@ func (p *parsedFields) Estimate() uint64 { return p.sketch.Estimate() } +func (p *parsedFields) Marshal() ([]byte, error) { + return p.sketch.MarshalBinary() +} + func (p *parsedFields) DetermineType(value string) { p.fieldType = determineType(value) p.isTypeDetected = true diff --git a/pkg/querier/queryrange/codec.go b/pkg/querier/queryrange/codec.go index 313c280b3164..5fcd7b8bcf0f 100644 --- a/pkg/querier/queryrange/codec.go +++ b/pkg/querier/queryrange/codec.go @@ -1594,16 +1594,23 @@ func (Codec) MergeResponse(responses ...queryrangebase.Response) (queryrangebase case *DetectedFieldsResponse: resp0 := responses[0].(*DetectedFieldsResponse) headers := resp0.Headers - limit := resp0.Response.Limit + fieldLimit := resp0.Response.GetFieldLimit() fields := []*logproto.DetectedField{} for _, r := range responses { fields = append(fields, r.(*DetectedFieldsResponse).Response.Fields...) } + mergedFields, err := detected.MergeFields(fields, fieldLimit) + + if err != nil { + return nil, err + } + return &DetectedFieldsResponse{ Response: &logproto.DetectedFieldsResponse{ - Fields: detected.MergeFields(fields, limit), + Fields: mergedFields, + FieldLimit: fieldLimit, }, Headers: headers, }, nil @@ -2208,6 +2215,11 @@ func (r *DetectedFieldsRequest) LogToSpan(sp opentracing.Span) { sp.LogFields( otlog.String("start", timestamp.Time(r.GetStart().UnixNano()).String()), otlog.String("end", timestamp.Time(r.GetEnd().UnixNano()).String()), + otlog.String("query", r.GetQuery()), + otlog.Int64("step (ms)", r.GetStep()), + otlog.Int64("line_limit", int64(r.GetLineLimit())), + otlog.Int64("field_limit", int64(r.GetFieldLimit())), + otlog.String("step", fmt.Sprintf("%d", r.GetStep())), ) } diff --git a/pkg/querier/queryrange/split_by_interval.go b/pkg/querier/queryrange/split_by_interval.go index 92c956bbfed8..fc7174285979 100644 --- a/pkg/querier/queryrange/split_by_interval.go +++ b/pkg/querier/queryrange/split_by_interval.go @@ -223,6 +223,11 @@ func (h *splitByInterval) Do(ctx context.Context, r queryrangebase.Request) (que intervals[i], intervals[j] = intervals[j], intervals[i] } } + case *DetectedFieldsRequest: + limit = int64(req.LineLimit) + for i, j := 0, len(intervals)-1; i < j; i, j = i+1, j-1 { + intervals[i], intervals[j] = intervals[j], intervals[i] + } case *LokiSeriesRequest, *LabelRequest, *logproto.IndexStatsRequest, *logproto.VolumeRequest, *logproto.ShardsRequest: // Set this to 0 since this is not used in Series/Labels/Index Request. limit = 0 diff --git a/pkg/querier/queryrange/splitters.go b/pkg/querier/queryrange/splitters.go index 30bc3da18392..42a81f6defd3 100644 --- a/pkg/querier/queryrange/splitters.go +++ b/pkg/querier/queryrange/splitters.go @@ -95,6 +95,20 @@ func (s *defaultSplitter) split(execTime time.Time, tenantIDs []string, req quer AggregateBy: r.AggregateBy, }) } + case *DetectedFieldsRequest: + factory = func(start, end time.Time) { + reqs = append(reqs, &DetectedFieldsRequest{ + DetectedFieldsRequest: logproto.DetectedFieldsRequest{ + Start: start, + End: end, + Query: r.GetQuery(), + LineLimit: r.GetLineLimit(), + FieldLimit: r.GetFieldLimit(), + Step: r.GetStep(), + }, + path: r.path, + }) + } default: return nil, nil } diff --git a/pkg/storage/detected/fields.go b/pkg/storage/detected/fields.go index cda3a318e7d6..c34a84748b91 100644 --- a/pkg/storage/detected/fields.go +++ b/pkg/storage/detected/fields.go @@ -1,11 +1,45 @@ package detected import ( + "github.com/axiomhq/hyperloglog" "github.com/grafana/loki/v3/pkg/logproto" ) -func MergeFields(fields []*logproto.DetectedField, limit uint32) []*logproto.DetectedField { - mergedFields := make(map[string]*logproto.DetectedField, limit) +type UnmarshaledDetectedField struct { + Label string + Type logproto.DetectedFieldType + Sketch *hyperloglog.Sketch +} + +func UnmarshalDetectedField(f *logproto.DetectedField) (*UnmarshaledDetectedField, error) { + sketch := hyperloglog.New() + err := sketch.UnmarshalBinary(f.Sketch) + if err != nil { + return nil, err + } + + return &UnmarshaledDetectedField{ + Label: f.Label, + Type: f.Type, + Sketch: sketch, + }, nil +} + +func (f *UnmarshaledDetectedField) Merge(df *logproto.DetectedField) error { + sketch := hyperloglog.New() + err := sketch.UnmarshalBinary(df.Sketch) + if err != nil { + return err + } + + return f.Sketch.Merge(sketch) +} + +func MergeFields( + fields []*logproto.DetectedField, + fieldLimit uint32, +) ([]*logproto.DetectedField, error) { + mergedFields := make(map[string]*UnmarshaledDetectedField, fieldLimit) foundFields := uint32(0) for _, field := range fields { @@ -13,30 +47,43 @@ func MergeFields(fields []*logproto.DetectedField, limit uint32) []*logproto.Det continue } - //TODO(twhitney): this will take the first N up to limit, is there a better - //way to rank the fields to make sure we get the most interesting ones? + // TODO(twhitney): this will take the first N up to limit, is there a better + // way to rank the fields to make sure we get the most interesting ones? f, ok := mergedFields[field.Label] - if !ok && foundFields < limit { - mergedFields[field.Label] = field + if !ok && foundFields < fieldLimit { + unmarshaledField, err := UnmarshalDetectedField(field) + if err != nil { + return nil, err + } + + mergedFields[field.Label] = unmarshaledField foundFields++ continue } - //seeing the same field again, update the cardinality if it's higher - //this is an estimation, as the true cardinality could be greater - //than either of the seen values, but will never be less if ok { - curCard, newCard := f.Cardinality, field.Cardinality - if newCard > curCard { - f.Cardinality = newCard - } + // seeing the same field again, merge it with the existing one + f.Merge(field) } } - result := make([]*logproto.DetectedField, 0, limit) + result := make([]*logproto.DetectedField, 0, fieldLimit) for _, field := range mergedFields { - result = append(result, field) + // TODO(twhitney): what's the performance cost of marshalling here? We technically don't need to marshal in the merge + // but it's nice to keep the response consistent through middlewares in case we need the sketch somewhere else, + // need to benchmark this to find out. + sketch, err := field.Sketch.MarshalBinary() + if err != nil { + return nil, err + } + detectedField := &logproto.DetectedField{ + Label: field.Label, + Type: field.Type, + Cardinality: field.Sketch.Estimate(), + Sketch: sketch, + } + result = append(result, detectedField) } - return result + return result, nil } From 3b5c132e7b5e41da2280453917984f15585fda05 Mon Sep 17 00:00:00 2001 From: Trevor Whitney Date: Tue, 16 Apr 2024 16:42:11 -0600 Subject: [PATCH 08/10] fix: linters --- pkg/ingester/ingester.go | 2 +- pkg/logproto/logproto.proto | 2 +- pkg/lokifrontend/frontend/v2/frontend.go | 4 ++-- pkg/querier/queryrange/codec.go | 8 ++++---- pkg/querier/queryrange/codec_test.go | 8 ++++---- pkg/storage/detected/fields.go | 5 ++++- pkg/storage/detected/fields_test.go | 10 +++++++--- 7 files changed, 23 insertions(+), 16 deletions(-) diff --git a/pkg/ingester/ingester.go b/pkg/ingester/ingester.go index bdc90e452ba6..b0d197623d3c 100644 --- a/pkg/ingester/ingester.go +++ b/pkg/ingester/ingester.go @@ -1374,7 +1374,7 @@ func (i *Ingester) GetDetectedFields(_ context.Context, r *logproto.DetectedFiel Cardinality: 1, }, }, - FieldLimit: r.GetFieldLimit(), + FieldLimit: r.GetFieldLimit(), }, nil } diff --git a/pkg/logproto/logproto.proto b/pkg/logproto/logproto.proto index a903271751f2..487eea41dce0 100644 --- a/pkg/logproto/logproto.proto +++ b/pkg/logproto/logproto.proto @@ -461,7 +461,7 @@ message DetectedField { string label = 1; string type = 2 [(gogoproto.casttype) = "DetectedFieldType"]; uint64 cardinality = 3; - bytes sketch = 4 [ (gogoproto.jsontag) = "-" ]; //serialized hyperloglog sketch + bytes sketch = 4 [(gogoproto.jsontag) = "-"]; //serialized hyperloglog sketch } message DetectedLabelsRequest { diff --git a/pkg/lokifrontend/frontend/v2/frontend.go b/pkg/lokifrontend/frontend/v2/frontend.go index 898003993dde..dae27ec94682 100644 --- a/pkg/lokifrontend/frontend/v2/frontend.go +++ b/pkg/lokifrontend/frontend/v2/frontend.go @@ -447,11 +447,11 @@ func (f *Frontend) CheckReady(_ context.Context) error { } func (f *Frontend) IsProtobufEncoded() bool { - return f.cfg.Encoding == EncodingProtobuf + return f.cfg.Encoding == EncodingProtobuf } func (f *Frontend) IsJSONEncoded() bool { - return f.cfg.Encoding == EncodingJSON + return f.cfg.Encoding == EncodingJSON } const stripeSize = 1 << 6 diff --git a/pkg/querier/queryrange/codec.go b/pkg/querier/queryrange/codec.go index 5fcd7b8bcf0f..09997156128e 100644 --- a/pkg/querier/queryrange/codec.go +++ b/pkg/querier/queryrange/codec.go @@ -1601,11 +1601,11 @@ func (Codec) MergeResponse(responses ...queryrangebase.Response) (queryrangebase fields = append(fields, r.(*DetectedFieldsResponse).Response.Fields...) } - mergedFields, err := detected.MergeFields(fields, fieldLimit) + mergedFields, err := detected.MergeFields(fields, fieldLimit) - if err != nil { - return nil, err - } + if err != nil { + return nil, err + } return &DetectedFieldsResponse{ Response: &logproto.DetectedFieldsResponse{ diff --git a/pkg/querier/queryrange/codec_test.go b/pkg/querier/queryrange/codec_test.go index 4e3a950f0300..197baf8d0549 100644 --- a/pkg/querier/queryrange/codec_test.go +++ b/pkg/querier/queryrange/codec_test.go @@ -1632,7 +1632,7 @@ func Test_codec_MergeResponse_DetectedFieldsResponse(t *testing.T) { Type: logproto.DetectedFieldString, Cardinality: 1, }}, - Limit: 2, + FieldLimit: 2, }, }, &DetectedFieldsResponse{ @@ -1642,7 +1642,7 @@ func Test_codec_MergeResponse_DetectedFieldsResponse(t *testing.T) { Type: logproto.DetectedFieldString, Cardinality: 3, }}, - Limit: 2, + FieldLimit: 2, }, }, } @@ -1674,7 +1674,7 @@ func Test_codec_MergeResponse_DetectedFieldsResponse(t *testing.T) { Cardinality: 42, }, }, - Limit: 2, + FieldLimit: 2, }, }, &DetectedFieldsResponse{ @@ -1690,7 +1690,7 @@ func Test_codec_MergeResponse_DetectedFieldsResponse(t *testing.T) { Cardinality: 3, }, }, - Limit: 2, + FieldLimit: 2, }, }, } diff --git a/pkg/storage/detected/fields.go b/pkg/storage/detected/fields.go index c34a84748b91..8e2fa209edf0 100644 --- a/pkg/storage/detected/fields.go +++ b/pkg/storage/detected/fields.go @@ -63,7 +63,10 @@ func MergeFields( if ok { // seeing the same field again, merge it with the existing one - f.Merge(field) + err := f.Merge(field) + if err != nil { + return nil, err + } } } diff --git a/pkg/storage/detected/fields_test.go b/pkg/storage/detected/fields_test.go index 694491244731..38a68b4b0215 100644 --- a/pkg/storage/detected/fields_test.go +++ b/pkg/storage/detected/fields_test.go @@ -4,6 +4,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/grafana/loki/v3/pkg/logproto" ) @@ -29,7 +30,8 @@ func Test_MergeFields(t *testing.T) { t.Run("merges fields, taking the highest cardinality", func(t *testing.T) { limit := uint32(3) - result := MergeFields(fields, limit) + result, err := MergeFields(fields, limit) + require.NoError(t, err) assert.Equal(t, 2, len(result)) var foo *logproto.DetectedField @@ -45,11 +47,13 @@ func Test_MergeFields(t *testing.T) { t.Run("returns up to limit number of fields", func(t *testing.T) { limit := uint32(1) - result := MergeFields(fields, limit) + result, err := MergeFields(fields, limit) + require.NoError(t, err) assert.Equal(t, 1, len(result)) limit = uint32(4) - result = MergeFields(fields, limit) + result, err = MergeFields(fields, limit) + require.NoError(t, err) assert.Equal(t, 2, len(result)) }) } From 2b948ac0349f1bf841ac6e13f4f3159c48560683 Mon Sep 17 00:00:00 2001 From: Trevor Whitney Date: Wed, 17 Apr 2024 10:36:40 -0600 Subject: [PATCH 09/10] fix tests --- pkg/storage/detected/fields.go | 1 + pkg/storage/detected/fields_test.go | 47 +++++++++++++++++++++++++---- 2 files changed, 42 insertions(+), 6 deletions(-) diff --git a/pkg/storage/detected/fields.go b/pkg/storage/detected/fields.go index 8e2fa209edf0..b9e3c714d1ec 100644 --- a/pkg/storage/detected/fields.go +++ b/pkg/storage/detected/fields.go @@ -2,6 +2,7 @@ package detected import ( "github.com/axiomhq/hyperloglog" + "github.com/grafana/loki/v3/pkg/logproto" ) diff --git a/pkg/storage/detected/fields_test.go b/pkg/storage/detected/fields_test.go index 38a68b4b0215..4edd7026baf6 100644 --- a/pkg/storage/detected/fields_test.go +++ b/pkg/storage/detected/fields_test.go @@ -3,6 +3,7 @@ package detected import ( "testing" + "github.com/axiomhq/hyperloglog" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -10,26 +11,47 @@ import ( ) func Test_MergeFields(t *testing.T) { + fooSketch := hyperloglog.New() + fooSketch.Insert([]byte("bar")) + marshalledFooSketch, err := fooSketch.MarshalBinary() + require.NoError(t, err) + + barSketch := hyperloglog.New() + barSketch.Insert([]byte("baz")) + marshalledBarSketch, err := barSketch.MarshalBinary() + require.NoError(t, err) + + otherFooSketch := hyperloglog.New() + otherFooSketch.Insert([]byte("bar")) + otherFooSketch.Insert([]byte("baz")) + otherFooSketch.Insert([]byte("qux")) + marhsalledOtherFooSketch, err := otherFooSketch.MarshalBinary() + require.NoError(t, err) + fields := []*logproto.DetectedField{ { Label: "foo", Type: logproto.DetectedFieldString, Cardinality: 1, + Sketch: marshalledFooSketch, }, { Label: "bar", Type: logproto.DetectedFieldBoolean, Cardinality: 2, + Sketch: marshalledBarSketch, }, { Label: "foo", Type: logproto.DetectedFieldString, Cardinality: 3, + Sketch: marhsalledOtherFooSketch, }, } - t.Run("merges fields, taking the highest cardinality", func(t *testing.T) { - limit := uint32(3) + limit := uint32(3) + + t.Run("merges fields", func(t *testing.T) { result, err := MergeFields(fields, limit) require.NoError(t, err) assert.Equal(t, 2, len(result)) @@ -46,14 +68,27 @@ func Test_MergeFields(t *testing.T) { }) t.Run("returns up to limit number of fields", func(t *testing.T) { - limit := uint32(1) - result, err := MergeFields(fields, limit) + lowLimit := uint32(1) + result, err := MergeFields(fields, lowLimit) require.NoError(t, err) assert.Equal(t, 1, len(result)) - limit = uint32(4) - result, err = MergeFields(fields, limit) + highLimit := uint32(4) + result, err = MergeFields(fields, highLimit) require.NoError(t, err) assert.Equal(t, 2, len(result)) }) + + t.Run("returns an error when the field cannot be unmarshalled", func(t *testing.T) { + badFields := []*logproto.DetectedField{ + { + Label: "bad", + Type: logproto.DetectedFieldBoolean, + Cardinality: 42, + Sketch: []byte("bad"), + }, + } + _, err := MergeFields(badFields, limit) + require.Error(t, err) + }) } From 2d99fe7a7eea042623ef4035770c09133b76c15c Mon Sep 17 00:00:00 2001 From: Trevor Whitney Date: Wed, 17 Apr 2024 11:09:17 -0600 Subject: [PATCH 10/10] fix more tests --- pkg/querier/queryrange/codec_test.go | 59 ++++++++++++++-------------- 1 file changed, 29 insertions(+), 30 deletions(-) diff --git a/pkg/querier/queryrange/codec_test.go b/pkg/querier/queryrange/codec_test.go index 7584c224a43e..833132d5c2b9 100644 --- a/pkg/querier/queryrange/codec_test.go +++ b/pkg/querier/queryrange/codec_test.go @@ -14,6 +14,7 @@ import ( "testing" "time" + "github.com/axiomhq/hyperloglog" "github.com/gorilla/mux" "github.com/grafana/dskit/user" "github.com/opentracing/opentracing-go/mocktracer" @@ -1623,25 +1624,38 @@ func Test_codec_MergeResponse(t *testing.T) { } func Test_codec_MergeResponse_DetectedFieldsResponse(t *testing.T) { - t.Run("merges the responses, taking the highest cardinality", func(t *testing.T) { + buildDetctedField := func(label string, cardinality uint64) *logproto.DetectedField { + fooSketch := hyperloglog.New() + + for i := 0; i < int(cardinality); i++ { + fooSketch.Insert([]byte(fmt.Sprintf("value %d", i))) + } + marshalledSketch, err := fooSketch.MarshalBinary() + require.NoError(t, err) + + return &logproto.DetectedField{ + Label: label, + Type: logproto.DetectedFieldString, + Cardinality: cardinality, + Sketch: marshalledSketch, + } + } + + t.Run("merges the responses", func(t *testing.T) { responses := []queryrangebase.Response{ &DetectedFieldsResponse{ Response: &logproto.DetectedFieldsResponse{ - Fields: []*logproto.DetectedField{{ - Label: "foo", - Type: logproto.DetectedFieldString, - Cardinality: 1, - }}, + Fields: []*logproto.DetectedField{ + buildDetctedField("foo", 1), + }, FieldLimit: 2, }, }, &DetectedFieldsResponse{ Response: &logproto.DetectedFieldsResponse{ - Fields: []*logproto.DetectedField{{ - Label: "foo", - Type: logproto.DetectedFieldString, - Cardinality: 3, - }}, + Fields: []*logproto.DetectedField{ + buildDetctedField("foo", 3), + }, FieldLimit: 2, }, }, @@ -1663,16 +1677,8 @@ func Test_codec_MergeResponse_DetectedFieldsResponse(t *testing.T) { &DetectedFieldsResponse{ Response: &logproto.DetectedFieldsResponse{ Fields: []*logproto.DetectedField{ - { - Label: "foo", - Type: logproto.DetectedFieldString, - Cardinality: 1, - }, - { - Label: "bar", - Type: logproto.DetectedFieldInt, - Cardinality: 42, - }, + buildDetctedField("foo", 1), + buildDetctedField("bar", 42), }, FieldLimit: 2, }, @@ -1680,15 +1686,8 @@ func Test_codec_MergeResponse_DetectedFieldsResponse(t *testing.T) { &DetectedFieldsResponse{ Response: &logproto.DetectedFieldsResponse{ Fields: []*logproto.DetectedField{ - { - Label: "foo", - Type: logproto.DetectedFieldString, - Cardinality: 27, - }, { - Label: "baz", - Type: logproto.DetectedFieldBoolean, - Cardinality: 3, - }, + buildDetctedField("foo", 27), + buildDetctedField("baz", 3), }, FieldLimit: 2, },