github.com/grafana/pyroscope@v1.18.0/pkg/frontend/readpath/queryfrontend/query_series_labels_compat.go (about) 1 package queryfrontend 2 3 import ( 4 "context" 5 "fmt" 6 "slices" 7 "sort" 8 9 "connectrpc.com/connect" 10 "github.com/go-kit/log/level" 11 "github.com/pkg/errors" 12 13 metastorev1 "github.com/grafana/pyroscope/api/gen/proto/go/metastore/v1" 14 querierv1 "github.com/grafana/pyroscope/api/gen/proto/go/querier/v1" 15 typesv1 "github.com/grafana/pyroscope/api/gen/proto/go/types/v1" 16 phlaremodel "github.com/grafana/pyroscope/pkg/model" 17 ) 18 19 var ( 20 errMissingServiceName = errors.New("service name is missing") 21 errMissingProfileType = errors.New("profile type is missing") 22 errInvalidProfileType = errors.New("invalid profile type") 23 ) 24 25 var profileTypeLabels2 = []string{ 26 phlaremodel.LabelNameProfileType, 27 phlaremodel.LabelNameServiceName, 28 } 29 30 var profileTypeLabels5 = []string{ 31 phlaremodel.LabelNameProfileName, 32 phlaremodel.LabelNameProfileType, 33 phlaremodel.LabelNameType, 34 "pyroscope_app", 35 phlaremodel.LabelNameServiceName, 36 } 37 38 func (q *QueryFrontend) isProfileTypeQuery(labels, matchers []string) bool { 39 if len(matchers) > 0 { 40 return false 41 } 42 var s []string 43 switch len(labels) { 44 case 2: 45 s = profileTypeLabels2 46 case 5: 47 s = profileTypeLabels5 48 default: 49 return false 50 } 51 sort.Strings(labels) 52 return slices.Compare(s, labels) == 0 53 } 54 55 func (q *QueryFrontend) queryProfileTypeMetadataLabels( 56 ctx context.Context, 57 tenants []string, 58 startTime int64, 59 endTime int64, 60 labels []string, 61 ) (*connect.Response[querierv1.SeriesResponse], error) { 62 meta, err := q.metadataQueryClient.QueryMetadataLabels(ctx, &metastorev1.QueryMetadataLabelsRequest{ 63 TenantId: tenants, 64 StartTime: startTime, 65 EndTime: endTime, 66 Query: "{}", 67 Labels: []string{ 68 phlaremodel.LabelNameServiceName, 69 phlaremodel.LabelNameProfileType, 70 }, 71 }) 72 if err != nil { 73 return nil, err 74 } 75 meta.Labels = q.buildProfileTypeMetadataLabels(meta.Labels, labels) 76 return connect.NewResponse(&querierv1.SeriesResponse{LabelsSet: meta.Labels}), nil 77 } 78 79 func (q *QueryFrontend) buildProfileTypeMetadataLabels(labels []*typesv1.Labels, names []string) []*typesv1.Labels { 80 for _, ls := range labels { 81 if err := sanitizeProfileTypeMetadataLabels(ls, names); err != nil { 82 level.Warn(q.logger).Log("msg", "malformed label set", "labels", phlaremodel.LabelPairsString(ls.Labels), "err", err) 83 ls.Labels = nil 84 } 85 } 86 labels = slices.DeleteFunc(labels, func(ls *typesv1.Labels) bool { 87 return len(ls.Labels) == 0 88 }) 89 slices.SortFunc(labels, func(a, b *typesv1.Labels) int { 90 return phlaremodel.CompareLabelPairs(a.Labels, b.Labels) 91 }) 92 return labels 93 } 94 95 func sanitizeProfileTypeMetadataLabels(ls *typesv1.Labels, names []string) error { 96 var serviceName, profileType string 97 for _, l := range ls.Labels { 98 switch l.Name { 99 case phlaremodel.LabelNameServiceName: 100 serviceName = l.Value 101 case phlaremodel.LabelNameProfileType: 102 profileType = l.Value 103 } 104 } 105 if serviceName == "" { 106 return errMissingServiceName 107 } 108 if profileType == "" { 109 return errMissingProfileType 110 } 111 pt, err := phlaremodel.ParseProfileTypeSelector(profileType) 112 if err != nil { 113 return fmt.Errorf("%w: %w", errInvalidProfileType, err) 114 } 115 if len(names) == 5 { 116 // Replace the labels with the expected ones. 117 ls.Labels = append(ls.Labels[:0], []*typesv1.LabelPair{ 118 {Name: phlaremodel.LabelNameProfileType, Value: profileType}, 119 {Name: phlaremodel.LabelNameServiceName, Value: serviceName}, 120 {Name: phlaremodel.LabelNameProfileName, Value: pt.Name}, 121 {Name: phlaremodel.LabelNameType, Value: pt.SampleType}, 122 }...) 123 } 124 sort.Sort(phlaremodel.Labels(ls.Labels)) 125 return nil 126 }