github.com/grafana/pyroscope@v1.18.0/pkg/querier/analyze_query.go (about) 1 package querier 2 3 import ( 4 "context" 5 6 "connectrpc.com/connect" 7 "github.com/grafana/dskit/tenant" 8 "github.com/opentracing/opentracing-go" 9 "github.com/prometheus/common/model" 10 "github.com/prometheus/prometheus/model/labels" 11 "github.com/prometheus/prometheus/promql/parser" 12 13 ingestv1 "github.com/grafana/pyroscope/api/gen/proto/go/ingester/v1" 14 querierv1 "github.com/grafana/pyroscope/api/gen/proto/go/querier/v1" 15 phlaremodel "github.com/grafana/pyroscope/pkg/model" 16 ) 17 18 type queryScope struct { 19 *querierv1.QueryScope 20 21 blockIds []string 22 } 23 24 func (q *Querier) AnalyzeQuery(ctx context.Context, req *connect.Request[querierv1.AnalyzeQueryRequest]) (*connect.Response[querierv1.AnalyzeQueryResponse], error) { 25 sp, ctx := opentracing.StartSpanFromContext(ctx, "AnalyzeQuery") 26 defer sp.Finish() 27 28 plan, err := q.blockSelect(ctx, model.Time(req.Msg.Start), model.Time(req.Msg.End)) 29 ingesterQueryScope, storeGatewayQueryScope, deduplicationNeeded := getDataFromPlan(plan) 30 31 blockStatsFromReplicas, err := q.getBlockStatsFromIngesters(ctx, plan, ingesterQueryScope.blockIds) 32 if err != nil { 33 return nil, err 34 } 35 addBlockStatsToQueryScope(blockStatsFromReplicas, ingesterQueryScope) 36 37 if q.storeGatewayQuerier != nil { 38 blockStatsFromReplicas, err = q.getBlockStatsFromStoreGateways(ctx, plan, storeGatewayQueryScope.blockIds) 39 if err != nil { 40 return nil, err 41 } 42 addBlockStatsToQueryScope(blockStatsFromReplicas, storeGatewayQueryScope) 43 } 44 45 queriedSeries, err := q.getQueriedSeriesCount(ctx, req.Msg) 46 if err != nil { 47 return nil, err 48 } 49 50 res := createResponse(ingesterQueryScope, storeGatewayQueryScope) 51 res.QueryImpact.DeduplicationNeeded = deduplicationNeeded 52 res.QueryImpact.TotalQueriedSeries = queriedSeries 53 54 return connect.NewResponse(res), nil 55 } 56 57 func getDataFromPlan(plan blockPlan) (ingesterQueryScope *queryScope, storeGatewayQueryScope *queryScope, deduplicationNeeded bool) { 58 ingesterQueryScope = &queryScope{ 59 QueryScope: &querierv1.QueryScope{ 60 ComponentType: "Short term storage", 61 }, 62 } 63 storeGatewayQueryScope = &queryScope{ 64 QueryScope: &querierv1.QueryScope{ 65 ComponentType: "Long term storage", 66 }, 67 } 68 deduplicationNeeded = false 69 for _, planEntry := range plan { 70 deduplicationNeeded = deduplicationNeeded || planEntry.Deduplication 71 for _, t := range planEntry.InstanceTypes { 72 if t == ingesterInstance { 73 ingesterQueryScope.ComponentCount += 1 74 ingesterQueryScope.BlockCount += uint64(len(planEntry.Ulids)) 75 ingesterQueryScope.blockIds = append(ingesterQueryScope.blockIds, planEntry.Ulids...) 76 } else { 77 storeGatewayQueryScope.ComponentCount += 1 78 storeGatewayQueryScope.BlockCount += uint64(len(planEntry.Ulids)) 79 storeGatewayQueryScope.blockIds = append(storeGatewayQueryScope.blockIds, planEntry.Ulids...) 80 } 81 } 82 } 83 return ingesterQueryScope, storeGatewayQueryScope, deduplicationNeeded 84 } 85 86 func (q *Querier) getBlockStatsFromIngesters(ctx context.Context, plan blockPlan, ingesterBlockIds []string) ([]ResponseFromReplica[*ingestv1.GetBlockStatsResponse], error) { 87 var blockStatsFromReplicas []ResponseFromReplica[*ingestv1.GetBlockStatsResponse] 88 blockStatsFromReplicas, err := forAllPlannedIngesters(ctx, q.ingesterQuerier, plan, func(ctx context.Context, iq IngesterQueryClient, hint *ingestv1.Hints) (*ingestv1.GetBlockStatsResponse, error) { 89 stats, err := iq.GetBlockStats(ctx, connect.NewRequest(&ingestv1.GetBlockStatsRequest{Ulids: ingesterBlockIds})) 90 if err != nil { 91 return nil, err 92 } 93 return stats.Msg, err 94 }) 95 return blockStatsFromReplicas, err 96 } 97 98 func (q *Querier) getBlockStatsFromStoreGateways(ctx context.Context, plan blockPlan, storeGatewayBlockIds []string) ([]ResponseFromReplica[*ingestv1.GetBlockStatsResponse], error) { 99 tenantId, err := tenant.TenantID(ctx) 100 if err != nil { 101 return nil, err 102 } 103 blockStatsFromReplicas, err := forAllPlannedStoreGateways(ctx, tenantId, q.storeGatewayQuerier, plan, func(ctx context.Context, sq StoreGatewayQueryClient, hint *ingestv1.Hints) (*ingestv1.GetBlockStatsResponse, error) { 104 stats, err := sq.GetBlockStats(ctx, connect.NewRequest(&ingestv1.GetBlockStatsRequest{Ulids: storeGatewayBlockIds})) 105 if err != nil { 106 return nil, err 107 } 108 return stats.Msg, err 109 }) 110 return blockStatsFromReplicas, nil 111 } 112 113 func addBlockStatsToQueryScope(blockStatsFromReplicas []ResponseFromReplica[*ingestv1.GetBlockStatsResponse], queryScope *queryScope) { 114 for _, r := range blockStatsFromReplicas { 115 for _, stats := range r.response.BlockStats { 116 queryScope.SeriesCount += stats.SeriesCount 117 queryScope.ProfileCount += stats.ProfileCount 118 queryScope.SampleCount += stats.SampleCount 119 queryScope.IndexBytes += stats.IndexBytes 120 queryScope.ProfileBytes += stats.ProfileBytes 121 queryScope.SymbolBytes += stats.SymbolBytes 122 } 123 } 124 } 125 126 func (q *Querier) getQueriedSeriesCount(ctx context.Context, req *querierv1.AnalyzeQueryRequest) (uint64, error) { 127 tenantId, err := tenant.TenantID(ctx) 128 if err != nil { 129 return 0, err 130 } 131 if !q.limits.QueryAnalysisSeriesEnabled(tenantId) { 132 return 0, nil 133 } 134 matchers, err := createMatchersFromQuery(req.Query) 135 if err != nil { 136 return 0, err 137 } 138 resSeries, err := q.Series(ctx, connect.NewRequest(&querierv1.SeriesRequest{ 139 Matchers: matchers, 140 Start: req.Start, 141 End: req.End, 142 })) 143 if err != nil { 144 return 0, err 145 } 146 return uint64(len(resSeries.Msg.LabelsSet)), nil 147 } 148 149 func createMatchersFromQuery(query string) ([]string, error) { 150 var matchers []*labels.Matcher 151 var err error 152 if query != "" { 153 matchers, err = parser.ParseMetricSelector(query) 154 if err != nil { 155 return nil, err 156 } 157 } 158 for _, matcher := range matchers { 159 if matcher.Name == labels.MetricName { 160 matcher.Name = phlaremodel.LabelNameProfileType 161 } 162 } 163 return []string{convertMatchersToString(matchers)}, nil 164 } 165 166 func createResponse(ingesterQueryScope *queryScope, storeGatewayQueryScope *queryScope) *querierv1.AnalyzeQueryResponse { 167 totalBytes := ingesterQueryScope.IndexBytes + 168 ingesterQueryScope.ProfileBytes + 169 ingesterQueryScope.SymbolBytes + 170 storeGatewayQueryScope.IndexBytes + 171 storeGatewayQueryScope.ProfileBytes + 172 storeGatewayQueryScope.SymbolBytes 173 174 return &querierv1.AnalyzeQueryResponse{ 175 QueryScopes: []*querierv1.QueryScope{ingesterQueryScope.QueryScope, storeGatewayQueryScope.QueryScope}, 176 QueryImpact: &querierv1.QueryImpact{ 177 TotalBytesInTimeRange: totalBytes, 178 }, 179 } 180 }