github.com/grafana/pyroscope@v1.18.0/pkg/querier/ingester_querier.go (about) 1 package querier 2 3 import ( 4 "context" 5 6 "connectrpc.com/connect" 7 "github.com/grafana/dskit/ring" 8 ring_client "github.com/grafana/dskit/ring/client" 9 "github.com/opentracing/opentracing-go" 10 otlog "github.com/opentracing/opentracing-go/log" 11 "github.com/prometheus/prometheus/promql/parser" 12 "golang.org/x/sync/errgroup" 13 14 googlev1 "github.com/grafana/pyroscope/api/gen/proto/go/google/v1" 15 ingesterv1 "github.com/grafana/pyroscope/api/gen/proto/go/ingester/v1" 16 querierv1 "github.com/grafana/pyroscope/api/gen/proto/go/querier/v1" 17 typesv1 "github.com/grafana/pyroscope/api/gen/proto/go/types/v1" 18 "github.com/grafana/pyroscope/pkg/clientpool" 19 phlaremodel "github.com/grafana/pyroscope/pkg/model" 20 "github.com/grafana/pyroscope/pkg/util" 21 ) 22 23 type IngesterQueryClient interface { 24 LabelValues(context.Context, *connect.Request[typesv1.LabelValuesRequest]) (*connect.Response[typesv1.LabelValuesResponse], error) 25 LabelNames(context.Context, *connect.Request[typesv1.LabelNamesRequest]) (*connect.Response[typesv1.LabelNamesResponse], error) 26 ProfileTypes(context.Context, *connect.Request[ingesterv1.ProfileTypesRequest]) (*connect.Response[ingesterv1.ProfileTypesResponse], error) 27 Series(ctx context.Context, req *connect.Request[ingesterv1.SeriesRequest]) (*connect.Response[ingesterv1.SeriesResponse], error) 28 MergeProfilesStacktraces(context.Context) clientpool.BidiClientMergeProfilesStacktraces 29 MergeProfilesLabels(ctx context.Context) clientpool.BidiClientMergeProfilesLabels 30 MergeProfilesPprof(ctx context.Context) clientpool.BidiClientMergeProfilesPprof 31 MergeSpanProfile(ctx context.Context) clientpool.BidiClientMergeSpanProfile 32 BlockMetadata(ctx context.Context, req *connect.Request[ingesterv1.BlockMetadataRequest]) (*connect.Response[ingesterv1.BlockMetadataResponse], error) 33 GetProfileStats(ctx context.Context, req *connect.Request[typesv1.GetProfileStatsRequest]) (*connect.Response[typesv1.GetProfileStatsResponse], error) 34 GetBlockStats(ctx context.Context, req *connect.Request[ingesterv1.GetBlockStatsRequest]) (*connect.Response[ingesterv1.GetBlockStatsResponse], error) 35 } 36 37 // IngesterQuerier helps with querying the ingesters. 38 type IngesterQuerier struct { 39 ring ring.ReadRing 40 pool *ring_client.Pool 41 } 42 43 func NewIngesterQuerier(pool *ring_client.Pool, ring ring.ReadRing) *IngesterQuerier { 44 return &IngesterQuerier{ 45 ring: ring, 46 pool: pool, 47 } 48 } 49 50 // readNoExtend is a ring.Operation that only selects instances marked as ring.ACTIVE. 51 // This should mirror the operation used when choosing ingesters to write series to (ring.WriteNoExtend). 52 var readNoExtend = ring.NewOp([]ring.InstanceState{ring.ACTIVE}, nil) 53 54 // forAllIngesters runs f, in parallel, for all ingesters 55 func forAllIngesters[T any](ctx context.Context, ingesterQuerier *IngesterQuerier, f QueryReplicaFn[T, IngesterQueryClient]) ([]ResponseFromReplica[T], error) { 56 replicationSet, err := ingesterQuerier.ring.GetReplicationSetForOperation(readNoExtend) 57 if err != nil { 58 return nil, err 59 } 60 61 clientFactoryFn := func(addr string) (IngesterQueryClient, error) { 62 client, err := ingesterQuerier.pool.GetClientFor(addr) 63 if err != nil { 64 return nil, err 65 } 66 return client.(IngesterQueryClient), nil 67 } 68 return forGivenReplicationSet(ctx, clientFactoryFn, replicationSet, f) 69 } 70 71 // forAllPlannedIngesters runs f, in parallel, for all ingesters part of the plan 72 func forAllPlannedIngesters[T any](ctx context.Context, ingesterQuerier *IngesterQuerier, plan blockPlan, f QueryReplicaWithHintsFn[T, IngesterQueryClient]) ([]ResponseFromReplica[T], error) { 73 replicationSet, err := ingesterQuerier.ring.GetReplicationSetForOperation(readNoExtend) 74 if err != nil { 75 return nil, err 76 } 77 78 return forGivenPlan(ctx, plan, func(addr string) (IngesterQueryClient, error) { 79 client, err := ingesterQuerier.pool.GetClientFor(addr) 80 if err != nil { 81 return nil, err 82 } 83 return client.(IngesterQueryClient), nil 84 }, replicationSet, f) 85 } 86 87 func (q *Querier) selectTreeFromIngesters(ctx context.Context, req *querierv1.SelectMergeStacktracesRequest, plan blockPlan) (*phlaremodel.Tree, error) { 88 sp, ctx := opentracing.StartSpanFromContext(ctx, "SelectTree Ingesters") 89 defer sp.Finish() 90 profileType, err := phlaremodel.ParseProfileTypeSelector(req.ProfileTypeID) 91 if err != nil { 92 return nil, connect.NewError(connect.CodeInvalidArgument, err) 93 } 94 _, err = parser.ParseMetricSelector(req.LabelSelector) 95 if err != nil { 96 return nil, connect.NewError(connect.CodeInvalidArgument, err) 97 } 98 ctx, cancel := context.WithCancel(ctx) 99 defer cancel() 100 101 var responses []ResponseFromReplica[clientpool.BidiClientMergeProfilesStacktraces] 102 if plan != nil { 103 responses, err = forAllPlannedIngesters(ctx, q.ingesterQuerier, plan, func(ctx context.Context, ic IngesterQueryClient, hints *ingesterv1.Hints) (clientpool.BidiClientMergeProfilesStacktraces, error) { 104 return ic.MergeProfilesStacktraces(ctx), nil 105 }) 106 } else { 107 responses, err = forAllIngesters(ctx, q.ingesterQuerier, func(ctx context.Context, ic IngesterQueryClient) (clientpool.BidiClientMergeProfilesStacktraces, error) { 108 return ic.MergeProfilesStacktraces(ctx), nil 109 }) 110 } 111 if err != nil { 112 return nil, connect.NewError(connect.CodeInternal, err) 113 } 114 // send the first initial request to all ingesters. 115 g, _ := errgroup.WithContext(ctx) 116 for idx := range responses { 117 r := responses[idx] 118 blockHints, err := BlockHints(plan, r.addr) 119 if err != nil { 120 return nil, connect.NewError(connect.CodeInternal, err) 121 } 122 123 g.Go(util.RecoverPanic(func() error { 124 return r.response.Send(&ingesterv1.MergeProfilesStacktracesRequest{ 125 Request: &ingesterv1.SelectProfilesRequest{ 126 LabelSelector: req.LabelSelector, 127 Start: req.Start, 128 End: req.End, 129 Type: profileType, 130 Hints: &ingesterv1.Hints{Block: blockHints}, 131 }, 132 MaxNodes: req.MaxNodes, 133 }) 134 })) 135 } 136 if err = g.Wait(); err != nil { 137 return nil, connect.NewError(connect.CodeInternal, err) 138 } 139 140 // merge all profiles 141 return selectMergeTree(ctx, responses) 142 } 143 144 func (q *Querier) selectProfileFromIngesters(ctx context.Context, req *querierv1.SelectMergeProfileRequest, plan blockPlan) (*googlev1.Profile, error) { 145 span, ctx := opentracing.StartSpanFromContext(ctx, "SelectProfile Ingesters") 146 defer span.Finish() 147 profileType, err := phlaremodel.ParseProfileTypeSelector(req.ProfileTypeID) 148 if err != nil { 149 return nil, connect.NewError(connect.CodeInvalidArgument, err) 150 } 151 _, err = parser.ParseMetricSelector(req.LabelSelector) 152 if err != nil { 153 return nil, connect.NewError(connect.CodeInvalidArgument, err) 154 } 155 ctx, cancel := context.WithCancel(ctx) 156 defer cancel() 157 158 var responses []ResponseFromReplica[clientpool.BidiClientMergeProfilesPprof] 159 if plan != nil { 160 responses, err = forAllPlannedIngesters(ctx, q.ingesterQuerier, plan, func(ctx context.Context, ic IngesterQueryClient, hints *ingesterv1.Hints) (clientpool.BidiClientMergeProfilesPprof, error) { 161 return ic.MergeProfilesPprof(ctx), nil 162 }) 163 } else { 164 responses, err = forAllIngesters(ctx, q.ingesterQuerier, func(ctx context.Context, ic IngesterQueryClient) (clientpool.BidiClientMergeProfilesPprof, error) { 165 return ic.MergeProfilesPprof(ctx), nil 166 }) 167 } 168 if err != nil { 169 return nil, connect.NewError(connect.CodeInternal, err) 170 } 171 // send the first initial request to all ingesters. 172 g, _ := errgroup.WithContext(ctx) 173 for idx := range responses { 174 r := responses[idx] 175 blockHints, err := BlockHints(plan, r.addr) 176 if err != nil { 177 return nil, connect.NewError(connect.CodeInternal, err) 178 } 179 180 g.Go(util.RecoverPanic(func() error { 181 return r.response.Send(&ingesterv1.MergeProfilesPprofRequest{ 182 Request: &ingesterv1.SelectProfilesRequest{ 183 LabelSelector: req.LabelSelector, 184 Start: req.Start, 185 End: req.End, 186 Type: profileType, 187 Hints: &ingesterv1.Hints{Block: blockHints}, 188 }, 189 MaxNodes: req.MaxNodes, 190 StackTraceSelector: req.StackTraceSelector, 191 }) 192 })) 193 } 194 if err = g.Wait(); err != nil { 195 return nil, connect.NewError(connect.CodeInternal, err) 196 } 197 198 // merge all profiles 199 span.LogFields(otlog.String("msg", "selectMergePprofProfile")) 200 return selectMergePprofProfile(ctx, profileType, responses) 201 } 202 203 func (q *Querier) selectSeriesFromIngesters(ctx context.Context, req *ingesterv1.MergeProfilesLabelsRequest, plan map[string]*blockPlanEntry) ([]ResponseFromReplica[clientpool.BidiClientMergeProfilesLabels], error) { 204 sp, ctx := opentracing.StartSpanFromContext(ctx, "SelectSeries Ingesters") 205 defer sp.Finish() 206 var responses []ResponseFromReplica[clientpool.BidiClientMergeProfilesLabels] 207 var err error 208 209 if plan != nil { 210 responses, err = forAllPlannedIngesters(ctx, q.ingesterQuerier, plan, func(ctx context.Context, q IngesterQueryClient, hint *ingesterv1.Hints) (clientpool.BidiClientMergeProfilesLabels, error) { 211 return q.MergeProfilesLabels(ctx), nil 212 }) 213 } else { 214 responses, err = forAllIngesters(ctx, q.ingesterQuerier, func(ctx context.Context, ic IngesterQueryClient) (clientpool.BidiClientMergeProfilesLabels, error) { 215 return ic.MergeProfilesLabels(ctx), nil 216 }) 217 } 218 if err != nil { 219 return nil, connect.NewError(connect.CodeInternal, err) 220 } 221 // send the first initial request to all ingesters. 222 g, _ := errgroup.WithContext(ctx) 223 for _, r := range responses { 224 r := r 225 blockHints, err := BlockHints(plan, r.addr) 226 if err != nil { 227 return nil, connect.NewError(connect.CodeInternal, err) 228 } 229 g.Go(util.RecoverPanic(func() error { 230 req := req.CloneVT() 231 req.Request.Hints = &ingesterv1.Hints{Block: blockHints} 232 return r.response.Send(req) 233 })) 234 } 235 if err := g.Wait(); err != nil { 236 return nil, connect.NewError(connect.CodeInternal, err) 237 } 238 return responses, nil 239 } 240 241 func (q *Querier) labelValuesFromIngesters(ctx context.Context, req *typesv1.LabelValuesRequest) ([]ResponseFromReplica[[]string], error) { 242 sp, ctx := opentracing.StartSpanFromContext(ctx, "LabelValues Ingesters") 243 defer sp.Finish() 244 245 responses, err := forAllIngesters(ctx, q.ingesterQuerier, func(childCtx context.Context, ic IngesterQueryClient) ([]string, error) { 246 res, err := ic.LabelValues(childCtx, connect.NewRequest(req)) 247 if err != nil { 248 return nil, err 249 } 250 return res.Msg.Names, nil 251 }) 252 if err != nil { 253 return nil, connect.NewError(connect.CodeInternal, err) 254 } 255 return responses, nil 256 } 257 258 func (q *Querier) labelNamesFromIngesters(ctx context.Context, req *typesv1.LabelNamesRequest) ([]ResponseFromReplica[[]string], error) { 259 sp, ctx := opentracing.StartSpanFromContext(ctx, "LabelNames Ingesters") 260 defer sp.Finish() 261 262 responses, err := forAllIngesters(ctx, q.ingesterQuerier, func(childCtx context.Context, ic IngesterQueryClient) ([]string, error) { 263 res, err := ic.LabelNames(childCtx, connect.NewRequest(req)) 264 if err != nil { 265 return nil, err 266 } 267 return res.Msg.Names, nil 268 }) 269 if err != nil { 270 return nil, connect.NewError(connect.CodeInternal, err) 271 } 272 return responses, nil 273 } 274 275 func (q *Querier) seriesFromIngesters(ctx context.Context, req *ingesterv1.SeriesRequest) ([]ResponseFromReplica[[]*typesv1.Labels], error) { 276 sp, ctx := opentracing.StartSpanFromContext(ctx, "Series Ingesters") 277 defer sp.Finish() 278 279 responses, err := forAllIngesters(ctx, q.ingesterQuerier, func(childCtx context.Context, ic IngesterQueryClient) ([]*typesv1.Labels, error) { 280 res, err := ic.Series(childCtx, connect.NewRequest(&ingesterv1.SeriesRequest{ 281 Matchers: req.Matchers, 282 LabelNames: req.LabelNames, 283 Start: req.Start, 284 End: req.End, 285 })) 286 if err != nil { 287 return nil, err 288 } 289 return res.Msg.LabelsSet, nil 290 }) 291 if err != nil { 292 return nil, connect.NewError(connect.CodeInternal, err) 293 } 294 return responses, nil 295 } 296 297 func (q *Querier) selectSpanProfileFromIngesters(ctx context.Context, req *querierv1.SelectMergeSpanProfileRequest, plan map[string]*blockPlanEntry) (*phlaremodel.Tree, error) { 298 sp, ctx := opentracing.StartSpanFromContext(ctx, "SelectMergeSpanProfile Ingesters") 299 defer sp.Finish() 300 profileType, err := phlaremodel.ParseProfileTypeSelector(req.ProfileTypeID) 301 if err != nil { 302 return nil, connect.NewError(connect.CodeInvalidArgument, err) 303 } 304 _, err = parser.ParseMetricSelector(req.LabelSelector) 305 if err != nil { 306 return nil, connect.NewError(connect.CodeInvalidArgument, err) 307 } 308 ctx, cancel := context.WithCancel(ctx) 309 defer cancel() 310 311 var responses []ResponseFromReplica[clientpool.BidiClientMergeSpanProfile] 312 if plan != nil { 313 responses, err = forAllPlannedIngesters(ctx, q.ingesterQuerier, plan, func(ctx context.Context, ic IngesterQueryClient, hints *ingesterv1.Hints) (clientpool.BidiClientMergeSpanProfile, error) { 314 return ic.MergeSpanProfile(ctx), nil 315 }) 316 } else { 317 responses, err = forAllIngesters(ctx, q.ingesterQuerier, func(ctx context.Context, ic IngesterQueryClient) (clientpool.BidiClientMergeSpanProfile, error) { 318 return ic.MergeSpanProfile(ctx), nil 319 }) 320 } 321 if err != nil { 322 return nil, connect.NewError(connect.CodeInternal, err) 323 } 324 // send the first initial request to all ingesters. 325 g, _ := errgroup.WithContext(ctx) 326 for idx := range responses { 327 r := responses[idx] 328 blockHints, err := BlockHints(plan, r.addr) 329 if err != nil { 330 return nil, connect.NewError(connect.CodeInternal, err) 331 } 332 333 g.Go(util.RecoverPanic(func() error { 334 return r.response.Send(&ingesterv1.MergeSpanProfileRequest{ 335 Request: &ingesterv1.SelectSpanProfileRequest{ 336 LabelSelector: req.LabelSelector, 337 Start: req.Start, 338 End: req.End, 339 Type: profileType, 340 SpanSelector: req.SpanSelector, 341 Hints: &ingesterv1.Hints{Block: blockHints}, 342 }, 343 MaxNodes: req.MaxNodes, 344 }) 345 })) 346 } 347 if err = g.Wait(); err != nil { 348 return nil, connect.NewError(connect.CodeInternal, err) 349 } 350 351 // merge all profiles 352 return selectMergeSpanProfile(ctx, responses) 353 } 354 355 func (q *Querier) blockSelectFromIngesters(ctx context.Context, req *ingesterv1.BlockMetadataRequest) ([]ResponseFromReplica[[]*typesv1.BlockInfo], error) { 356 sp, ctx := opentracing.StartSpanFromContext(ctx, "blockSelectFromIngesters") 357 defer sp.Finish() 358 359 responses, err := forAllIngesters(ctx, q.ingesterQuerier, func(childCtx context.Context, ic IngesterQueryClient) ([]*typesv1.BlockInfo, error) { 360 res, err := ic.BlockMetadata(childCtx, connect.NewRequest(&ingesterv1.BlockMetadataRequest{ 361 Start: req.Start, 362 End: req.End, 363 })) 364 if err != nil { 365 return nil, err 366 } 367 return res.Msg.Blocks, nil 368 }) 369 if err != nil { 370 return nil, connect.NewError(connect.CodeInternal, err) 371 } 372 return responses, nil 373 }