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  }