diff --git a/cmd/thanos/query.go b/cmd/thanos/query.go index 944a2ccdbe..44e771a94b 100644 --- a/cmd/thanos/query.go +++ b/cmd/thanos/query.go @@ -783,11 +783,12 @@ func runQuery( if httpProbe.IsReady() { mint, maxt := proxy.TimeRange() return &infopb.StoreInfo{ - MinTime: mint, - MaxTime: maxt, - SupportsSharding: true, - SupportsWithoutReplicaLabels: true, - TsdbInfos: proxy.TSDBInfos(), + MinTime: mint, + MaxTime: maxt, + SupportsSharding: true, + SupportsWithoutReplicaLabels: true, + SupportsSortWithoutExternalLabels: true, + TsdbInfos: proxy.TSDBInfos(), } } return nil diff --git a/cmd/thanos/receive.go b/cmd/thanos/receive.go index 5175ae089f..3856734fd2 100644 --- a/cmd/thanos/receive.go +++ b/cmd/thanos/receive.go @@ -339,11 +339,12 @@ func runReceive( if httpProbe.IsReady() { minTime, maxTime := proxy.TimeRange() return &infopb.StoreInfo{ - MinTime: minTime, - MaxTime: maxTime, - SupportsSharding: true, - SupportsWithoutReplicaLabels: true, - TsdbInfos: proxy.TSDBInfos(), + MinTime: minTime, + MaxTime: maxTime, + SupportsSharding: true, + SupportsWithoutReplicaLabels: true, + SupportsSortWithoutExternalLabels: true, + TsdbInfos: proxy.TSDBInfos(), } } return nil diff --git a/cmd/thanos/rule.go b/cmd/thanos/rule.go index 6a57a55296..c0097ff300 100644 --- a/cmd/thanos/rule.go +++ b/cmd/thanos/rule.go @@ -628,11 +628,12 @@ func runRule( if httpProbe.IsReady() { mint, maxt := tsdbStore.TimeRange() return &infopb.StoreInfo{ - MinTime: mint, - MaxTime: maxt, - SupportsSharding: true, - SupportsWithoutReplicaLabels: true, - TsdbInfos: tsdbStore.TSDBInfos(), + MinTime: mint, + MaxTime: maxt, + SupportsSharding: true, + SupportsWithoutReplicaLabels: true, + SupportsSortWithoutExternalLabels: true, + TsdbInfos: tsdbStore.TSDBInfos(), } } return nil diff --git a/cmd/thanos/sidecar.go b/cmd/thanos/sidecar.go index ddd24104ed..bfd252fab6 100644 --- a/cmd/thanos/sidecar.go +++ b/cmd/thanos/sidecar.go @@ -268,11 +268,12 @@ func runSidecar( if httpProbe.IsReady() { mint, maxt := promStore.Timestamps() return &infopb.StoreInfo{ - MinTime: mint, - MaxTime: maxt, - SupportsSharding: true, - SupportsWithoutReplicaLabels: true, - TsdbInfos: promStore.TSDBInfos(), + MinTime: mint, + MaxTime: maxt, + SupportsSharding: true, + SupportsWithoutReplicaLabels: true, + SupportsSortWithoutExternalLabels: true, + TsdbInfos: promStore.TSDBInfos(), } } return nil diff --git a/cmd/thanos/store.go b/cmd/thanos/store.go index 4ac27ddeb3..faf31f0b9d 100644 --- a/cmd/thanos/store.go +++ b/cmd/thanos/store.go @@ -456,11 +456,12 @@ func runStore( if httpProbe.IsReady() { mint, maxt := bs.TimeRange() return &infopb.StoreInfo{ - MinTime: mint, - MaxTime: maxt, - SupportsSharding: true, - SupportsWithoutReplicaLabels: true, - TsdbInfos: bs.TSDBInfos(), + MinTime: mint, + MaxTime: maxt, + SupportsSharding: true, + SupportsWithoutReplicaLabels: true, + SupportsSortWithoutExternalLabels: true, + TsdbInfos: bs.TSDBInfos(), } } return nil diff --git a/pkg/info/infopb/rpc.pb.go b/pkg/info/infopb/rpc.pb.go index c88be63cfc..e11f91d806 100644 --- a/pkg/info/infopb/rpc.pb.go +++ b/pkg/info/infopb/rpc.pb.go @@ -124,6 +124,9 @@ type StoreInfo struct { SupportsWithoutReplicaLabels bool `protobuf:"varint,5,opt,name=supports_without_replica_labels,json=supportsWithoutReplicaLabels,proto3" json:"supports_without_replica_labels,omitempty"` // TSDBInfos holds metadata for all TSDBs exposed by the store. TsdbInfos []TSDBInfo `protobuf:"bytes,6,rep,name=tsdb_infos,json=tsdbInfos,proto3" json:"tsdb_infos"` + // supports_sort_without_external_labels indicates whether the store ignores external labels + // when comparing two label sets. + SupportsSortWithoutExternalLabels bool `protobuf:"varint,7,opt,name=supports_sort_without_external_labels,json=supportsSortWithoutExternalLabels,proto3" json:"supports_sort_without_external_labels,omitempty"` } func (m *StoreInfo) Reset() { *m = StoreInfo{} } @@ -400,44 +403,46 @@ func init() { func init() { proto.RegisterFile("info/infopb/rpc.proto", fileDescriptor_a1214ec45d2bf952) } var fileDescriptor_a1214ec45d2bf952 = []byte{ - // 589 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x94, 0xdf, 0x8a, 0xda, 0x40, - 0x14, 0xc6, 0x8d, 0x7f, 0xe3, 0x71, 0xdd, 0xee, 0x0e, 0xbb, 0x25, 0x4a, 0x89, 0x12, 0xf6, 0x42, - 0x68, 0x31, 0x60, 0xa1, 0x94, 0xf6, 0xaa, 0x6e, 0x85, 0x6e, 0xe9, 0x42, 0x1b, 0x85, 0xc2, 0xde, - 0x84, 0xa8, 0xb3, 0x1a, 0x48, 0x32, 0x63, 0x66, 0xa4, 0xfa, 0x16, 0x7d, 0x95, 0xbe, 0x85, 0x97, - 0x7b, 0xd9, 0xab, 0xd2, 0xea, 0x43, 0xf4, 0xb6, 0xcc, 0x4c, 0x62, 0x0d, 0xdd, 0xbd, 0xe9, 0x8d, - 0x66, 0xe6, 0xfb, 0x9d, 0xc9, 0x39, 0xdf, 0x39, 0x13, 0x38, 0xf7, 0xa3, 0x5b, 0x62, 0x8b, 0x1f, - 0x3a, 0xb6, 0x63, 0x3a, 0xe9, 0xd2, 0x98, 0x70, 0x82, 0x6a, 0x7c, 0xee, 0x45, 0x84, 0x75, 0x85, - 0xd0, 0x6c, 0x30, 0x4e, 0x62, 0x6c, 0x07, 0xde, 0x18, 0x07, 0x74, 0x6c, 0xf3, 0x35, 0xc5, 0x4c, - 0x71, 0xcd, 0xb3, 0x19, 0x99, 0x11, 0xf9, 0x68, 0x8b, 0x27, 0xb5, 0x6b, 0xd5, 0xa1, 0x76, 0x15, - 0xdd, 0x12, 0x07, 0x2f, 0x96, 0x98, 0x71, 0xeb, 0x5b, 0x01, 0x8e, 0xd4, 0x9a, 0x51, 0x12, 0x31, - 0x8c, 0x5e, 0x00, 0xc8, 0xc3, 0x5c, 0x86, 0x39, 0x33, 0xb4, 0x76, 0xa1, 0x53, 0xeb, 0x9d, 0x76, - 0x93, 0x57, 0xde, 0x7c, 0x10, 0xd2, 0x10, 0xf3, 0x7e, 0x71, 0xf3, 0xa3, 0x95, 0x73, 0xaa, 0x41, - 0xb2, 0x66, 0xe8, 0x02, 0xea, 0x97, 0x24, 0xa4, 0x24, 0xc2, 0x11, 0x1f, 0xad, 0x29, 0x36, 0xf2, - 0x6d, 0xad, 0x53, 0x75, 0xb2, 0x9b, 0xe8, 0x19, 0x94, 0x64, 0xc2, 0x46, 0xa1, 0xad, 0x75, 0x6a, - 0xbd, 0xc7, 0xdd, 0x83, 0x5a, 0xba, 0x43, 0xa1, 0xc8, 0x64, 0x14, 0x24, 0xe8, 0x78, 0x19, 0x60, - 0x66, 0x14, 0xef, 0xa1, 0x1d, 0xa1, 0x28, 0x5a, 0x42, 0xe8, 0x1d, 0x3c, 0x0a, 0x31, 0x8f, 0xfd, - 0x89, 0x1b, 0x62, 0xee, 0x4d, 0x3d, 0xee, 0x19, 0x25, 0x19, 0xd7, 0xca, 0xc4, 0x5d, 0x4b, 0xe6, - 0x3a, 0x41, 0xe4, 0x01, 0xc7, 0x61, 0x66, 0x0f, 0xf5, 0xa0, 0xc2, 0xbd, 0x78, 0x26, 0x0c, 0x28, - 0xcb, 0x13, 0x8c, 0xcc, 0x09, 0x23, 0xa5, 0xc9, 0xd0, 0x14, 0x44, 0x2f, 0xa1, 0x8a, 0x57, 0x38, - 0xa4, 0x81, 0x17, 0x33, 0xa3, 0x22, 0xa3, 0x9a, 0x99, 0xa8, 0x41, 0xaa, 0xca, 0xb8, 0xbf, 0x30, - 0xb2, 0xa1, 0xb4, 0x58, 0xe2, 0x78, 0x6d, 0xe8, 0x32, 0xaa, 0x91, 0x89, 0xfa, 0x24, 0x94, 0x37, - 0x1f, 0xaf, 0x54, 0xa1, 0x92, 0xb3, 0x7e, 0x6b, 0x50, 0xdd, 0x7b, 0x85, 0x1a, 0xa0, 0x87, 0x7e, - 0xe4, 0x72, 0x3f, 0xc4, 0x86, 0xd6, 0xd6, 0x3a, 0x05, 0xa7, 0x12, 0xfa, 0xd1, 0xc8, 0x0f, 0xb1, - 0x94, 0xbc, 0x95, 0x92, 0xf2, 0x89, 0xe4, 0xad, 0xa4, 0xf4, 0x14, 0x4e, 0xd9, 0x92, 0x52, 0x12, - 0x73, 0xe6, 0xb2, 0xb9, 0x17, 0x4f, 0xfd, 0x68, 0x26, 0x9b, 0xa2, 0x3b, 0x27, 0xa9, 0x30, 0x4c, - 0xf6, 0xd1, 0x00, 0x5a, 0x7b, 0xf8, 0x8b, 0xcf, 0xe7, 0x64, 0xc9, 0xdd, 0x18, 0xd3, 0xc0, 0x9f, - 0x78, 0xae, 0x9c, 0x00, 0x26, 0x9d, 0xd6, 0x9d, 0x27, 0x29, 0xf6, 0x59, 0x51, 0x8e, 0x82, 0xe4, - 0xd4, 0x30, 0xf4, 0x0a, 0x80, 0xb3, 0xe9, 0xd8, 0x15, 0x85, 0x09, 0x67, 0xc5, 0x68, 0x9d, 0x67, - 0x9d, 0x1d, 0xbe, 0xed, 0x8b, 0xa2, 0xd2, 0xf1, 0x12, 0xb8, 0x58, 0xb3, 0xf7, 0x45, 0xbd, 0x78, - 0x52, 0xb2, 0x6a, 0x50, 0xdd, 0xb7, 0xdd, 0x3a, 0x03, 0xf4, 0x6f, 0x2f, 0xc5, 0x7c, 0x1f, 0xf4, - 0xc7, 0x1a, 0x40, 0x3d, 0x63, 0xfc, 0xff, 0xd9, 0x65, 0x1d, 0xc3, 0xd1, 0x61, 0x27, 0xac, 0x05, - 0xe8, 0x69, 0xae, 0xc8, 0x86, 0x72, 0x62, 0x82, 0x26, 0x1b, 0xf8, 0xe0, 0x6d, 0x49, 0xb0, 0x4c, - 0x0a, 0xf9, 0x87, 0x53, 0x28, 0x64, 0x52, 0xe8, 0x5d, 0x42, 0x51, 0xbe, 0xee, 0x75, 0xf2, 0x9f, - 0x9d, 0xc9, 0x83, 0x3b, 0xdd, 0x6c, 0xdc, 0xa3, 0xa8, 0xdb, 0xdd, 0xbf, 0xd8, 0xfc, 0x32, 0x73, - 0x9b, 0xad, 0xa9, 0xdd, 0x6d, 0x4d, 0xed, 0xe7, 0xd6, 0xd4, 0xbe, 0xee, 0xcc, 0xdc, 0xdd, 0xce, - 0xcc, 0x7d, 0xdf, 0x99, 0xb9, 0x9b, 0xb2, 0xfa, 0xd6, 0x8c, 0xcb, 0xf2, 0x53, 0xf1, 0xfc, 0x4f, - 0x00, 0x00, 0x00, 0xff, 0xff, 0xf1, 0x5f, 0x0a, 0x2f, 0x81, 0x04, 0x00, 0x00, + // 619 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x94, 0xdd, 0x6a, 0xdb, 0x30, + 0x14, 0xc7, 0xe3, 0x7c, 0xe7, 0xa4, 0xed, 0x5a, 0xd1, 0x0e, 0x27, 0x0c, 0x37, 0x33, 0x1d, 0x04, + 0x36, 0x62, 0xc8, 0x60, 0x8c, 0xed, 0x6a, 0xed, 0x02, 0xeb, 0x58, 0xa1, 0x73, 0x0a, 0x83, 0xde, + 0x18, 0xa7, 0x55, 0x53, 0x83, 0x6d, 0xa9, 0x92, 0xc2, 0xd2, 0xb7, 0xd8, 0xab, 0xec, 0x2d, 0xba, + 0xbb, 0x5e, 0xee, 0x6a, 0x6c, 0xed, 0x8b, 0x0c, 0x1d, 0xd9, 0x69, 0xcc, 0xda, 0x9b, 0xdd, 0x24, + 0x96, 0xfe, 0xbf, 0x23, 0xff, 0xcf, 0x87, 0x05, 0x5b, 0x51, 0x7a, 0xc6, 0x3c, 0xfd, 0xc3, 0x27, + 0x9e, 0xe0, 0x27, 0x03, 0x2e, 0x98, 0x62, 0xa4, 0xad, 0xce, 0xc3, 0x94, 0xc9, 0x81, 0x16, 0xba, + 0x1d, 0xa9, 0x98, 0xa0, 0x5e, 0x1c, 0x4e, 0x68, 0xcc, 0x27, 0x9e, 0xba, 0xe4, 0x54, 0x1a, 0xae, + 0xbb, 0x39, 0x65, 0x53, 0x86, 0x8f, 0x9e, 0x7e, 0x32, 0xbb, 0xee, 0x2a, 0xb4, 0xf7, 0xd3, 0x33, + 0xe6, 0xd3, 0x8b, 0x19, 0x95, 0xca, 0xfd, 0x5e, 0x81, 0x15, 0xb3, 0x96, 0x9c, 0xa5, 0x92, 0x92, + 0x57, 0x00, 0x78, 0x58, 0x20, 0xa9, 0x92, 0xb6, 0xd5, 0xab, 0xf4, 0xdb, 0xc3, 0x8d, 0x41, 0xf6, + 0xca, 0xe3, 0x4f, 0x5a, 0x1a, 0x53, 0xb5, 0x5b, 0xbd, 0xfa, 0xb5, 0x5d, 0xf2, 0x5b, 0x71, 0xb6, + 0x96, 0x64, 0x07, 0x56, 0xf7, 0x58, 0xc2, 0x59, 0x4a, 0x53, 0x75, 0x74, 0xc9, 0xa9, 0x5d, 0xee, + 0x59, 0xfd, 0x96, 0x5f, 0xdc, 0x24, 0x2f, 0xa0, 0x86, 0x86, 0xed, 0x4a, 0xcf, 0xea, 0xb7, 0x87, + 0x8f, 0x07, 0x4b, 0xb9, 0x0c, 0xc6, 0x5a, 0x41, 0x33, 0x06, 0xd2, 0xb4, 0x98, 0xc5, 0x54, 0xda, + 0xd5, 0x7b, 0x68, 0x5f, 0x2b, 0x86, 0x46, 0x88, 0x7c, 0x80, 0x47, 0x09, 0x55, 0x22, 0x3a, 0x09, + 0x12, 0xaa, 0xc2, 0xd3, 0x50, 0x85, 0x76, 0x0d, 0xe3, 0xb6, 0x0b, 0x71, 0x07, 0xc8, 0x1c, 0x64, + 0x08, 0x1e, 0xb0, 0x96, 0x14, 0xf6, 0xc8, 0x10, 0x1a, 0x2a, 0x14, 0x53, 0x5d, 0x80, 0x3a, 0x9e, + 0x60, 0x17, 0x4e, 0x38, 0x32, 0x1a, 0x86, 0xe6, 0x20, 0x79, 0x0d, 0x2d, 0x3a, 0xa7, 0x09, 0x8f, + 0x43, 0x21, 0xed, 0x06, 0x46, 0x75, 0x0b, 0x51, 0xa3, 0x5c, 0xc5, 0xb8, 0x3b, 0x98, 0x78, 0x50, + 0xbb, 0x98, 0x51, 0x71, 0x69, 0x37, 0x31, 0xaa, 0x53, 0x88, 0xfa, 0xac, 0x95, 0x77, 0x87, 0xfb, + 0x26, 0x51, 0xe4, 0xdc, 0x1f, 0x65, 0x68, 0x2d, 0x6a, 0x45, 0x3a, 0xd0, 0x4c, 0xa2, 0x34, 0x50, + 0x51, 0x42, 0x6d, 0xab, 0x67, 0xf5, 0x2b, 0x7e, 0x23, 0x89, 0xd2, 0xa3, 0x28, 0xa1, 0x28, 0x85, + 0x73, 0x23, 0x95, 0x33, 0x29, 0x9c, 0xa3, 0xf4, 0x1c, 0x36, 0xe4, 0x8c, 0x73, 0x26, 0x94, 0x0c, + 0xe4, 0x79, 0x28, 0x4e, 0xa3, 0x74, 0x8a, 0x4d, 0x69, 0xfa, 0xeb, 0xb9, 0x30, 0xce, 0xf6, 0xc9, + 0x08, 0xb6, 0x17, 0xf0, 0xd7, 0x48, 0x9d, 0xb3, 0x99, 0x0a, 0x04, 0xe5, 0x71, 0x74, 0x12, 0x06, + 0x38, 0x01, 0x12, 0x2b, 0xdd, 0xf4, 0x9f, 0xe4, 0xd8, 0x17, 0x43, 0xf9, 0x06, 0xc2, 0xa9, 0x91, + 0xe4, 0x0d, 0x80, 0x92, 0xa7, 0x93, 0x40, 0x27, 0xa6, 0x2b, 0xab, 0x47, 0x6b, 0xab, 0x58, 0xd9, + 0xf1, 0xfb, 0x5d, 0x9d, 0x54, 0x3e, 0x5e, 0x1a, 0xd7, 0x6b, 0x49, 0x0e, 0xe1, 0xd9, 0x9d, 0x5f, + 0x26, 0xd4, 0xc2, 0x07, 0x9d, 0x2b, 0x2a, 0xd2, 0x30, 0xce, 0x8d, 0x34, 0xd0, 0xc8, 0xd3, 0x45, + 0x0e, 0x4c, 0xa8, 0xcc, 0xcc, 0x28, 0x23, 0x8d, 0x9b, 0x8f, 0xd5, 0x66, 0x75, 0xbd, 0xe6, 0xb6, + 0xa1, 0xb5, 0x18, 0x24, 0x77, 0x13, 0xc8, 0xbf, 0xd3, 0xa1, 0xbf, 0x98, 0xa5, 0x8e, 0xbb, 0x23, + 0x58, 0x2d, 0xb4, 0xf2, 0xff, 0x1a, 0xe0, 0xae, 0xc1, 0xca, 0x72, 0x6f, 0xdd, 0x0b, 0x68, 0xe6, + 0xd9, 0x13, 0x0f, 0xea, 0x59, 0x36, 0x16, 0x8e, 0xc4, 0x83, 0xdf, 0x5f, 0x86, 0x15, 0x2c, 0x94, + 0x1f, 0xb6, 0x50, 0x29, 0x58, 0x18, 0xee, 0x41, 0x15, 0x5f, 0xf7, 0x36, 0xfb, 0x2f, 0x4e, 0xf9, + 0xd2, 0x2d, 0xd1, 0xed, 0xdc, 0xa3, 0x98, 0xfb, 0x62, 0x77, 0xe7, 0xea, 0x8f, 0x53, 0xba, 0xba, + 0x71, 0xac, 0xeb, 0x1b, 0xc7, 0xfa, 0x7d, 0xe3, 0x58, 0xdf, 0x6e, 0x9d, 0xd2, 0xf5, 0xad, 0x53, + 0xfa, 0x79, 0xeb, 0x94, 0x8e, 0xeb, 0xe6, 0xf6, 0x9a, 0xd4, 0xf1, 0xf2, 0x79, 0xf9, 0x37, 0x00, + 0x00, 0xff, 0xff, 0x99, 0x59, 0x64, 0x56, 0xd3, 0x04, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -681,6 +686,16 @@ func (m *StoreInfo) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if m.SupportsSortWithoutExternalLabels { + i-- + if m.SupportsSortWithoutExternalLabels { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x38 + } if len(m.TsdbInfos) > 0 { for iNdEx := len(m.TsdbInfos) - 1; iNdEx >= 0; iNdEx-- { { @@ -983,6 +998,9 @@ func (m *StoreInfo) Size() (n int) { n += 1 + l + sovRpc(uint64(l)) } } + if m.SupportsSortWithoutExternalLabels { + n += 2 + } return n } @@ -1583,6 +1601,26 @@ func (m *StoreInfo) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 7: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field SupportsSortWithoutExternalLabels", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRpc + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.SupportsSortWithoutExternalLabels = bool(v != 0) default: iNdEx = preIndex skippy, err := skipRpc(dAtA[iNdEx:]) diff --git a/pkg/info/infopb/rpc.proto b/pkg/info/infopb/rpc.proto index 9f0db3709d..acd0dae3e7 100644 --- a/pkg/info/infopb/rpc.proto +++ b/pkg/info/infopb/rpc.proto @@ -64,6 +64,10 @@ message StoreInfo { // TSDBInfos holds metadata for all TSDBs exposed by the store. repeated TSDBInfo tsdb_infos = 6 [(gogoproto.nullable) = false]; + + // supports_sort_without_external_labels indicates whether the store ignores external labels + // when comparing two label sets. + bool supports_sort_without_external_labels = 7; } // RulesInfo holds the metadata related to Rules API exposed by the component. diff --git a/pkg/query/endpointset.go b/pkg/query/endpointset.go index a92bcf39b6..c8328aaa27 100644 --- a/pkg/query/endpointset.go +++ b/pkg/query/endpointset.go @@ -843,6 +843,17 @@ func (er *endpointRef) SupportsWithoutReplicaLabels() bool { return er.metadata.Store.SupportsWithoutReplicaLabels } +func (er *endpointRef) SupportsSortWithoutExternalLabels() bool { + er.mtx.RLock() + defer er.mtx.RUnlock() + + if er.metadata == nil || er.metadata.Store == nil { + return false + } + + return er.metadata.Store.SupportsSortWithoutExternalLabels +} + func (er *endpointRef) String() string { mint, maxt := er.TimeRange() return fmt.Sprintf( diff --git a/pkg/query/internal/test-storeset-pre-v0.8.0/storeset.go b/pkg/query/internal/test-storeset-pre-v0.8.0/storeset.go index ddf413bd8d..93da9edff5 100644 --- a/pkg/query/internal/test-storeset-pre-v0.8.0/storeset.go +++ b/pkg/query/internal/test-storeset-pre-v0.8.0/storeset.go @@ -215,6 +215,10 @@ func (s *storeRef) SupportsWithoutReplicaLabels() bool { return false } +func (s *storeRef) SupportsSortWithoutExternalLabels() bool { + return false +} + func (s *storeRef) String() string { mint, maxt := s.TimeRange() return fmt.Sprintf( diff --git a/pkg/receive/multitsdb.go b/pkg/receive/multitsdb.go index 349ceb98ef..da4f551040 100644 --- a/pkg/receive/multitsdb.go +++ b/pkg/receive/multitsdb.go @@ -160,6 +160,10 @@ func (l *localClient) SupportsWithoutReplicaLabels() bool { return true } +func (l *localClient) SupportsSortWithoutExternalLabels() bool { + return true +} + type tenant struct { readyS *ReadyStorage storeTSDB *store.TSDBStore diff --git a/pkg/store/proxy.go b/pkg/store/proxy.go index 7ff18892d9..6a1ccb5bbc 100644 --- a/pkg/store/proxy.go +++ b/pkg/store/proxy.go @@ -64,6 +64,10 @@ type Client interface { // and sorted response is supported by the underlying store. SupportsWithoutReplicaLabels() bool + // SupportsSortWithoutExternalLabels returns true if the store sorts series + // with external labels excluded from the comparison function. + SupportsSortWithoutExternalLabels() bool + // String returns the string representation of the store client. String() string diff --git a/pkg/store/proxy_heap.go b/pkg/store/proxy_heap.go index d5cc940637..1ff0c32d20 100644 --- a/pkg/store/proxy_heap.go +++ b/pkg/store/proxy_heap.go @@ -174,26 +174,13 @@ func (h *ProxyResponseHeap) Less(i, j int) bool { jResp := h.nodes[j].rs.At() if iResp.GetSeries() != nil && jResp.GetSeries() != nil { - // Response sets are sorted before adding external labels. - // This comparison excludes those labels to keep the same order. - iStoreLbls := h.nodes[i].rs.StoreLabels() - jStoreLbls := h.nodes[j].rs.StoreLabels() - + var less bool iLbls := labelpb.ZLabelsToPromLabels(iResp.GetSeries().Labels) jLbls := labelpb.ZLabelsToPromLabels(jResp.GetSeries().Labels) - - copyLabels(&h.iLblsScratch, iLbls) - copyLabels(&h.jLblsScratch, jLbls) - - var iExtLbls, jExtLbls labels.Labels - h.iLblsScratch, iExtLbls = dropLabels(h.iLblsScratch, iStoreLbls) - h.jLblsScratch, jExtLbls = dropLabels(h.jLblsScratch, jStoreLbls) - - c := labels.Compare(h.iLblsScratch, h.jLblsScratch) - if c != 0 { - return c < 0 - } - return labels.Compare(iExtLbls, jExtLbls) < 0 + iStoreLbls := h.nodes[i].rs.StoreLabels() + jStoreLbls := h.nodes[j].rs.StoreLabels() + less, h.iLblsScratch, h.jLblsScratch = compareSeries(iLbls, jLbls, iStoreLbls, jStoreLbls, h.iLblsScratch, h.jLblsScratch) + return less } else if iResp.GetSeries() == nil && jResp.GetSeries() != nil { return true } else if iResp.GetSeries() != nil && jResp.GetSeries() == nil { @@ -205,6 +192,23 @@ func (h *ProxyResponseHeap) Less(i, j int) bool { return false } +func compareSeries(iLbls, jLbls labels.Labels, iStoreLbls, jStoreLbls map[string]struct{}, iLblsScratch, jLblsScratch labels.Labels) (bool, labels.Labels, labels.Labels) { + // Response sets are sorted before adding external labels. + // This comparison excludes those labels to keep the same order. + copyLabels(&iLblsScratch, iLbls) + copyLabels(&jLblsScratch, jLbls) + + var iExtLbls, jExtLbls labels.Labels + iLblsScratch, iExtLbls = dropLabels(iLblsScratch, iStoreLbls) + jLblsScratch, jExtLbls = dropLabels(jLblsScratch, jStoreLbls) + + c := labels.Compare(iLblsScratch, jLblsScratch) + if c != 0 { + return c < 0, iLblsScratch, jLblsScratch + } + return labels.Compare(iExtLbls, jExtLbls) < 0, iLblsScratch, jLblsScratch +} + func (h *ProxyResponseHeap) Len() int { return len(h.nodes) } @@ -575,7 +579,7 @@ func newAsyncRespSet( } var labelsToRemove map[string]struct{} - if !st.SupportsWithoutReplicaLabels() && len(req.WithoutReplicaLabels) > 0 { + if !st.SupportsSortWithoutExternalLabels() || !st.SupportsWithoutReplicaLabels() && len(req.WithoutReplicaLabels) > 0 { level.Warn(logger).Log("msg", "detecting store that does not support without replica label setting. "+ "Falling back to eager retrieval with additional sort. Make sure your storeAPI supports it to speed up your queries", "store", st.String()) retrievalStrategy = EagerRetrieval @@ -770,7 +774,7 @@ func newEagerRespSet( // This should be used only for stores that does not support doing this on server side. // See docs/proposals-accepted/20221129-avoid-global-sort.md for details. if len(l.removeLabels) > 0 { - sortWithoutLabels(l.bufferedResponses, l.removeLabels) + sortWithoutLabels(l.bufferedResponses, l.storeLabels, l.removeLabels) } }(st, ret) @@ -819,29 +823,59 @@ func copyLabels(dest *labels.Labels, src labels.Labels) { // sortWithoutLabels removes given labels from series and re-sorts the series responses that the same // series with different labels are coming right after each other. Other types of responses are moved to front. -func sortWithoutLabels(set []*storepb.SeriesResponse, labelsToRemove map[string]struct{}) { - for _, s := range set { +func sortWithoutLabels(set []*storepb.SeriesResponse, storeLabels map[string]struct{}, labelsToRemove map[string]struct{}) { + extLabels := make([]labels.Labels, len(set)) + lblScratch := labels.Labels{} + for i, s := range set { ser := s.GetSeries() if ser == nil { continue } - ser.Labels = labelpb.ZLabelsFromPromLabels(rmLabels(labelpb.ZLabelsToPromLabels(ser.Labels), labelsToRemove)) + internalLabels := rmLabels(labelpb.ZLabelsToPromLabels(ser.Labels), labelsToRemove) + internalLabels, lblScratch = dropLabels(internalLabels, storeLabels) + ser.Labels = labelpb.ZLabelsFromPromLabels(internalLabels) + extLabels[i] = lblScratch } // With the re-ordered label sets, re-sorting all series aligns the same series // from different replicas sequentially. + sort.Slice(extLabels, func(i, j int) bool { + return compareResponses(set, extLabels, i, j) + }) sort.Slice(set, func(i, j int) bool { - si := set[i].GetSeries() - if si == nil { - return true - } - sj := set[j].GetSeries() - if sj == nil { - return false - } - return labels.Compare(labelpb.ZLabelsToPromLabels(si.Labels), labelpb.ZLabelsToPromLabels(sj.Labels)) < 0 + return compareResponses(set, extLabels, i, j) }) + + for i, _ := range set { + ser := set[i].GetSeries() + if ser == nil { + continue + } + + ser.Labels = labelpb.ZLabelsFromPromLabels( + labelpb.ExtendSortedLabels(labelpb.ZLabelsToPromLabels(ser.Labels), extLabels[i]), + ) + } +} + +func compareResponses(set []*storepb.SeriesResponse, extLabels []labels.Labels, i int, j int) bool { + si := set[i].GetSeries() + if si == nil { + return true + } + sj := set[j].GetSeries() + if sj == nil { + return false + } + + iLbls := labelpb.ZLabelsToPromLabels(si.Labels) + jLbls := labelpb.ZLabelsToPromLabels(sj.Labels) + order := labels.Compare(iLbls, jLbls) + if order == 0 { + return labels.Compare(extLabels[i], extLabels[j]) < 0 + } + return order < 0 } func (l *eagerRespSet) Close() { diff --git a/pkg/store/proxy_heap_test.go b/pkg/store/proxy_heap_test.go index fdfec178ca..acaf420fa1 100644 --- a/pkg/store/proxy_heap_test.go +++ b/pkg/store/proxy_heap_test.go @@ -4,6 +4,7 @@ package store import ( + "fmt" "sync" "testing" @@ -205,9 +206,10 @@ func TestProxyResponseHeapSort(t *testing.T) { } func TestSortWithoutLabels(t *testing.T) { - for _, tcase := range []struct { + for i, tcase := range []struct { input []*storepb.SeriesResponse exp []*storepb.SeriesResponse + extLset map[string]struct{} dedupLabels map[string]struct{} }{ // Single deduplication label. @@ -297,9 +299,26 @@ func TestSortWithoutLabels(t *testing.T) { }, dedupLabels: map[string]struct{}{"replica": {}}, }, + // Stores with external labels. + { + input: []*storepb.SeriesResponse{ + storeSeriesResponse(t, labelsFromStrings("a", "1", "b", "replica-1", "c", "3", "d", "4")), + storeSeriesResponse(t, labelsFromStrings("a", "1", "b", "replica-1", "c", "1")), + storeSeriesResponse(t, labelsFromStrings("a", "2", "b", "replica-1", "c", "4")), + storeSeriesResponse(t, labelsFromStrings("a", "2", "b", "replica-2", "c", "3")), + }, + exp: []*storepb.SeriesResponse{ + storeSeriesResponse(t, labelsFromStrings("a", "1", "c", "1")), + storeSeriesResponse(t, labelsFromStrings("a", "1", "c", "3", "d", "4")), + storeSeriesResponse(t, labelsFromStrings("a", "2", "c", "3")), + storeSeriesResponse(t, labelsFromStrings("a", "2", "c", "4")), + }, + dedupLabels: map[string]struct{}{"b": {}}, + extLset: map[string]struct{}{"c": {}}, + }, } { - t.Run("", func(t *testing.T) { - sortWithoutLabels(tcase.input, tcase.dedupLabels) + t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { + sortWithoutLabels(tcase.input, tcase.extLset, tcase.dedupLabels) testutil.Equals(t, tcase.exp, tcase.input) }) } @@ -332,6 +351,6 @@ func BenchmarkSortWithoutLabels(b *testing.B) { resps[i] = storeSeriesResponse(b, labels.FromStrings("a", "1", "b", "replica-1", "c", "replica-1", "d", "1")) } b.StartTimer() - sortWithoutLabels(resps, labelsToRemove) + sortWithoutLabels(resps, nil, labelsToRemove) } } diff --git a/pkg/store/proxy_test.go b/pkg/store/proxy_test.go index f2c4aba47a..cbbe22fff8 100644 --- a/pkg/store/proxy_test.go +++ b/pkg/store/proxy_test.go @@ -629,6 +629,7 @@ func TestProxyStore_Series(t *testing.T) { for _, s := range tc.storeAPIs { cl := s.(*storetestutil.TestClient) cl.WithoutReplicaLabelsEnabled = replicaLabelSupport + cl.SortWithoutExtLabels = true } for _, strategy := range []RetrievalStrategy{EagerRetrieval, LazyRetrieval} { t.Run(string(strategy), func(t *testing.T) { diff --git a/pkg/store/storepb/testutil/client.go b/pkg/store/storepb/testutil/client.go index 90874842d6..625ba5f803 100644 --- a/pkg/store/storepb/testutil/client.go +++ b/pkg/store/storepb/testutil/client.go @@ -19,14 +19,17 @@ type TestClient struct { MinTime, MaxTime int64 Shardable bool WithoutReplicaLabelsEnabled bool + SortWithoutExtLabels bool IsLocalStore bool StoreTSDBInfos []infopb.TSDBInfo } -func (c TestClient) LabelSets() []labels.Labels { return c.ExtLset } -func (c TestClient) TimeRange() (mint, maxt int64) { return c.MinTime, c.MaxTime } -func (c TestClient) TSDBInfos() []infopb.TSDBInfo { return c.StoreTSDBInfos } -func (c TestClient) SupportsSharding() bool { return c.Shardable } -func (c TestClient) SupportsWithoutReplicaLabels() bool { return c.WithoutReplicaLabelsEnabled } -func (c TestClient) String() string { return c.Name } -func (c TestClient) Addr() (string, bool) { return c.Name, c.IsLocalStore } +func (c TestClient) LabelSets() []labels.Labels { return c.ExtLset } + +func (c TestClient) TimeRange() (mint, maxt int64) { return c.MinTime, c.MaxTime } +func (c TestClient) TSDBInfos() []infopb.TSDBInfo { return c.StoreTSDBInfos } +func (c TestClient) SupportsSharding() bool { return c.Shardable } +func (c TestClient) SupportsWithoutReplicaLabels() bool { return c.WithoutReplicaLabelsEnabled } +func (c TestClient) SupportsSortWithoutExternalLabels() bool { return c.SortWithoutExtLabels } +func (c TestClient) String() string { return c.Name } +func (c TestClient) Addr() (string, bool) { return c.Name, c.IsLocalStore }