2021-09-22 22:38:06 +00:00
|
|
|
// Package runtime includes useful utilities globally accessible in
|
2020-04-29 21:32:39 +00:00
|
|
|
// the Prysm monorepo.
|
2021-09-17 19:20:50 +00:00
|
|
|
package runtime
|
2018-07-14 02:15:37 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"reflect"
|
2018-07-31 04:41:27 +00:00
|
|
|
|
|
|
|
"github.com/sirupsen/logrus"
|
2018-07-14 02:15:37 +00:00
|
|
|
)
|
|
|
|
|
2018-07-31 04:41:27 +00:00
|
|
|
var log = logrus.WithField("prefix", "registry")
|
|
|
|
|
2018-10-09 05:58:54 +00:00
|
|
|
// Service is a struct that can be registered into a ServiceRegistry for
|
|
|
|
// easy dependency management.
|
|
|
|
type Service interface {
|
|
|
|
// Start spawns any goroutines required by the service.
|
|
|
|
Start()
|
|
|
|
// Stop terminates all goroutines belonging to the service,
|
|
|
|
// blocking until they are all terminated.
|
|
|
|
Stop() error
|
2021-04-23 12:06:05 +00:00
|
|
|
// Status returns error if the service is not considered healthy.
|
2018-12-30 21:20:43 +00:00
|
|
|
Status() error
|
2018-10-09 05:58:54 +00:00
|
|
|
}
|
|
|
|
|
2018-07-14 02:15:37 +00:00
|
|
|
// ServiceRegistry provides a useful pattern for managing services.
|
|
|
|
// It allows for ease of dependency management and ensures services
|
|
|
|
// dependent on others use the same references in memory.
|
|
|
|
type ServiceRegistry struct {
|
|
|
|
services map[reflect.Type]Service // map of types to services.
|
|
|
|
serviceTypes []reflect.Type // keep an ordered slice of registered service types.
|
|
|
|
}
|
|
|
|
|
2024-01-15 14:46:54 +00:00
|
|
|
// NewServiceRegistry starts a registry instance for convenience.
|
2018-07-14 02:15:37 +00:00
|
|
|
func NewServiceRegistry() *ServiceRegistry {
|
|
|
|
return &ServiceRegistry{
|
|
|
|
services: make(map[reflect.Type]Service),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// StartAll initialized each service in order of registration.
|
|
|
|
func (s *ServiceRegistry) StartAll() {
|
2020-04-05 19:36:18 +00:00
|
|
|
log.Debugf("Starting %d services: %v", len(s.serviceTypes), s.serviceTypes)
|
2018-07-14 02:15:37 +00:00
|
|
|
for _, kind := range s.serviceTypes {
|
2018-11-24 12:03:36 +00:00
|
|
|
log.Debugf("Starting service type %v", kind)
|
2019-08-21 20:58:38 +00:00
|
|
|
go s.services[kind].Start()
|
2018-07-14 02:15:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-31 04:41:27 +00:00
|
|
|
// StopAll ends every service in reverse order of registration, logging a
|
|
|
|
// panic if any of them fail to stop.
|
2018-07-14 02:15:37 +00:00
|
|
|
func (s *ServiceRegistry) StopAll() {
|
2018-07-31 04:41:27 +00:00
|
|
|
for i := len(s.serviceTypes) - 1; i >= 0; i-- {
|
|
|
|
kind := s.serviceTypes[i]
|
|
|
|
service := s.services[kind]
|
2018-07-14 02:15:37 +00:00
|
|
|
if err := service.Stop(); err != nil {
|
2020-10-21 23:41:01 +00:00
|
|
|
log.WithError(err).Errorf("Could not stop the following service: %v", kind)
|
2018-07-14 02:15:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-30 21:20:43 +00:00
|
|
|
// Statuses returns a map of Service type -> error. The map will be populated
|
|
|
|
// with the results of each service.Status() method call.
|
|
|
|
func (s *ServiceRegistry) Statuses() map[reflect.Type]error {
|
2020-07-09 15:50:58 +00:00
|
|
|
m := make(map[reflect.Type]error, len(s.serviceTypes))
|
2018-12-30 21:20:43 +00:00
|
|
|
for _, kind := range s.serviceTypes {
|
|
|
|
m[kind] = s.services[kind].Status()
|
|
|
|
}
|
|
|
|
return m
|
|
|
|
}
|
|
|
|
|
2018-07-14 02:15:37 +00:00
|
|
|
// RegisterService appends a service constructor function to the service
|
|
|
|
// registry.
|
|
|
|
func (s *ServiceRegistry) RegisterService(service Service) error {
|
|
|
|
kind := reflect.TypeOf(service)
|
|
|
|
if _, exists := s.services[kind]; exists {
|
|
|
|
return fmt.Errorf("service already exists: %v", kind)
|
|
|
|
}
|
|
|
|
s.services[kind] = service
|
|
|
|
s.serviceTypes = append(s.serviceTypes, kind)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// FetchService takes in a struct pointer and sets the value of that pointer
|
|
|
|
// to a service currently stored in the service registry. This ensures the input argument is
|
|
|
|
// set to the right pointer that refers to the originally registered service.
|
|
|
|
func (s *ServiceRegistry) FetchService(service interface{}) error {
|
|
|
|
if reflect.TypeOf(service).Kind() != reflect.Ptr {
|
|
|
|
return fmt.Errorf("input must be of pointer type, received value type instead: %T", service)
|
|
|
|
}
|
|
|
|
element := reflect.ValueOf(service).Elem()
|
|
|
|
if running, ok := s.services[element.Type()]; ok {
|
|
|
|
element.Set(reflect.ValueOf(running))
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return fmt.Errorf("unknown service: %T", service)
|
|
|
|
}
|