Files
grafana/pkg/registry/backgroundsvcs/adapter/manager.go
T
2025-10-02 19:53:31 -04:00

125 lines
4.1 KiB
Go

package adapter
import (
"context"
"time"
"github.com/grafana/dskit/services"
"go.opentelemetry.io/otel/trace"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/infra/tracing"
grafanamodules "github.com/grafana/grafana/pkg/modules"
"github.com/grafana/grafana/pkg/registry"
)
var (
stopTimeout = 5 * time.Second
)
type ManagerAdapter struct {
services.NamedService
reg registry.BackgroundServiceRegistry
manager grafanamodules.Manager
dependencyMap map[string][]string
}
// NewManagerAdapter creates a new manager adapter that bridges Grafana's background
// service registry with dskit's module and service patterns. The adapter converts background
// services to dskit services and manages them using dskit's module Manager, which provides:
// - Coordinated service initialization
// - Observable service states and health monitoring
// - Graceful shutdown with proper cleanup ordering
//
// Services implementing CanBeDisabled that are disabled will be skipped.
func NewManagerAdapter(reg registry.BackgroundServiceRegistry) *ManagerAdapter {
m := &ManagerAdapter{
reg: reg,
dependencyMap: dependencyMap(),
}
m.NamedService = services.NewBasicService(m.starting, m.running, m.stopping).WithName("backgroundsvcs.managerAdapter")
return m
}
func (m *ManagerAdapter) WithDependencies(dependencyMap map[string][]string) *ManagerAdapter {
m.dependencyMap = dependencyMap
return m
}
func (m *ManagerAdapter) starting(ctx context.Context) error {
spanCtx, span := tracing.Start(ctx, "backgroundsvcs.managerAdapter.starting")
defer span.End()
logger := log.New("backgroundsvcs.managerAdapter").FromContext(spanCtx)
manager := grafanamodules.New(logger, []string{BackgroundServices}).WithDependencies(m.dependencyMap)
for _, bgSvc := range m.reg.GetServices() {
//only wrap background services that are not already a NamedService
namedService, ok := bgSvc.(services.NamedService)
if !ok {
namedService = asNamedService(bgSvc)
}
// skip disabled services
if s, ok := bgSvc.(registry.CanBeDisabled); ok && s.IsDisabled() {
logger.Debug("Skipping disabled service", "service", namedService.ServiceName())
manager.RegisterInvisibleModule(namedService.ServiceName(), nil)
continue
}
// register the service as an invisible module
manager.RegisterInvisibleModule(namedService.ServiceName(), func() (services.Service, error) {
return namedService, nil
})
// add the service as a background service dependency if it's not already in the dependency map
if _, ok := m.dependencyMap[namedService.ServiceName()]; !ok {
m.dependencyMap[namedService.ServiceName()] = []string{Core}
m.dependencyMap[BackgroundServices] = append(m.dependencyMap[BackgroundServices], namedService.ServiceName())
}
}
manager.RegisterModule(Core, nil)
manager.RegisterModule(BackgroundServices, nil)
m.manager = manager
if err := m.manager.StartAsync(ctx); err != nil {
return err
}
return m.manager.AwaitRunning(ctx)
}
func (m *ManagerAdapter) running(ctx context.Context) error {
newCtx := trace.ContextWithSpan(context.Background(), trace.SpanFromContext(ctx))
spanCtx, span := tracing.Start(newCtx, "backgroundsvcs.managerAdapter.running")
defer span.End()
return m.manager.AwaitTerminated(spanCtx)
}
func (m *ManagerAdapter) stopping(failure error) error {
ctx, cancel := context.WithTimeout(context.Background(), stopTimeout)
defer cancel()
spanCtx, span := tracing.Start(ctx, "backgroundsvcs.managerAdapter.stopping")
defer span.End()
reason := ""
if failure != nil {
reason = failure.Error()
}
return m.manager.Shutdown(spanCtx, reason)
}
// Run initializes and starts all background services using dskit's module and service patterns.
func (m *ManagerAdapter) Run(ctx context.Context) error {
if err := m.StartAsync(ctx); err != nil {
return err
}
stopCtx := trace.ContextWithSpan(context.Background(), trace.SpanFromContext(ctx))
return m.AwaitTerminated(stopCtx)
}
// Shutdown calls calls the underlying manager's Shutdown
func (m *ManagerAdapter) Shutdown(ctx context.Context, reason string) error {
m.StopAsync()
return m.AwaitTerminated(ctx)
}