package factory import ( "context" "errors" "fmt" "log/slog" "os" "os/signal" "syscall" ) type Registry struct { services NamedMap[NamedService] logger *slog.Logger startCh chan error stopCh chan error } // New creates a new registry of services. It needs at least one service in the input. func NewRegistry(logger *slog.Logger, services ...NamedService) (*Registry, error) { if logger == nil { return nil, fmt.Errorf("cannot build registry, logger is required") } if len(services) == 0 { return nil, fmt.Errorf("cannot build registry, at least one service is required") } m, err := NewNamedMap(services...) if err != nil { return nil, err } return &Registry{ logger: logger.With("pkg", "go.signoz.io/pkg/factory"), services: m, startCh: make(chan error, 1), stopCh: make(chan error, len(services)), }, nil } func (r *Registry) Start(ctx context.Context) { for _, s := range r.services.GetInOrder() { go func(s NamedService) { r.logger.InfoContext(ctx, "starting service", "service", s.Name()) err := s.Start(ctx) r.startCh <- err }(s) } } func (r *Registry) Wait(ctx context.Context) error { interrupt := make(chan os.Signal, 1) signal.Notify(interrupt, syscall.SIGINT, syscall.SIGTERM) select { case <-ctx.Done(): r.logger.InfoContext(ctx, "caught context error, exiting", "error", ctx.Err()) case s := <-interrupt: r.logger.InfoContext(ctx, "caught interrupt signal, exiting", "signal", s) case err := <-r.startCh: r.logger.ErrorContext(ctx, "caught service error, exiting", "error", err) return err } return nil } func (r *Registry) Stop(ctx context.Context) error { for _, s := range r.services.GetInOrder() { go func(s NamedService) { r.logger.InfoContext(ctx, "stopping service", "service", s.Name()) err := s.Stop(ctx) r.stopCh <- err }(s) } errs := make([]error, len(r.services.GetInOrder())) for i := 0; i < len(r.services.GetInOrder()); i++ { err := <-r.stopCh if err != nil { errs = append(errs, err) } } return errors.Join(errs...) }