72 lines
2.4 KiB
Go
72 lines
2.4 KiB
Go
package adapter
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"reflect"
|
|
|
|
"github.com/grafana/dskit/services"
|
|
"github.com/grafana/grafana/pkg/registry"
|
|
)
|
|
|
|
var _ services.NamedService = &serviceAdapter{}
|
|
|
|
// serviceAdapter adapts a Grafana background service to dskit's NamedService interface.
|
|
// It wraps the background service with dskit's BasicService to provide the standard
|
|
// service state model: New → Starting → Running → Stopping → Terminated/Failed.
|
|
//
|
|
// The adapter uses dskit's BasicService with a custom RunningFn:
|
|
// - Starting phase: No-op, transitions immediately to Running
|
|
// - Running phase: Delegates to the wrapped service's Run method
|
|
// - Stopping phase: Closes the stop channel to signal the service to stop
|
|
type serviceAdapter struct {
|
|
services.NamedService
|
|
stopCh chan struct{}
|
|
name string
|
|
service registry.BackgroundService
|
|
}
|
|
|
|
// asNamedService converts a Grafana background service into a dskit NamedService.
|
|
// The returned service starts in the New state and can be managed using dskit's
|
|
// standard service operations (StartAsync, AwaitRunning, StopAsync, AwaitTerminated).
|
|
//
|
|
// The service name is derived from the Go type name using reflection, ensuring
|
|
// each service type has a unique identifier within the dskit module system.
|
|
func asNamedService(service registry.BackgroundService) *serviceAdapter {
|
|
name := reflect.TypeOf(service).String()
|
|
a := &serviceAdapter{
|
|
name: name,
|
|
service: service,
|
|
stopCh: make(chan struct{}),
|
|
}
|
|
a.NamedService = services.NewBasicService(nil, a.running, a.stopping).WithName(name)
|
|
return a
|
|
}
|
|
|
|
// run implements the RunningFn for dskit's BasicService.
|
|
// This phase keeps the service in Running state by delegating to the wrapped
|
|
// background service's Run method. If the background service completes without
|
|
// error, the adapter waits for context cancellation (service stop) before
|
|
// transitioning to Stopping state, ensuring proper dskit service lifecycle.
|
|
func (a *serviceAdapter) running(ctx context.Context) error {
|
|
serviceCtx, serviceCancel := context.WithCancel(ctx)
|
|
go func() {
|
|
<-a.stopCh
|
|
serviceCancel()
|
|
}()
|
|
|
|
err := a.service.Run(serviceCtx)
|
|
if err != nil && !errors.Is(err, context.Canceled) {
|
|
return err
|
|
}
|
|
// wait for context cancellation to transition to Stopping state.
|
|
// this prevents the service from causing it's dependents to stop prematurely.
|
|
<-serviceCtx.Done()
|
|
return nil
|
|
}
|
|
|
|
func (a *serviceAdapter) stopping(_ error) error {
|
|
close(a.stopCh)
|
|
return nil
|
|
}
|