github.com/grafana/pyroscope@v1.18.0/pkg/pyroscope/modules_experimental.go (about) 1 package pyroscope 2 3 import ( 4 "context" 5 "fmt" 6 "slices" 7 "time" 8 9 "github.com/go-kit/log" 10 "github.com/go-kit/log/level" 11 "github.com/grafana/dskit/middleware" 12 "github.com/grafana/dskit/netutil" 13 "github.com/grafana/dskit/ring" 14 "github.com/grafana/dskit/services" 15 otgrpc "github.com/opentracing-contrib/go-grpc" 16 "github.com/opentracing/opentracing-go" 17 "github.com/prometheus/client_golang/prometheus" 18 "google.golang.org/grpc" 19 grpchealth "google.golang.org/grpc/health" 20 21 "github.com/grafana/pyroscope/pkg/compactionworker" 22 "github.com/grafana/pyroscope/pkg/frontend" 23 "github.com/grafana/pyroscope/pkg/frontend/readpath" 24 "github.com/grafana/pyroscope/pkg/frontend/readpath/queryfrontend" 25 "github.com/grafana/pyroscope/pkg/frontend/vcs" 26 "github.com/grafana/pyroscope/pkg/metastore" 27 metastoreadmin "github.com/grafana/pyroscope/pkg/metastore/admin" 28 metastoreclient "github.com/grafana/pyroscope/pkg/metastore/client" 29 "github.com/grafana/pyroscope/pkg/metastore/discovery" 30 "github.com/grafana/pyroscope/pkg/metrics" 31 "github.com/grafana/pyroscope/pkg/objstore" 32 operationsv2 "github.com/grafana/pyroscope/pkg/operations/v2" 33 "github.com/grafana/pyroscope/pkg/querybackend" 34 querybackendclient "github.com/grafana/pyroscope/pkg/querybackend/client" 35 "github.com/grafana/pyroscope/pkg/segmentwriter" 36 segmentwriterclient "github.com/grafana/pyroscope/pkg/segmentwriter/client" 37 placement "github.com/grafana/pyroscope/pkg/segmentwriter/client/distributor/placement/adaptiveplacement" 38 recordingrulesclient "github.com/grafana/pyroscope/pkg/settings/recording/client" 39 "github.com/grafana/pyroscope/pkg/symbolizer" 40 "github.com/grafana/pyroscope/pkg/tenant" 41 "github.com/grafana/pyroscope/pkg/util" 42 "github.com/grafana/pyroscope/pkg/util/health" 43 "github.com/grafana/pyroscope/pkg/util/spanlogger" 44 ) 45 46 func (f *Pyroscope) initQueryFrontend() (services.Service, error) { 47 var err error 48 if f.Cfg.Frontend.Addr, err = f.getFrontendAddress(); err != nil { 49 return nil, fmt.Errorf("failed to get frontend address: %w", err) 50 } 51 if f.Cfg.Frontend.Port == 0 { 52 f.Cfg.Frontend.Port = f.Cfg.Server.HTTPListenPort 53 } 54 if !f.Cfg.V2 { 55 return f.initQueryFrontendV1() 56 } 57 // If the new read path is enabled globally by default, 58 // the old query frontend is not used. Tenant-specific overrides 59 // are ignored — all tenants use the new read path. 60 // 61 // If the old read path is still in use, we configure the router 62 // to use both the old and new query frontends. 63 c := f.Overrides.ReadPathOverrides(tenant.DefaultTenantID) 64 switch { 65 case !c.EnableQueryBackend: 66 return f.initQueryFrontendV1() 67 case c.EnableQueryBackend && c.EnableQueryBackendFrom.IsZero(): 68 return f.initQueryFrontendV2() 69 case c.EnableQueryBackend && !c.EnableQueryBackendFrom.IsZero(): 70 return f.initQueryFrontendV12() 71 default: 72 return nil, fmt.Errorf("invalid query backend configuration: %v", c) 73 } 74 } 75 76 func (f *Pyroscope) initQueryFrontendV1() (services.Service, error) { 77 queryFrontendLogger := log.With(f.logger, "component", "frontend") 78 var err error 79 f.frontend, err = frontend.NewFrontend(f.Cfg.Frontend, f.Overrides, queryFrontendLogger, f.reg) 80 if err != nil { 81 return nil, err 82 } 83 f.API.RegisterFrontendForQuerierHandler(f.frontend) 84 f.API.RegisterQuerierServiceHandler(spanlogger.NewLogSpanParametersWrapper(f.frontend, queryFrontendLogger)) 85 f.API.RegisterPyroscopeHandlers(spanlogger.NewLogSpanParametersWrapper(f.frontend, queryFrontendLogger)) 86 f.API.RegisterVCSServiceHandler(f.frontend) 87 return f.frontend, nil 88 } 89 90 func (f *Pyroscope) initQueryFrontendV2() (services.Service, error) { 91 queryFrontendLogger := log.With(f.logger, "component", "query-frontend") 92 queryFrontend := queryfrontend.NewQueryFrontend( 93 queryFrontendLogger, 94 f.Overrides, 95 f.metastoreClient, 96 f.metastoreClient, 97 f.queryBackendClient, 98 f.symbolizer, 99 ) 100 101 vcsService := vcs.New( 102 log.With(f.logger, "component", "vcs-service"), 103 f.reg, 104 ) 105 106 f.API.RegisterQuerierServiceHandler(spanlogger.NewLogSpanParametersWrapper(queryFrontend, queryFrontendLogger)) 107 f.API.RegisterPyroscopeHandlers(spanlogger.NewLogSpanParametersWrapper(queryFrontend, queryFrontendLogger)) 108 f.API.RegisterVCSServiceHandler(vcsService) 109 110 // New query frontend does not have any state. 111 // For simplicity, we return a no-op service. 112 svc := services.NewIdleService( 113 func(context.Context) error { return nil }, 114 func(error) error { return nil }, 115 ) 116 117 return svc, nil 118 } 119 120 func (f *Pyroscope) initQueryFrontendV12() (services.Service, error) { 121 var err error 122 f.frontend, err = frontend.NewFrontend(f.Cfg.Frontend, f.Overrides, log.With(f.logger, "component", "frontend"), f.reg) 123 if err != nil { 124 return nil, err 125 } 126 127 queryFrontendLogger := log.With(f.logger, "component", "query-frontend") 128 newFrontend := queryfrontend.NewQueryFrontend( 129 queryFrontendLogger, 130 f.Overrides, 131 f.metastoreClient, 132 f.metastoreClient, 133 f.queryBackendClient, 134 f.symbolizer, 135 ) 136 137 handler := readpath.NewRouter( 138 log.With(f.logger, "component", "read-path-router"), 139 f.Overrides, 140 f.frontend, 141 newFrontend, 142 ) 143 144 vcsService := vcs.New( 145 log.With(f.logger, "component", "vcs-service"), 146 f.reg, 147 ) 148 149 f.API.RegisterFrontendForQuerierHandler(f.frontend) 150 f.API.RegisterQuerierServiceHandler(spanlogger.NewLogSpanParametersWrapper(handler, queryFrontendLogger)) 151 f.API.RegisterPyroscopeHandlers(spanlogger.NewLogSpanParametersWrapper(handler, queryFrontendLogger)) 152 f.API.RegisterVCSServiceHandler(vcsService) 153 154 return f.frontend, nil 155 } 156 157 func (f *Pyroscope) getFrontendAddress() (addr string, err error) { 158 addr = f.Cfg.Frontend.Addr 159 if f.Cfg.Frontend.AddrOld != "" { 160 addr = f.Cfg.Frontend.AddrOld 161 } 162 if addr != "" { 163 return addr, nil 164 } 165 return netutil.GetFirstAddressOf(f.Cfg.Frontend.InfNames, f.logger, f.Cfg.Frontend.EnableIPv6) 166 } 167 168 func (f *Pyroscope) initSegmentWriterRing() (_ services.Service, err error) { 169 if err = f.Cfg.SegmentWriter.Validate(); err != nil { 170 return nil, err 171 } 172 logger := log.With(f.logger, "component", "segment-writer-ring") 173 reg := prometheus.WrapRegistererWithPrefix("pyroscope_", f.reg) 174 f.segmentWriterRing, err = ring.New( 175 f.Cfg.SegmentWriter.LifecyclerConfig.RingConfig, 176 segmentwriter.RingName, 177 segmentwriter.RingKey, 178 logger, reg, 179 ) 180 if err != nil { 181 return nil, err 182 } 183 f.API.RegisterSegmentWriterRing(f.segmentWriterRing) 184 return f.segmentWriterRing, nil 185 } 186 187 func (f *Pyroscope) initSegmentWriter() (services.Service, error) { 188 f.Cfg.SegmentWriter.LifecyclerConfig.ListenPort = f.Cfg.Server.GRPCListenPort 189 if err := f.Cfg.SegmentWriter.Validate(); err != nil { 190 return nil, err 191 } 192 193 logger := log.With(f.logger, "component", "segment-writer") 194 healthService := health.NewGRPCHealthService(f.healthServer, logger, "pyroscope.segment-writer") 195 segmentWriter, err := segmentwriter.New( 196 f.reg, 197 logger, 198 f.Cfg.SegmentWriter, 199 f.Overrides, 200 healthService, 201 f.storageBucket, 202 f.metastoreClient, 203 ) 204 if err != nil { 205 return nil, err 206 } 207 208 f.segmentWriter = segmentWriter 209 f.API.RegisterSegmentWriter(segmentWriter) 210 return f.segmentWriter, nil 211 } 212 213 func (f *Pyroscope) initSegmentWriterClient() (_ services.Service, err error) { 214 f.Cfg.SegmentWriter.GRPCClientConfig.Middleware = f.grpcClientInterceptors() 215 // Validation of the config is not required since 216 // it's already validated in initSegmentWriterRing. 217 logger := log.With(f.logger, "component", "segment-writer-client") 218 placement := f.placementAgent.Placement() 219 client, err := segmentwriterclient.NewSegmentWriterClient( 220 f.Cfg.SegmentWriter.GRPCClientConfig, 221 logger, f.reg, 222 f.segmentWriterRing, 223 placement, 224 ) 225 if err != nil { 226 return nil, err 227 } 228 f.segmentWriterClient = client 229 return client.Service(), nil 230 } 231 232 func (f *Pyroscope) initCompactionWorker() (svc services.Service, err error) { 233 logger := log.With(f.logger, "component", "compaction-worker") 234 registerer := prometheus.WrapRegistererWithPrefix("pyroscope_compaction_worker_", f.reg) 235 236 var ruler metrics.Ruler 237 var exporter metrics.Exporter 238 if f.Cfg.CompactionWorker.MetricsExporter.Enabled { 239 if f.recordingRulesClient != nil { 240 ruler, err = metrics.NewCachedRemoteRuler(f.recordingRulesClient, f.logger) 241 if err != nil { 242 return nil, err 243 } 244 } else { 245 ruler = metrics.NewStaticRulerFromOverrides(f.Overrides) 246 } 247 248 exporter, err = metrics.NewExporter(f.Cfg.CompactionWorker.MetricsExporter.RemoteWriteAddress, f.logger, f.reg) 249 if err != nil { 250 return nil, err 251 } 252 } 253 254 w, err := compactionworker.New( 255 logger, 256 f.Cfg.CompactionWorker, 257 f.metastoreClient, 258 f.storageBucket, 259 registerer, 260 ruler, 261 exporter, 262 ) 263 if err != nil { 264 return nil, err 265 } 266 f.compactionWorker = w 267 return w.Service(), nil 268 } 269 270 func (f *Pyroscope) initMetastore() (services.Service, error) { 271 if err := f.Cfg.Metastore.Validate(); err != nil { 272 return nil, err 273 } 274 275 logger := log.With(f.logger, "component", "metastore") 276 healthService := health.NewGRPCHealthService(f.healthServer, logger, "pyroscope.metastore") 277 registerer := prometheus.WrapRegistererWithPrefix("pyroscope_metastore_", f.reg) 278 m, err := metastore.New( 279 f.Cfg.Metastore, 280 f.Overrides, 281 logger, 282 registerer, 283 healthService, 284 f.metastoreClient, 285 f.storageBucket, 286 f.placementManager, 287 ) 288 if err != nil { 289 return nil, err 290 } 291 292 m.Register(f.Server.GRPC) 293 f.metastore = m 294 return m.Service(), nil 295 } 296 297 func (f *Pyroscope) initMetastoreClient() (services.Service, error) { 298 if err := f.Cfg.Metastore.Validate(); err != nil { 299 return nil, err 300 } 301 302 disc, err := discovery.NewDiscovery(f.logger, f.Cfg.Metastore.Address, f.reg) 303 if err != nil { 304 return nil, fmt.Errorf("failed to create discovery: %w %s", err, f.Cfg.Metastore.Address) 305 } 306 307 f.Cfg.Metastore.GRPCClientConfig.Middleware = f.grpcClientInterceptors() 308 f.metastoreClient = metastoreclient.New( 309 f.logger, 310 f.Cfg.Metastore.GRPCClientConfig, 311 disc, 312 ) 313 return f.metastoreClient.Service(), nil 314 } 315 316 func (f *Pyroscope) initMetastoreAdmin() (services.Service, error) { 317 level.Info(f.logger).Log("msg", "initializing metastore admin") 318 if err := f.Cfg.Metastore.Validate(); err != nil { 319 return nil, err 320 } 321 322 var err error 323 f.metastoreAdmin, err = metastoreadmin.New(f.metastoreClient, f.logger, f.Cfg.Metastore.Address, f.metastoreClient) 324 if err != nil { 325 return nil, err 326 } 327 level.Info(f.logger).Log("msg", "registering metastore admin routes") 328 f.API.RegisterMetastoreAdmin(f.metastoreAdmin) 329 return f.metastoreAdmin.Service(), nil 330 } 331 332 func (f *Pyroscope) initAdminV2() (services.Service, error) { 333 level.Info(f.logger).Log("msg", "initializing v2 admin (metastore-based)") 334 335 a, err := operationsv2.NewAdmin(f.metastoreClient, f.storageBucket, f.logger) 336 if err != nil { 337 level.Info(f.logger).Log("msg", "failed to initialize v2 admin", "err", err) 338 return nil, nil 339 } 340 f.admin = a 341 f.API.RegisterAdmin(a) 342 return a, nil 343 } 344 345 func (f *Pyroscope) initQueryBackend() (services.Service, error) { 346 if err := f.Cfg.QueryBackend.Validate(); err != nil { 347 return nil, err 348 } 349 logger := log.With(f.logger, "component", "query-backend") 350 b, err := querybackend.New( 351 f.Cfg.QueryBackend, 352 logger, 353 f.reg, 354 f.queryBackendClient, 355 querybackend.NewBlockReader(f.logger, f.storageBucket, f.reg), 356 ) 357 if err != nil { 358 return nil, err 359 } 360 f.API.RegisterQueryBackend(b) 361 return b.Service(), nil 362 } 363 364 func (f *Pyroscope) initQueryBackendClient() (services.Service, error) { 365 if err := f.Cfg.QueryBackend.Validate(); err != nil { 366 return nil, err 367 } 368 f.Cfg.QueryBackend.GRPCClientConfig.Middleware = f.grpcClientInterceptors() 369 c, err := querybackendclient.New( 370 f.Cfg.QueryBackend.Address, 371 f.Cfg.QueryBackend.GRPCClientConfig, 372 f.Cfg.QueryBackend.ClientTimeout, 373 ) 374 if err != nil { 375 return nil, err 376 } 377 f.queryBackendClient = c 378 return c.Service(), nil 379 } 380 381 func (f *Pyroscope) initRecordingRulesClient() (services.Service, error) { 382 if err := f.Cfg.CompactionWorker.MetricsExporter.Validate(); err != nil { 383 return nil, err 384 } 385 if !f.Cfg.CompactionWorker.MetricsExporter.Enabled || 386 f.Cfg.CompactionWorker.MetricsExporter.RulesSource.ClientAddress == "" { 387 return nil, nil 388 } 389 c, err := recordingrulesclient.NewClient(f.Cfg.CompactionWorker.MetricsExporter.RulesSource.ClientAddress, f.logger, f.auth) 390 if err != nil { 391 return nil, err 392 } 393 f.recordingRulesClient = c 394 return c.Service(), nil 395 } 396 397 func (f *Pyroscope) initSymbolizer() (services.Service, error) { 398 prefixedBucket := objstore.NewPrefixedBucket(f.storageBucket, "symbolizer") 399 400 sym, err := symbolizer.New( 401 f.logger, 402 f.Cfg.Symbolizer, 403 f.reg, 404 prefixedBucket, 405 f.Overrides, 406 ) 407 if err != nil { 408 return nil, fmt.Errorf("failed to create symbolizer: %w", err) 409 } 410 411 f.symbolizer = sym 412 413 return nil, nil 414 } 415 416 func (f *Pyroscope) initPlacementAgent() (services.Service, error) { 417 f.placementAgent = placement.NewAgent( 418 f.logger, 419 f.reg, 420 f.Cfg.AdaptivePlacement, 421 f.Overrides, 422 f.adaptivePlacementStore(), 423 ) 424 return f.placementAgent.Service(), nil 425 } 426 427 func (f *Pyroscope) initPlacementManager() (services.Service, error) { 428 f.placementManager = placement.NewManager( 429 f.logger, 430 f.reg, 431 f.Cfg.AdaptivePlacement, 432 f.Overrides, 433 f.adaptivePlacementStore(), 434 ) 435 return f.placementManager.Service(), nil 436 } 437 438 func (f *Pyroscope) adaptivePlacementStore() placement.Store { 439 if slices.Contains(f.Cfg.Target, All) { 440 // Disables sharding in all-in-one scenario. 441 return placement.NewEmptyStore() 442 } 443 return placement.NewStore(f.storageBucket) 444 } 445 446 func (f *Pyroscope) initHealthServer() (services.Service, error) { 447 f.healthServer = grpchealth.NewServer() 448 return nil, nil 449 } 450 451 func (f *Pyroscope) grpcClientInterceptors() []grpc.UnaryClientInterceptor { 452 requestDuration := util.RegisterOrGet(f.reg, prometheus.NewHistogramVec(prometheus.HistogramOpts{ 453 Namespace: "pyroscope", 454 Subsystem: "grpc_client", 455 Name: "request_duration_seconds", 456 Help: "Time (in seconds) spent waiting for gRPC response.", 457 Buckets: prometheus.DefBuckets, 458 NativeHistogramBucketFactor: 1.1, 459 NativeHistogramMaxBucketNumber: 50, 460 NativeHistogramMinResetDuration: time.Hour, 461 }, []string{"method", "status_code"})) 462 463 return []grpc.UnaryClientInterceptor{ 464 middleware.UnaryClientInstrumentInterceptor(requestDuration, middleware.ReportGRPCStatusOption), 465 otgrpc.OpenTracingClientInterceptor(opentracing.GlobalTracer()), 466 } 467 }