package service import ( "time" "github.com/ethereum/go-ethereum/rpc" msg_pb "github.com/harmony-one/harmony/api/proto/message" "github.com/harmony-one/harmony/internal/utils" ) // ActionType is the input for Service Manager to operate. type ActionType byte // Constants for Action Type. const ( Start ActionType = iota Stop Notify ) // Type is service type. type Type byte // Constants for Type. const ( ClientSupport Type = iota SupportExplorer Consensus BlockProposal NetworkInfo PeerDiscovery ) func (t Type) String() string { switch t { case SupportExplorer: return "SupportExplorer" case ClientSupport: return "ClientSupport" case Consensus: return "Consensus" case BlockProposal: return "BlockProposal" case NetworkInfo: return "NetworkInfo" case PeerDiscovery: return "PeerDiscovery" default: return "Unknown" } } // Constants for timing. const ( // WaitForStatusUpdate is the delay time to update new status. Currently set 1 second for development. Should be 30 minutes for production. WaitForStatusUpdate = time.Minute * 1 ) // Action is type of service action. type Action struct { Action ActionType ServiceType Type Params map[string]interface{} } // Interface is the collection of functions any service needs to implement. type Interface interface { StartService() SetMessageChan(msgChan chan *msg_pb.Message) StopService() NotifyService(map[string]interface{}) // APIs retrieves the list of RPC descriptors the service provides APIs() []rpc.API } // Manager stores all services for service manager. type Manager struct { services map[Type]Interface actionChannel chan *Action } // GetServices returns all registered services. func (m *Manager) GetServices() map[Type]Interface { return m.services } // Register registers new service to service store. func (m *Manager) Register(t Type, service Interface) { utils.Logger().Info().Int("service", int(t)).Msg("Register Service") if m.services == nil { m.services = make(map[Type]Interface) } if _, ok := m.services[t]; ok { utils.Logger().Error().Int("servie", int(t)).Msg("This service is already included") return } m.services[t] = service } // SetupServiceManager inits service map and start service manager. func (m *Manager) SetupServiceManager() { m.InitServiceMap() m.actionChannel = m.StartServiceManager() } // RegisterService is used for testing. func (m *Manager) RegisterService(t Type, service Interface) { m.Register(t, service) } // InitServiceMap initializes service map. func (m *Manager) InitServiceMap() { m.services = make(map[Type]Interface) } // SendAction sends action to action channel which is observed by service manager. func (m *Manager) SendAction(action *Action) { m.actionChannel <- action } // TakeAction is how service manager handles the action. func (m *Manager) TakeAction(action *Action) { if m.services == nil { utils.Logger().Error().Msg("Service store is not initialized") return } if service, ok := m.services[action.ServiceType]; ok { switch action.Action { case Start: service.StartService() case Stop: service.StopService() case Notify: service.NotifyService(action.Params) } } } // StartServiceManager starts service manager. func (m *Manager) StartServiceManager() chan *Action { ch := make(chan *Action) go func() { for { select { case action := <-ch: m.TakeAction(action) case <-time.After(WaitForStatusUpdate): utils.Logger().Info().Msg("Waiting for new action") } } }() return ch } // RunServices run registered services. func (m *Manager) RunServices() { for serviceType := range m.services { action := &Action{ Action: Start, ServiceType: serviceType, } m.TakeAction(action) } } // SetupServiceMessageChan sets up message channel to services. func (m *Manager) SetupServiceMessageChan( mapServiceTypeChan map[Type]chan *msg_pb.Message, ) { for serviceType, service := range m.services { mapServiceTypeChan[serviceType] = make(chan *msg_pb.Message) service.SetMessageChan(mapServiceTypeChan[serviceType]) } } // StopService stops service with type t. func (m *Manager) StopService(t Type) { if service, ok := m.services[t]; ok { service.StopService() } } // StopServicesByRole stops all service of the given role. func (m *Manager) StopServicesByRole(liveServices []Type) { marked := make(map[Type]bool) for _, s := range liveServices { marked[s] = true } for t := range m.GetServices() { if _, ok := marked[t]; !ok { m.StopService(t) } } }