diff --git a/api/gen/proto/go/types/v1/types.pb.go b/api/gen/proto/go/types/v1/types.pb.go index cd2f67933f..1fecdd8aba 100644 --- a/api/gen/proto/go/types/v1/types.pb.go +++ b/api/gen/proto/go/types/v1/types.pb.go @@ -490,9 +490,10 @@ type LabelNamesRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Matchers []string `protobuf:"bytes,1,rep,name=matchers,proto3" json:"matchers,omitempty"` - Start int64 `protobuf:"varint,2,opt,name=start,proto3" json:"start,omitempty"` - End int64 `protobuf:"varint,3,opt,name=end,proto3" json:"end,omitempty"` + Matchers []string `protobuf:"bytes,1,rep,name=matchers,proto3" json:"matchers,omitempty"` + Start int64 `protobuf:"varint,2,opt,name=start,proto3" json:"start,omitempty"` + End int64 `protobuf:"varint,3,opt,name=end,proto3" json:"end,omitempty"` + IncludeCardinality *bool `protobuf:"varint,4,opt,name=include_cardinality,json=includeCardinality,proto3,oneof" json:"include_cardinality,omitempty"` } func (x *LabelNamesRequest) Reset() { @@ -548,12 +549,20 @@ func (x *LabelNamesRequest) GetEnd() int64 { return 0 } +func (x *LabelNamesRequest) GetIncludeCardinality() bool { + if x != nil && x.IncludeCardinality != nil { + return *x.IncludeCardinality + } + return false +} + type LabelNamesResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Names []string `protobuf:"bytes,1,rep,name=names,proto3" json:"names,omitempty"` + Names []string `protobuf:"bytes,1,rep,name=names,proto3" json:"names,omitempty"` + EstimatedCardinality []int64 `protobuf:"varint,2,rep,packed,name=estimated_cardinality,json=estimatedCardinality,proto3" json:"estimated_cardinality,omitempty"` } func (x *LabelNamesResponse) Reset() { @@ -595,6 +604,13 @@ func (x *LabelNamesResponse) GetNames() []string { return nil } +func (x *LabelNamesResponse) GetEstimatedCardinality() []int64 { + if x != nil { + return x.EstimatedCardinality + } + return nil +} + type BlockInfo struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1049,77 +1065,85 @@ var file_types_v1_types_proto_rawDesc = []byte{ 0x10, 0x0a, 0x03, 0x65, 0x6e, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x65, 0x6e, 0x64, 0x22, 0x2b, 0x0a, 0x13, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x6e, 0x61, 0x6d, 0x65, - 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x22, 0x57, - 0x0a, 0x11, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x72, 0x73, 0x18, - 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x72, 0x73, 0x12, - 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, - 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x6e, 0x64, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x03, 0x52, 0x03, 0x65, 0x6e, 0x64, 0x22, 0x2a, 0x0a, 0x12, 0x4c, 0x61, 0x62, 0x65, 0x6c, - 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a, - 0x05, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x6e, 0x61, - 0x6d, 0x65, 0x73, 0x22, 0xbd, 0x01, 0x0a, 0x09, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x6e, 0x66, - 0x6f, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x6c, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x04, 0x75, 0x6c, 0x69, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x6d, 0x69, 0x6e, 0x5f, 0x74, 0x69, 0x6d, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x6d, 0x69, 0x6e, 0x54, 0x69, 0x6d, 0x65, - 0x12, 0x19, 0x0a, 0x08, 0x6d, 0x61, 0x78, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x03, 0x52, 0x07, 0x6d, 0x61, 0x78, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x39, 0x0a, 0x0a, 0x63, - 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x19, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, - 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x63, 0x6f, 0x6d, 0x70, - 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2b, 0x0a, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, - 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x76, - 0x31, 0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x50, 0x61, 0x69, 0x72, 0x52, 0x06, 0x6c, 0x61, 0x62, - 0x65, 0x6c, 0x73, 0x22, 0x5b, 0x0a, 0x0f, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x43, 0x6f, 0x6d, 0x70, - 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x18, 0x0a, 0x07, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, - 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x73, - 0x22, 0x6d, 0x0a, 0x12, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x54, 0x72, 0x61, 0x63, 0x65, 0x53, 0x65, - 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x2f, 0x0a, 0x09, 0x63, 0x61, 0x6c, 0x6c, 0x5f, 0x73, - 0x69, 0x74, 0x65, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x74, 0x79, 0x70, 0x65, - 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x08, 0x63, - 0x61, 0x6c, 0x6c, 0x53, 0x69, 0x74, 0x65, 0x12, 0x26, 0x0a, 0x06, 0x67, 0x6f, 0x5f, 0x70, 0x67, - 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, - 0x76, 0x31, 0x2e, 0x47, 0x6f, 0x50, 0x47, 0x4f, 0x52, 0x05, 0x67, 0x6f, 0x50, 0x67, 0x6f, 0x22, - 0x1e, 0x0a, 0x08, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x6e, - 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, - 0x5b, 0x0a, 0x05, 0x47, 0x6f, 0x50, 0x47, 0x4f, 0x12, 0x25, 0x0a, 0x0e, 0x6b, 0x65, 0x65, 0x70, - 0x5f, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, - 0x52, 0x0d, 0x6b, 0x65, 0x65, 0x70, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, - 0x2b, 0x0a, 0x11, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x5f, 0x63, 0x61, 0x6c, - 0x6c, 0x65, 0x65, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x61, 0x67, 0x67, 0x72, - 0x65, 0x67, 0x61, 0x74, 0x65, 0x43, 0x61, 0x6c, 0x6c, 0x65, 0x65, 0x73, 0x22, 0x18, 0x0a, 0x16, - 0x47, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x9e, 0x01, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x50, 0x72, - 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x69, 0x6e, 0x67, 0x65, 0x73, - 0x74, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x64, 0x61, 0x74, 0x61, 0x49, - 0x6e, 0x67, 0x65, 0x73, 0x74, 0x65, 0x64, 0x12, 0x2e, 0x0a, 0x13, 0x6f, 0x6c, 0x64, 0x65, 0x73, - 0x74, 0x5f, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x03, 0x52, 0x11, 0x6f, 0x6c, 0x64, 0x65, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x66, - 0x69, 0x6c, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x2e, 0x0a, 0x13, 0x6e, 0x65, 0x77, 0x65, 0x73, - 0x74, 0x5f, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x03, 0x52, 0x11, 0x6e, 0x65, 0x77, 0x65, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x66, - 0x69, 0x6c, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x2a, 0x6b, 0x0a, 0x19, 0x54, 0x69, 0x6d, 0x65, 0x53, - 0x65, 0x72, 0x69, 0x65, 0x73, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x54, 0x79, 0x70, 0x65, 0x12, 0x24, 0x0a, 0x20, 0x54, 0x49, 0x4d, 0x45, 0x5f, 0x53, 0x45, 0x52, - 0x49, 0x45, 0x53, 0x5f, 0x41, 0x47, 0x47, 0x52, 0x45, 0x47, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, - 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x55, 0x4d, 0x10, 0x00, 0x12, 0x28, 0x0a, 0x24, 0x54, 0x49, - 0x4d, 0x45, 0x5f, 0x53, 0x45, 0x52, 0x49, 0x45, 0x53, 0x5f, 0x41, 0x47, 0x47, 0x52, 0x45, 0x47, - 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x41, 0x56, 0x45, 0x52, 0x41, - 0x47, 0x45, 0x10, 0x01, 0x42, 0x9b, 0x01, 0x0a, 0x0c, 0x63, 0x6f, 0x6d, 0x2e, 0x74, 0x79, 0x70, - 0x65, 0x73, 0x2e, 0x76, 0x31, 0x42, 0x0a, 0x54, 0x79, 0x70, 0x65, 0x73, 0x50, 0x72, 0x6f, 0x74, - 0x6f, 0x50, 0x01, 0x5a, 0x3e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, - 0x67, 0x72, 0x61, 0x66, 0x61, 0x6e, 0x61, 0x2f, 0x70, 0x79, 0x72, 0x6f, 0x73, 0x63, 0x6f, 0x70, - 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, - 0x67, 0x6f, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x76, 0x31, 0x3b, 0x74, 0x79, 0x70, 0x65, - 0x73, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x54, 0x58, 0x58, 0xaa, 0x02, 0x08, 0x54, 0x79, 0x70, 0x65, - 0x73, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x08, 0x54, 0x79, 0x70, 0x65, 0x73, 0x5c, 0x56, 0x31, 0xe2, - 0x02, 0x14, 0x54, 0x79, 0x70, 0x65, 0x73, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, - 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x09, 0x54, 0x79, 0x70, 0x65, 0x73, 0x3a, 0x3a, - 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x22, 0xa5, + 0x01, 0x0a, 0x11, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x72, 0x73, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x72, 0x73, + 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, + 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x6e, 0x64, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x03, 0x65, 0x6e, 0x64, 0x12, 0x34, 0x0a, 0x13, 0x69, 0x6e, 0x63, 0x6c, + 0x75, 0x64, 0x65, 0x5f, 0x63, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x12, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, + 0x43, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x88, 0x01, 0x01, 0x42, 0x16, + 0x0a, 0x14, 0x5f, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x63, 0x61, 0x72, 0x64, 0x69, + 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x22, 0x5f, 0x0a, 0x12, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x4e, + 0x61, 0x6d, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, + 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x6e, 0x61, 0x6d, + 0x65, 0x73, 0x12, 0x33, 0x0a, 0x15, 0x65, 0x73, 0x74, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x64, 0x5f, + 0x63, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x18, 0x02, 0x20, 0x03, 0x28, + 0x03, 0x52, 0x14, 0x65, 0x73, 0x74, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x64, 0x43, 0x61, 0x72, 0x64, + 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x22, 0xbd, 0x01, 0x0a, 0x09, 0x42, 0x6c, 0x6f, 0x63, + 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x6c, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x75, 0x6c, 0x69, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x6d, 0x69, 0x6e, + 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x6d, 0x69, 0x6e, + 0x54, 0x69, 0x6d, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x6d, 0x61, 0x78, 0x5f, 0x74, 0x69, 0x6d, 0x65, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x6d, 0x61, 0x78, 0x54, 0x69, 0x6d, 0x65, 0x12, + 0x39, 0x0a, 0x0a, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x42, + 0x6c, 0x6f, 0x63, 0x6b, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0a, + 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2b, 0x0a, 0x06, 0x6c, 0x61, + 0x62, 0x65, 0x6c, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x74, 0x79, 0x70, + 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x50, 0x61, 0x69, 0x72, 0x52, + 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x22, 0x5b, 0x0a, 0x0f, 0x42, 0x6c, 0x6f, 0x63, 0x6b, + 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x65, + 0x76, 0x65, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, + 0x12, 0x18, 0x0a, 0x07, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, + 0x09, 0x52, 0x07, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, + 0x72, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x70, 0x61, 0x72, + 0x65, 0x6e, 0x74, 0x73, 0x22, 0x6d, 0x0a, 0x12, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x54, 0x72, 0x61, + 0x63, 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x2f, 0x0a, 0x09, 0x63, 0x61, + 0x6c, 0x6c, 0x5f, 0x73, 0x69, 0x74, 0x65, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, + 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x52, 0x08, 0x63, 0x61, 0x6c, 0x6c, 0x53, 0x69, 0x74, 0x65, 0x12, 0x26, 0x0a, 0x06, 0x67, + 0x6f, 0x5f, 0x70, 0x67, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x74, 0x79, + 0x70, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x6f, 0x50, 0x47, 0x4f, 0x52, 0x05, 0x67, 0x6f, + 0x50, 0x67, 0x6f, 0x22, 0x1e, 0x0a, 0x08, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, + 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x22, 0x5b, 0x0a, 0x05, 0x47, 0x6f, 0x50, 0x47, 0x4f, 0x12, 0x25, 0x0a, 0x0e, + 0x6b, 0x65, 0x65, 0x70, 0x5f, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x6b, 0x65, 0x65, 0x70, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x12, 0x2b, 0x0a, 0x11, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, + 0x5f, 0x63, 0x61, 0x6c, 0x6c, 0x65, 0x65, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, + 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x43, 0x61, 0x6c, 0x6c, 0x65, 0x65, 0x73, + 0x22, 0x18, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x53, 0x74, + 0x61, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x9e, 0x01, 0x0a, 0x17, 0x47, + 0x65, 0x74, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x69, + 0x6e, 0x67, 0x65, 0x73, 0x74, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x64, + 0x61, 0x74, 0x61, 0x49, 0x6e, 0x67, 0x65, 0x73, 0x74, 0x65, 0x64, 0x12, 0x2e, 0x0a, 0x13, 0x6f, + 0x6c, 0x64, 0x65, 0x73, 0x74, 0x5f, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x74, 0x69, + 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x11, 0x6f, 0x6c, 0x64, 0x65, 0x73, 0x74, + 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x2e, 0x0a, 0x13, 0x6e, + 0x65, 0x77, 0x65, 0x73, 0x74, 0x5f, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x74, 0x69, + 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x11, 0x6e, 0x65, 0x77, 0x65, 0x73, 0x74, + 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x2a, 0x6b, 0x0a, 0x19, 0x54, + 0x69, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x24, 0x0a, 0x20, 0x54, 0x49, 0x4d, 0x45, + 0x5f, 0x53, 0x45, 0x52, 0x49, 0x45, 0x53, 0x5f, 0x41, 0x47, 0x47, 0x52, 0x45, 0x47, 0x41, 0x54, + 0x49, 0x4f, 0x4e, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x55, 0x4d, 0x10, 0x00, 0x12, 0x28, + 0x0a, 0x24, 0x54, 0x49, 0x4d, 0x45, 0x5f, 0x53, 0x45, 0x52, 0x49, 0x45, 0x53, 0x5f, 0x41, 0x47, + 0x47, 0x52, 0x45, 0x47, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x41, + 0x56, 0x45, 0x52, 0x41, 0x47, 0x45, 0x10, 0x01, 0x42, 0x9b, 0x01, 0x0a, 0x0c, 0x63, 0x6f, 0x6d, + 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x42, 0x0a, 0x54, 0x79, 0x70, 0x65, 0x73, + 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x3e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x72, 0x61, 0x66, 0x61, 0x6e, 0x61, 0x2f, 0x70, 0x79, 0x72, 0x6f, + 0x73, 0x63, 0x6f, 0x70, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x6f, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x76, 0x31, 0x3b, + 0x74, 0x79, 0x70, 0x65, 0x73, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x54, 0x58, 0x58, 0xaa, 0x02, 0x08, + 0x54, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x08, 0x54, 0x79, 0x70, 0x65, 0x73, + 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x14, 0x54, 0x79, 0x70, 0x65, 0x73, 0x5c, 0x56, 0x31, 0x5c, 0x47, + 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x09, 0x54, 0x79, 0x70, + 0x65, 0x73, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -1369,6 +1393,7 @@ func file_types_v1_types_proto_init() { } } } + file_types_v1_types_proto_msgTypes[7].OneofWrappers = []any{} type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ diff --git a/api/gen/proto/go/types/v1/types_vtproto.pb.go b/api/gen/proto/go/types/v1/types_vtproto.pb.go index 14a8bcc7c7..0944f3a533 100644 --- a/api/gen/proto/go/types/v1/types_vtproto.pb.go +++ b/api/gen/proto/go/types/v1/types_vtproto.pb.go @@ -189,6 +189,10 @@ func (m *LabelNamesRequest) CloneVT() *LabelNamesRequest { copy(tmpContainer, rhs) r.Matchers = tmpContainer } + if rhs := m.IncludeCardinality; rhs != nil { + tmpVal := *rhs + r.IncludeCardinality = &tmpVal + } if len(m.unknownFields) > 0 { r.unknownFields = make([]byte, len(m.unknownFields)) copy(r.unknownFields, m.unknownFields) @@ -210,6 +214,11 @@ func (m *LabelNamesResponse) CloneVT() *LabelNamesResponse { copy(tmpContainer, rhs) r.Names = tmpContainer } + if rhs := m.EstimatedCardinality; rhs != nil { + tmpContainer := make([]int64, len(rhs)) + copy(tmpContainer, rhs) + r.EstimatedCardinality = tmpContainer + } if len(m.unknownFields) > 0 { r.unknownFields = make([]byte, len(m.unknownFields)) copy(r.unknownFields, m.unknownFields) @@ -610,6 +619,9 @@ func (this *LabelNamesRequest) EqualVT(that *LabelNamesRequest) bool { if this.End != that.End { return false } + if p, q := this.IncludeCardinality, that.IncludeCardinality; (p == nil && q != nil) || (p != nil && (q == nil || *p != *q)) { + return false + } return string(this.unknownFields) == string(that.unknownFields) } @@ -635,6 +647,15 @@ func (this *LabelNamesResponse) EqualVT(that *LabelNamesResponse) bool { return false } } + if len(this.EstimatedCardinality) != len(that.EstimatedCardinality) { + return false + } + for i, vx := range this.EstimatedCardinality { + vy := that.EstimatedCardinality[i] + if vx != vy { + return false + } + } return string(this.unknownFields) == string(that.unknownFields) } @@ -1244,6 +1265,16 @@ func (m *LabelNamesRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } + if m.IncludeCardinality != nil { + i-- + if *m.IncludeCardinality { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x20 + } if m.End != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.End)) i-- @@ -1296,6 +1327,27 @@ func (m *LabelNamesResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } + if len(m.EstimatedCardinality) > 0 { + var pksize2 int + for _, num := range m.EstimatedCardinality { + pksize2 += protohelpers.SizeOfVarint(uint64(num)) + } + i -= pksize2 + j1 := i + for _, num1 := range m.EstimatedCardinality { + num := uint64(num1) + for num >= 1<<7 { + dAtA[j1] = uint8(uint64(num)&0x7f | 0x80) + num >>= 7 + j1++ + } + dAtA[j1] = uint8(num) + j1++ + } + i = protohelpers.EncodeVarint(dAtA, i, uint64(pksize2)) + i-- + dAtA[i] = 0x12 + } if len(m.Names) > 0 { for iNdEx := len(m.Names) - 1; iNdEx >= 0; iNdEx-- { i -= len(m.Names[iNdEx]) @@ -1831,6 +1883,9 @@ func (m *LabelNamesRequest) SizeVT() (n int) { if m.End != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.End)) } + if m.IncludeCardinality != nil { + n += 2 + } n += len(m.unknownFields) return n } @@ -1847,6 +1902,13 @@ func (m *LabelNamesResponse) SizeVT() (n int) { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } + if len(m.EstimatedCardinality) > 0 { + l = 0 + for _, e := range m.EstimatedCardinality { + l += protohelpers.SizeOfVarint(uint64(e)) + } + n += 1 + protohelpers.SizeOfVarint(uint64(l)) + l + } n += len(m.unknownFields) return n } @@ -2963,6 +3025,27 @@ func (m *LabelNamesRequest) UnmarshalVT(dAtA []byte) error { break } } + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field IncludeCardinality", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + b := bool(v != 0) + m.IncludeCardinality = &b default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) @@ -3046,6 +3129,82 @@ func (m *LabelNamesResponse) UnmarshalVT(dAtA []byte) error { } m.Names = append(m.Names, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex + case 2: + if wireType == 0 { + var v int64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.EstimatedCardinality = append(m.EstimatedCardinality, v) + } else if wireType == 2 { + var packedLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + packedLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if packedLen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + packedLen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var elementCount int + var count int + for _, integer := range dAtA[iNdEx:postIndex] { + if integer < 128 { + count++ + } + } + elementCount = count + if elementCount != 0 && len(m.EstimatedCardinality) == 0 { + m.EstimatedCardinality = make([]int64, 0, elementCount) + } + for iNdEx < postIndex { + var v int64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.EstimatedCardinality = append(m.EstimatedCardinality, v) + } + } else { + return fmt.Errorf("proto: wrong wireType = %d for field EstimatedCardinality", wireType) + } default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) diff --git a/api/openapiv2/gen/phlare.swagger.json b/api/openapiv2/gen/phlare.swagger.json index 7a14fd90c3..9c9f4099e6 100644 --- a/api/openapiv2/gen/phlare.swagger.json +++ b/api/openapiv2/gen/phlare.swagger.json @@ -1214,6 +1214,13 @@ "items": { "type": "string" } + }, + "estimatedCardinality": { + "type": "array", + "items": { + "type": "string", + "format": "int64" + } } } }, diff --git a/api/types/v1/types.proto b/api/types/v1/types.proto index 45edf69f84..1ea30ecb2d 100644 --- a/api/types/v1/types.proto +++ b/api/types/v1/types.proto @@ -47,10 +47,12 @@ message LabelNamesRequest { repeated string matchers = 1; int64 start = 2; int64 end = 3; + optional bool include_cardinality = 4; } message LabelNamesResponse { repeated string names = 1; + repeated int64 estimated_cardinality = 2; } message BlockInfo { diff --git a/load.js b/load.js new file mode 100644 index 0000000000..f06542ccf5 --- /dev/null +++ b/load.js @@ -0,0 +1,57 @@ +import http from 'k6/http'; +import { check } from 'k6'; + +export let options = { + vus: 5, + duration: '10m', +}; + +export default function () { + // 24h query + const start = 1726473370933; + const end = 1726617370933; + + const response = labelNamesRequest({ + matchers: ["{service_name=\"fire-dev-001/querier\"}"], + start: start, + end: end + }); + + const names = response.json('names'); + check(response, { + 'Label names response is 200': (r) => r.status === 200, + 'Label names response has data': (r) => names.length > 0, + }); + + for (let name of names) { + const response = labelValuesRequest({ + matchers: [`{service_name=\"fire-dev-001/querier\", metric_name=\"${name}\"}`], + start: start, + end: end + }); + + const values = response.json('names'); + check(response, { + 'Label values response is 200': (r) => r.status === 200, + 'Label values response has data': () => values !== undefined && values.length > 0, + }); + } +} + +function labelNamesRequest(body) { + return http.post('http://localhost:4100/querier.v1.QuerierService/LabelNames', JSON.stringify(body), { + headers: { + 'Content-Type': 'application/json', + 'X-Scope-OrgID': '1218' + }, + }); +} + +function labelValuesRequest(body) { + return http.post('http://localhost:4100/querier.v1.QuerierService/LabelValues', JSON.stringify(body), { + headers: { + 'Content-Type': 'application/json', + 'X-Scope-OrgID': '1218' + }, + }); +} diff --git a/pkg/phlaredb/block_querier.go b/pkg/phlaredb/block_querier.go index dcdfe9cdcf..7641420eaa 100644 --- a/pkg/phlaredb/block_querier.go +++ b/pkg/phlaredb/block_querier.go @@ -479,7 +479,10 @@ func (b *singleBlockQuerier) LabelNames(ctx context.Context, req *connect.Reques iters = append(iters, iter) } - nameSet := make(map[string]struct{}) + // Maps label name to a set of label values. + nameSet := make(map[string]map[string]struct{}) + includeCardinality := req.Msg.IncludeCardinality != nil && *req.Msg.IncludeCardinality + iter := index.Intersect(iters...) for iter.Next() { names, err := b.index.LabelNamesFor(iter.At()) @@ -491,18 +494,46 @@ func (b *singleBlockQuerier) LabelNames(ctx context.Context, req *connect.Reques } for _, name := range names { - nameSet[name] = struct{}{} + var value string + if includeCardinality { + value, err = b.index.LabelValueFor(iter.At(), name) + if err != nil { + if err == storage.ErrNotFound { + continue + } + return nil, err + } + + if _, ok := nameSet[name]; !ok { + nameSet[name] = map[string]struct{}{value: {}} + } else { + nameSet[name][value] = struct{}{} + } + } else { + if _, ok := nameSet[name]; !ok { + nameSet[name] = map[string]struct{}{} + } + } } } - names := make([]string, 0, len(nameSet)) - for name := range nameSet { - names = append(names, name) + res := &typesv1.LabelNamesResponse{ + Names: make([]string, 0, len(nameSet)), } - slices.Sort(names) - return connect.NewResponse(&typesv1.LabelNamesResponse{ - Names: names, - }), nil + if includeCardinality { + res.EstimatedCardinality = make([]int64, 0, len(nameSet)) + for name, cardinality := range nameSet { + res.Names = append(res.Names, name) + res.EstimatedCardinality = append(res.EstimatedCardinality, int64(len(cardinality))) + } + } else { + for name := range nameSet { + res.Names = append(res.Names, name) + } + } + + util.SortLabelNamesResponse(res) + return connect.NewResponse(res), nil } func (b *singleBlockQuerier) BlockID() string { @@ -1393,7 +1424,7 @@ func LabelNames(ctx context.Context, req *connect.Request[typesv1.LabelNamesRequ return nil, err } - var labelNames []string + responses := make([]*typesv1.LabelNamesResponse, 0, len(queriers)) var lock sync.Mutex group, ctx := errgroup.WithContext(ctx) @@ -1408,7 +1439,7 @@ func LabelNames(ctx context.Context, req *connect.Request[typesv1.LabelNamesRequ } lock.Lock() - labelNames = append(labelNames, res.Msg.Names...) + responses = append(responses, res.Msg) lock.Unlock() return nil })) @@ -1418,10 +1449,8 @@ func LabelNames(ctx context.Context, req *connect.Request[typesv1.LabelNamesRequ return nil, err } - slices.Sort(labelNames) - return &typesv1.LabelNamesResponse{ - Names: lo.Uniq(labelNames), - }, nil + res := util.MergeLabelNames(responses) + return res, nil } func Series(ctx context.Context, req *ingestv1.SeriesRequest, blockGetter BlockGetter) (*ingestv1.SeriesResponse, error) { diff --git a/pkg/phlaredb/head.go b/pkg/phlaredb/head.go index 24239e7489..07eadf5a39 100644 --- a/pkg/phlaredb/head.go +++ b/pkg/phlaredb/head.go @@ -311,20 +311,40 @@ func (h *Head) LabelNames(ctx context.Context, req *connect.Request[typesv1.Labe }), nil } - // aggregate all label values from series matching, when matchers are given. - values := make(map[string]struct{}) + // aggregate all label nameSet from series matching, when matchers are given. + nameSet := make(map[string]map[string]struct{}) + includeCardinality := req.Msg.IncludeCardinality != nil && *req.Msg.IncludeCardinality + if err := h.forMatchingSelectors(selectors, func(lbs phlaremodel.Labels, fp model.Fingerprint) error { for _, lbl := range lbs { - values[lbl.Name] = struct{}{} + if includeCardinality { + addToLabelNameMap(nameSet, lbl) + } else { + if _, ok := nameSet[lbl.Name]; !ok { + nameSet[lbl.Name] = nil + } + } } return nil }); err != nil { return nil, err } - return connect.NewResponse(&typesv1.LabelNamesResponse{ - Names: lo.Keys(values), - }), nil + res := &typesv1.LabelNamesResponse{ + Names: make([]string, 0, len(nameSet)), + } + + if includeCardinality { + res.EstimatedCardinality = make([]int64, 0, len(nameSet)) + for name, cardinality := range nameSet { + res.Names = append(res.Names, name) + res.EstimatedCardinality = append(res.EstimatedCardinality, int64(len(cardinality))) + } + } else { + res.Names = lo.Keys(nameSet) + } + + return connect.NewResponse(res), nil } func (h *Head) MustProfileTypeNames() []string { @@ -674,3 +694,11 @@ func (h *Head) Meta() *block.Meta { func (h *Head) LocalPathFor(relPath string) string { return filepath.Join(h.localPath, relPath) } + +func addToLabelNameMap(labelNameMap map[string]map[string]struct{}, label *typesv1.LabelPair) map[string]map[string]struct{} { + if _, ok := labelNameMap[label.Name]; !ok { + labelNameMap[label.Name] = make(map[string]struct{}, 1) + } + labelNameMap[label.Name][label.Value] = struct{}{} + return labelNameMap +} diff --git a/pkg/querier/ingester_querier.go b/pkg/querier/ingester_querier.go index 739ad2765b..476bec2a15 100644 --- a/pkg/querier/ingester_querier.go +++ b/pkg/querier/ingester_querier.go @@ -254,16 +254,16 @@ func (q *Querier) labelValuesFromIngesters(ctx context.Context, req *typesv1.Lab return responses, nil } -func (q *Querier) labelNamesFromIngesters(ctx context.Context, req *typesv1.LabelNamesRequest) ([]ResponseFromReplica[[]string], error) { +func (q *Querier) labelNamesFromIngesters(ctx context.Context, req *typesv1.LabelNamesRequest) ([]ResponseFromReplica[*typesv1.LabelNamesResponse], error) { sp, ctx := opentracing.StartSpanFromContext(ctx, "LabelNames Ingesters") defer sp.Finish() - responses, err := forAllIngesters(ctx, q.ingesterQuerier, func(childCtx context.Context, ic IngesterQueryClient) ([]string, error) { + responses, err := forAllIngesters(ctx, q.ingesterQuerier, func(childCtx context.Context, ic IngesterQueryClient) (*typesv1.LabelNamesResponse, error) { res, err := ic.LabelNames(childCtx, connect.NewRequest(req)) if err != nil { return nil, err } - return res.Msg.Names, nil + return res.Msg, nil }) if err != nil { return nil, connect.NewError(connect.CodeInternal, err) diff --git a/pkg/querier/querier.go b/pkg/querier/querier.go index 510a9aa2cb..12c73d7aa5 100644 --- a/pkg/querier/querier.go +++ b/pkg/querier/querier.go @@ -40,6 +40,7 @@ import ( "github.com/grafana/pyroscope/pkg/pprof" "github.com/grafana/pyroscope/pkg/querier/vcs" "github.com/grafana/pyroscope/pkg/storegateway" + "github.com/grafana/pyroscope/pkg/util" pmath "github.com/grafana/pyroscope/pkg/util/math" "github.com/grafana/pyroscope/pkg/util/spanlogger" "github.com/grafana/pyroscope/pkg/validation" @@ -294,9 +295,9 @@ func (q *Querier) LabelNames(ctx context.Context, req *connect.Request[typesv1.L if err != nil { return nil, err } - return connect.NewResponse(&typesv1.LabelNamesResponse{ - Names: uniqueSortedStrings(responses), - }), nil + + res := uniqueSortedLabelNames(responses) + return connect.NewResponse(res), nil } storeQueries := splitQueryToStores(model.Time(req.Msg.Start), model.Time(req.Msg.End), model.Now(), q.cfg.QueryStoreAfter, nil) @@ -305,7 +306,7 @@ func (q *Querier) LabelNames(ctx context.Context, req *connect.Request[typesv1.L } storeQueries.Log(level.Debug(spanlogger.FromContext(ctx, q.logger))) - var responses []ResponseFromReplica[[]string] + var responses []ResponseFromReplica[*typesv1.LabelNamesResponse] var lock sync.Mutex group, gCtx := errgroup.WithContext(ctx) @@ -342,9 +343,8 @@ func (q *Querier) LabelNames(ctx context.Context, req *connect.Request[typesv1.L return nil, err } - return connect.NewResponse(&typesv1.LabelNamesResponse{ - Names: uniqueSortedStrings(responses), - }), nil + res := uniqueSortedLabelNames(responses) + return connect.NewResponse(res), nil } func (q *Querier) blockSelect(ctx context.Context, start, end model.Time) (blockPlan, error) { @@ -779,9 +779,10 @@ func (sq storeQuery) SeriesRequest(req *querierv1.SeriesRequest) *ingestv1.Serie func (sq storeQuery) LabelNamesRequest(req *typesv1.LabelNamesRequest) *typesv1.LabelNamesRequest { return &typesv1.LabelNamesRequest{ - Matchers: req.Matchers, - Start: int64(sq.start), - End: int64(sq.end), + Matchers: req.Matchers, + Start: int64(sq.start), + End: int64(sq.end), + IncludeCardinality: req.IncludeCardinality, } } @@ -1039,25 +1040,6 @@ func (q *Querier) selectSeries(ctx context.Context, req *connect.Request[querier return responses, nil } -func uniqueSortedStrings(responses []ResponseFromReplica[[]string]) []string { - total := 0 - for _, r := range responses { - total += len(r.response) - } - unique := make(map[string]struct{}, total) - result := make([]string, 0, total) - for _, r := range responses { - for _, elem := range r.response { - if _, ok := unique[elem]; !ok { - unique[elem] = struct{}{} - result = append(result, elem) - } - } - } - sort.Strings(result) - return result -} - func (q *Querier) selectSpanProfile(ctx context.Context, req *querierv1.SelectMergeSpanProfileRequest) (*phlaremodel.Tree, error) { // determine the block hints plan, err := q.blockSelect(ctx, model.Time(req.Start), model.Time(req.End)) @@ -1114,3 +1096,31 @@ func (q *Querier) selectSpanProfile(ctx context.Context, req *querierv1.SelectMe storegatewayTree.Merge(ingesterTree) return storegatewayTree, nil } + +func uniqueSortedStrings(responses []ResponseFromReplica[[]string]) []string { + total := 0 + for _, r := range responses { + total += len(r.response) + } + unique := make(map[string]struct{}, total) + result := make([]string, 0, total) + for _, r := range responses { + for _, elem := range r.response { + if _, ok := unique[elem]; !ok { + unique[elem] = struct{}{} + result = append(result, elem) + } + } + } + sort.Strings(result) + return result +} + +func uniqueSortedLabelNames(responses []ResponseFromReplica[*typesv1.LabelNamesResponse]) *typesv1.LabelNamesResponse { + innerResponses := make([]*typesv1.LabelNamesResponse, len(responses)) + for i, r := range responses { + innerResponses[i] = r.response + } + + return util.MergeLabelNames(innerResponses) +} diff --git a/pkg/querier/store_gateway_querier.go b/pkg/querier/store_gateway_querier.go index 7de34312c8..3cf0dab085 100644 --- a/pkg/querier/store_gateway_querier.go +++ b/pkg/querier/store_gateway_querier.go @@ -358,7 +358,7 @@ func (q *Querier) labelValuesFromStoreGateway(ctx context.Context, req *typesv1. return responses, nil } -func (q *Querier) labelNamesFromStoreGateway(ctx context.Context, req *typesv1.LabelNamesRequest) ([]ResponseFromReplica[[]string], error) { +func (q *Querier) labelNamesFromStoreGateway(ctx context.Context, req *typesv1.LabelNamesRequest) ([]ResponseFromReplica[*typesv1.LabelNamesResponse], error) { sp, ctx := opentracing.StartSpanFromContext(ctx, "LabelNames StoreGateway") defer sp.Finish() @@ -367,12 +367,12 @@ func (q *Querier) labelNamesFromStoreGateway(ctx context.Context, req *typesv1.L return nil, connect.NewError(connect.CodeInvalidArgument, err) } - responses, err := forAllStoreGateways(ctx, tenantID, q.storeGatewayQuerier, func(ctx context.Context, ic StoreGatewayQueryClient) ([]string, error) { + responses, err := forAllStoreGateways(ctx, tenantID, q.storeGatewayQuerier, func(ctx context.Context, ic StoreGatewayQueryClient) (*typesv1.LabelNamesResponse, error) { res, err := ic.LabelNames(ctx, connect.NewRequest(req)) if err != nil { return nil, err } - return res.Msg.Names, nil + return res.Msg, nil }) if err != nil { return nil, connect.NewError(connect.CodeInternal, err) diff --git a/pkg/util/labels.go b/pkg/util/labels.go index 82c0fa88ce..092a38af38 100644 --- a/pkg/util/labels.go +++ b/pkg/util/labels.go @@ -3,6 +3,8 @@ package util import ( "github.com/cespare/xxhash/v2" "github.com/prometheus/prometheus/model/labels" + + typesv1 "github.com/grafana/pyroscope/api/gen/proto/go/types/v1" ) var seps = []byte{'\xff'} @@ -33,3 +35,54 @@ func StableHash(ls labels.Labels) uint64 { } return xxhash.Sum64(b) } + +// MergeLabelNames merges multiple LabelNamesResponses into a single response. +// The result is sorted by label name. +func MergeLabelNames(responses []*typesv1.LabelNamesResponse) *typesv1.LabelNamesResponse { + nameCount := 0 + includeCardinality := true + for _, r := range responses { + nameCount += len(r.Names) + + // Cardinality and names should have a 1:1 mapping. If they don't, we + // can assume cardinality was not requested. + if len(r.Names) != len(r.EstimatedCardinality) { + includeCardinality = false + } + } + + uniqueNames := make(map[string]int64, nameCount) + for _, r := range responses { + for i, name := range r.Names { + cardinality := int64(0) + if includeCardinality { + cardinality = r.EstimatedCardinality[i] + } + + if _, ok := uniqueNames[name]; !ok { + uniqueNames[name] = cardinality + } else { + uniqueNames[name] += cardinality + } + } + } + + uniqueRes := &typesv1.LabelNamesResponse{ + Names: make([]string, 0, len(uniqueNames)), + } + + if includeCardinality { + uniqueRes.EstimatedCardinality = make([]int64, 0, len(uniqueNames)) + for name, cardinality := range uniqueNames { + uniqueRes.Names = append(uniqueRes.Names, name) + uniqueRes.EstimatedCardinality = append(uniqueRes.EstimatedCardinality, cardinality) + } + } else { + for name := range uniqueNames { + uniqueRes.Names = append(uniqueRes.Names, name) + } + } + + SortLabelNamesResponse(uniqueRes) + return uniqueRes +} diff --git a/pkg/util/labels_test.go b/pkg/util/labels_test.go new file mode 100644 index 0000000000..ce437e81b5 --- /dev/null +++ b/pkg/util/labels_test.go @@ -0,0 +1,128 @@ +package util + +import ( + "testing" + + "github.com/stretchr/testify/require" + + typesv1 "github.com/grafana/pyroscope/api/gen/proto/go/types/v1" +) + +func TestMergeLabelNames(t *testing.T) { + tests := []struct { + Name string + Responses []*typesv1.LabelNamesResponse + Want *typesv1.LabelNamesResponse + }{ + { + Name: "no_responses", + Responses: []*typesv1.LabelNamesResponse{}, + Want: &typesv1.LabelNamesResponse{ + Names: []string{}, + EstimatedCardinality: []int64{}, + }, + }, + { + Name: "single_response", + Responses: []*typesv1.LabelNamesResponse{ + { + Names: []string{"label_a", "label_b"}, + EstimatedCardinality: []int64{}, + }, + }, + Want: &typesv1.LabelNamesResponse{ + Names: []string{"label_a", "label_b"}, + EstimatedCardinality: nil, + }, + }, + { + Name: "single_response_with_cardinality", + Responses: []*typesv1.LabelNamesResponse{ + { + Names: []string{"label_a", "label_b"}, + EstimatedCardinality: []int64{10, 20}, + }, + }, + Want: &typesv1.LabelNamesResponse{ + Names: []string{"label_a", "label_b"}, + EstimatedCardinality: []int64{10, 20}, + }, + }, + { + Name: "multiple_responses", + Responses: []*typesv1.LabelNamesResponse{ + { + Names: []string{"label_a", "label_b"}, + EstimatedCardinality: []int64{}, + }, + { + Names: []string{"label_b", "label_c"}, + EstimatedCardinality: []int64{}, + }, + }, + Want: &typesv1.LabelNamesResponse{ + Names: []string{"label_a", "label_b", "label_c"}, + EstimatedCardinality: nil, + }, + }, + { + Name: "multiple_responses_with_cardinality", + Responses: []*typesv1.LabelNamesResponse{ + { + Names: []string{"label_a", "label_b"}, + EstimatedCardinality: []int64{10, 20}, + }, + { + Names: []string{"label_b", "label_c"}, + EstimatedCardinality: []int64{5, 6}, + }, + }, + Want: &typesv1.LabelNamesResponse{ + Names: []string{"label_a", "label_b", "label_c"}, + EstimatedCardinality: []int64{10, 25, 6}, + }, + }, + { + Name: "response_missing_cardinality", + Responses: []*typesv1.LabelNamesResponse{ + { + Names: []string{"label_a", "label_b"}, + EstimatedCardinality: []int64{10, 20}, + }, + { + Names: []string{"label_b", "label_c"}, + EstimatedCardinality: []int64{}, + }, + }, + Want: &typesv1.LabelNamesResponse{ + Names: []string{"label_a", "label_b", "label_c"}, + EstimatedCardinality: nil, + }, + }, + { + Name: "multiple_responses_with_empty_response", + Responses: []*typesv1.LabelNamesResponse{ + { + Names: []string{"label_a", "label_b"}, + EstimatedCardinality: []int64{10, 20}, + }, + { + Names: []string{}, + EstimatedCardinality: []int64{}, + }, + }, + Want: &typesv1.LabelNamesResponse{ + Names: []string{"label_a", "label_b"}, + EstimatedCardinality: []int64{10, 20}, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.Name, func(t *testing.T) { + got := MergeLabelNames(tt.Responses) + require.Equal(t, tt.Want.Names, got.Names) + require.Equal(t, tt.Want.EstimatedCardinality, got.EstimatedCardinality) + }) + } +} diff --git a/pkg/util/sort.go b/pkg/util/sort.go new file mode 100644 index 0000000000..8c7cb30b47 --- /dev/null +++ b/pkg/util/sort.go @@ -0,0 +1,32 @@ +package util + +import ( + "slices" + "sort" + + typesv1 "github.com/grafana/pyroscope/api/gen/proto/go/types/v1" +) + +type labelNameCardinalitySort typesv1.LabelNamesResponse + +func (s *labelNameCardinalitySort) Len() int { + return len(s.Names) +} + +func (s *labelNameCardinalitySort) Less(i, j int) bool { + return s.Names[i] < s.Names[j] +} + +func (s *labelNameCardinalitySort) Swap(i, j int) { + s.Names[i], s.Names[j] = s.Names[j], s.Names[i] + s.EstimatedCardinality[i], s.EstimatedCardinality[j] = s.EstimatedCardinality[j], s.EstimatedCardinality[i] +} + +func SortLabelNamesResponse(r *typesv1.LabelNamesResponse) { + if len(r.EstimatedCardinality) == 0 { + slices.Sort(r.Names) + return + } + + sort.Sort((*labelNameCardinalitySort)(r)) +}