github.com/grafana/pyroscope@v1.18.0/pkg/storegateway/query.go (about) 1 package storegateway 2 3 import ( 4 "context" 5 "io" 6 "slices" 7 8 "connectrpc.com/connect" 9 "github.com/pkg/errors" 10 "github.com/prometheus/common/model" 11 12 ingestv1 "github.com/grafana/pyroscope/api/gen/proto/go/ingester/v1" 13 typesv1 "github.com/grafana/pyroscope/api/gen/proto/go/types/v1" 14 "github.com/grafana/pyroscope/pkg/phlaredb" 15 "github.com/grafana/pyroscope/pkg/tenant" 16 ) 17 18 func (s *StoreGateway) MergeProfilesStacktraces(ctx context.Context, stream *connect.BidiStream[ingestv1.MergeProfilesStacktracesRequest, ingestv1.MergeProfilesStacktracesResponse]) error { 19 found, err := s.forBucketStore(ctx, func(bs *BucketStore) error { 20 return bs.MergeProfilesStacktraces(ctx, stream) 21 }) 22 if err != nil || found { 23 return err 24 } 25 return terminateStream(stream) 26 } 27 28 func (s *StoreGateway) MergeProfilesLabels(ctx context.Context, stream *connect.BidiStream[ingestv1.MergeProfilesLabelsRequest, ingestv1.MergeProfilesLabelsResponse]) error { 29 found, err := s.forBucketStore(ctx, func(bs *BucketStore) error { 30 return bs.MergeProfilesLabels(ctx, stream) 31 }) 32 if err != nil || found { 33 return err 34 } 35 return terminateStream(stream) 36 } 37 38 func (s *StoreGateway) MergeProfilesPprof(ctx context.Context, stream *connect.BidiStream[ingestv1.MergeProfilesPprofRequest, ingestv1.MergeProfilesPprofResponse]) error { 39 found, err := s.forBucketStore(ctx, func(bs *BucketStore) error { 40 return bs.MergeProfilesPprof(ctx, stream) 41 }) 42 if err != nil || found { 43 return err 44 } 45 return terminateStream(stream) 46 } 47 48 func (s *StoreGateway) ProfileTypes(ctx context.Context, req *connect.Request[ingestv1.ProfileTypesRequest]) (*connect.Response[ingestv1.ProfileTypesResponse], error) { 49 var res *ingestv1.ProfileTypesResponse 50 _, err := s.forBucketStore(ctx, func(bs *BucketStore) error { 51 var err error 52 result, err := phlaredb.ProfileTypes(ctx, req, bs.openBlocksForReading) 53 if err != nil { 54 return err 55 } 56 res = result.Msg 57 return nil 58 }) 59 if err != nil { 60 return nil, connect.NewError(connect.CodeInternal, err) 61 } 62 63 return connect.NewResponse(res), nil 64 } 65 66 func (s *StoreGateway) LabelValues(ctx context.Context, req *connect.Request[typesv1.LabelValuesRequest]) (*connect.Response[typesv1.LabelValuesResponse], error) { 67 var res *typesv1.LabelValuesResponse 68 _, err := s.forBucketStore(ctx, func(bs *BucketStore) error { 69 var err error 70 res, err = phlaredb.LabelValues(ctx, req, bs.openBlocksForReading) 71 if err != nil { 72 return err 73 } 74 return nil 75 }) 76 if err != nil { 77 return nil, connect.NewError(connect.CodeInternal, err) 78 } 79 80 return connect.NewResponse(res), nil 81 } 82 83 func (s *StoreGateway) LabelNames(ctx context.Context, req *connect.Request[typesv1.LabelNamesRequest]) (*connect.Response[typesv1.LabelNamesResponse], error) { 84 var res *typesv1.LabelNamesResponse 85 _, err := s.forBucketStore(ctx, func(bs *BucketStore) error { 86 var err error 87 res, err = phlaredb.LabelNames(ctx, req, bs.openBlocksForReading) 88 if err != nil { 89 return err 90 } 91 return nil 92 }) 93 if err != nil { 94 return nil, connect.NewError(connect.CodeInternal, err) 95 } 96 97 return connect.NewResponse(res), nil 98 } 99 100 func (s *StoreGateway) Series(ctx context.Context, req *connect.Request[ingestv1.SeriesRequest]) (*connect.Response[ingestv1.SeriesResponse], error) { 101 var res *ingestv1.SeriesResponse 102 _, err := s.forBucketStore(ctx, func(bs *BucketStore) error { 103 var err error 104 res, err = phlaredb.Series(ctx, req.Msg, bs.openBlocksForReading) 105 if err != nil { 106 return err 107 } 108 return nil 109 }) 110 if err != nil { 111 return nil, connect.NewError(connect.CodeInternal, err) 112 } 113 114 return connect.NewResponse(res), nil 115 } 116 117 func (s *StoreGateway) MergeSpanProfile(ctx context.Context, stream *connect.BidiStream[ingestv1.MergeSpanProfileRequest, ingestv1.MergeSpanProfileResponse]) error { 118 found, err := s.forBucketStore(ctx, func(bs *BucketStore) error { 119 return bs.MergeSpanProfile(ctx, stream) 120 }) 121 if err != nil || found { 122 return err 123 } 124 return terminateStream(stream) 125 } 126 127 func (s *StoreGateway) BlockMetadata(ctx context.Context, req *connect.Request[ingestv1.BlockMetadataRequest]) (*connect.Response[ingestv1.BlockMetadataResponse], error) { 128 var res *ingestv1.BlockMetadataResponse 129 found, err := s.forBucketStore(ctx, func(bs *BucketStore) error { 130 var err error 131 res, err = bs.BlockMetadata(ctx, req.Msg) 132 return err 133 }) 134 if err != nil { 135 return nil, connect.NewError(connect.CodeInternal, err) 136 } 137 if !found { 138 res = &ingestv1.BlockMetadataResponse{} 139 } 140 141 return connect.NewResponse(res), nil 142 } 143 144 func (s *StoreGateway) GetBlockStats(ctx context.Context, req *connect.Request[ingestv1.GetBlockStatsRequest]) (*connect.Response[ingestv1.GetBlockStatsResponse], error) { 145 res := &ingestv1.GetBlockStatsResponse{} 146 _, err := s.forBucketStore(ctx, func(bs *BucketStore) error { 147 bs.blocksMx.RLock() 148 defer bs.blocksMx.RUnlock() 149 150 for ulid, block := range bs.blocks { 151 if slices.Contains(req.Msg.Ulids, ulid.String()) { 152 res.BlockStats = append(res.BlockStats, block.meta.GetStats().ConvertToBlockStats()) 153 } 154 } 155 return nil 156 }) 157 if err != nil { 158 return nil, connect.NewError(connect.CodeInternal, err) 159 } 160 return connect.NewResponse(res), nil 161 } 162 163 func terminateStream[Req, Resp any](stream *connect.BidiStream[Req, Resp]) (err error) { 164 if _, err = stream.Receive(); err != nil { 165 if errors.Is(err, io.EOF) { 166 return connect.NewError(connect.CodeCanceled, errors.New("client closed stream")) 167 } 168 return err 169 } 170 if err = stream.Send(new(Resp)); err != nil { 171 return err 172 } 173 return stream.Send(new(Resp)) 174 } 175 176 // forBucketStore executes the given function for the bucketstore with the given tenant ID in the context. 177 func (s *StoreGateway) forBucketStore(ctx context.Context, f func(*BucketStore) error) (bool, error) { 178 tenantID, err := tenant.ExtractTenantIDFromContext(ctx) 179 if err != nil { 180 return true, connect.NewError(connect.CodeInvalidArgument, err) 181 } 182 store := s.stores.getStore(tenantID) 183 if store != nil { 184 return true, f(store) 185 } 186 return false, nil 187 } 188 189 func (s *BucketStore) openBlocksForReading(ctx context.Context, minT, maxT model.Time, hints *ingestv1.Hints) (phlaredb.Queriers, error) { 190 skipBlock := phlaredb.HintsToBlockSkipper(hints) 191 blks := s.blockSet.getFor(minT, maxT) 192 querier := make(phlaredb.Queriers, 0, len(blks)) 193 for _, b := range blks { 194 if skipBlock(b.BlockID()) { 195 continue 196 } 197 querier = append(querier, b) 198 } 199 if err := querier.Open(ctx); err != nil { 200 return nil, err 201 } 202 return querier, nil 203 } 204 205 func (store *BucketStore) MergeProfilesStacktraces(ctx context.Context, stream *connect.BidiStream[ingestv1.MergeProfilesStacktracesRequest, ingestv1.MergeProfilesStacktracesResponse]) error { 206 return phlaredb.MergeProfilesStacktraces(ctx, stream, store.openBlocksForReading) 207 } 208 209 func (store *BucketStore) MergeProfilesLabels(ctx context.Context, stream *connect.BidiStream[ingestv1.MergeProfilesLabelsRequest, ingestv1.MergeProfilesLabelsResponse]) error { 210 return phlaredb.MergeProfilesLabels(ctx, stream, store.openBlocksForReading) 211 } 212 213 func (store *BucketStore) MergeProfilesPprof(ctx context.Context, stream *connect.BidiStream[ingestv1.MergeProfilesPprofRequest, ingestv1.MergeProfilesPprofResponse]) error { 214 return phlaredb.MergeProfilesPprof(ctx, stream, store.openBlocksForReading) 215 } 216 217 func (store *BucketStore) MergeSpanProfile(ctx context.Context, stream *connect.BidiStream[ingestv1.MergeSpanProfileRequest, ingestv1.MergeSpanProfileResponse]) error { 218 return phlaredb.MergeSpanProfile(ctx, stream, store.openBlocksForReading) 219 } 220 221 func (s *BucketStore) BlockMetadata(ctx context.Context, req *ingestv1.BlockMetadataRequest) (*ingestv1.BlockMetadataResponse, error) { 222 set := s.blockSet.getFor(model.Time(req.Start), model.Time(req.End)) 223 result := &ingestv1.BlockMetadataResponse{ 224 Blocks: make([]*typesv1.BlockInfo, len(set)), 225 } 226 for idx, b := range set { 227 var info typesv1.BlockInfo 228 b.meta.WriteBlockInfo(&info) 229 result.Blocks[idx] = &info 230 } 231 return result, nil 232 }