github.com/prysmaticlabs/prysm@v1.4.4/shared/service_registry.go (about) 1 // Package shared includes useful utilities globally accessible in 2 // the Prysm monorepo. 3 package shared 4 5 import ( 6 "fmt" 7 "reflect" 8 9 "github.com/sirupsen/logrus" 10 ) 11 12 var log = logrus.WithField("prefix", "registry") 13 14 // Service is a struct that can be registered into a ServiceRegistry for 15 // easy dependency management. 16 type Service interface { 17 // Start spawns any goroutines required by the service. 18 Start() 19 // Stop terminates all goroutines belonging to the service, 20 // blocking until they are all terminated. 21 Stop() error 22 // Status returns error if the service is not considered healthy. 23 Status() error 24 } 25 26 // ServiceRegistry provides a useful pattern for managing services. 27 // It allows for ease of dependency management and ensures services 28 // dependent on others use the same references in memory. 29 type ServiceRegistry struct { 30 services map[reflect.Type]Service // map of types to services. 31 serviceTypes []reflect.Type // keep an ordered slice of registered service types. 32 } 33 34 // NewServiceRegistry starts a registry instance for convenience 35 func NewServiceRegistry() *ServiceRegistry { 36 return &ServiceRegistry{ 37 services: make(map[reflect.Type]Service), 38 } 39 } 40 41 // StartAll initialized each service in order of registration. 42 func (s *ServiceRegistry) StartAll() { 43 log.Debugf("Starting %d services: %v", len(s.serviceTypes), s.serviceTypes) 44 for _, kind := range s.serviceTypes { 45 log.Debugf("Starting service type %v", kind) 46 go s.services[kind].Start() 47 } 48 } 49 50 // StopAll ends every service in reverse order of registration, logging a 51 // panic if any of them fail to stop. 52 func (s *ServiceRegistry) StopAll() { 53 for i := len(s.serviceTypes) - 1; i >= 0; i-- { 54 kind := s.serviceTypes[i] 55 service := s.services[kind] 56 if err := service.Stop(); err != nil { 57 log.WithError(err).Errorf("Could not stop the following service: %v", kind) 58 } 59 } 60 } 61 62 // Statuses returns a map of Service type -> error. The map will be populated 63 // with the results of each service.Status() method call. 64 func (s *ServiceRegistry) Statuses() map[reflect.Type]error { 65 m := make(map[reflect.Type]error, len(s.serviceTypes)) 66 for _, kind := range s.serviceTypes { 67 m[kind] = s.services[kind].Status() 68 } 69 return m 70 } 71 72 // RegisterService appends a service constructor function to the service 73 // registry. 74 func (s *ServiceRegistry) RegisterService(service Service) error { 75 kind := reflect.TypeOf(service) 76 if _, exists := s.services[kind]; exists { 77 return fmt.Errorf("service already exists: %v", kind) 78 } 79 s.services[kind] = service 80 s.serviceTypes = append(s.serviceTypes, kind) 81 return nil 82 } 83 84 // FetchService takes in a struct pointer and sets the value of that pointer 85 // to a service currently stored in the service registry. This ensures the input argument is 86 // set to the right pointer that refers to the originally registered service. 87 func (s *ServiceRegistry) FetchService(service interface{}) error { 88 if reflect.TypeOf(service).Kind() != reflect.Ptr { 89 return fmt.Errorf("input must be of pointer type, received value type instead: %T", service) 90 } 91 element := reflect.ValueOf(service).Elem() 92 if running, ok := s.services[element.Type()]; ok { 93 element.Set(reflect.ValueOf(running)) 94 return nil 95 } 96 return fmt.Errorf("unknown service: %T", service) 97 }