beacon/light: simplified Module interface

This commit is contained in:
Zsolt Felfoldi 2024-01-16 05:42:32 +01:00 committed by Felix Lange
parent 30bdde91ae
commit c163cbdfc2
4 changed files with 130 additions and 131 deletions

View file

@ -47,8 +47,7 @@ type Module interface {
// Note: Process functions of different modules are never called concurrently; // Note: Process functions of different modules are never called concurrently;
// they are called by Scheduler in the same order of priority as they were // they are called by Scheduler in the same order of priority as they were
// registered in. // registered in.
HandleEvent(Event) Process([]Event)
Process()
MakeRequest(Server) (Request, float32) MakeRequest(Server) (Request, float32)
} }
@ -67,7 +66,8 @@ type Scheduler struct {
pending map[ServerAndID]pendingRequest pending map[ServerAndID]pendingRequest
eventLock sync.Mutex eventLock sync.Mutex
events []Event serverEvents []Event
requestEvents map[Module][]Event
stopCh chan chan struct{} stopCh chan chan struct{}
triggerCh chan struct{} // restarts waiting sync loop triggerCh chan struct{} // restarts waiting sync loop
@ -150,12 +150,16 @@ func (s *Scheduler) RegisterServer(rs requestServer) {
defer s.lock.Unlock() defer s.lock.Unlock()
server := newServer(rs, s.clock) server := newServer(rs, s.clock)
s.servers[server] = struct{}{}
s.eventLock.Lock()
s.addEvent(Event{Type: EvRegistered, Server: server}) s.addEvent(Event{Type: EvRegistered, Server: server})
s.eventLock.Unlock()
server.subscribe(func(event Event) { server.subscribe(func(event Event) {
event.Server = server event.Server = server
s.eventLock.Lock()
s.addEvent(event) s.addEvent(event)
s.eventLock.Unlock()
}) })
s.servers[server] = struct{}{}
} }
// UnregisterServer removes a registered server. // UnregisterServer removes a registered server.
@ -166,8 +170,10 @@ func (s *Scheduler) UnregisterServer(rs requestServer) {
for server := range s.servers { for server := range s.servers {
if sl, ok := server.(*serverWithLimits); ok && sl.parent == rs { if sl, ok := server.(*serverWithLimits); ok && sl.parent == rs {
server.unsubscribe() server.unsubscribe()
delete(s.servers, server) s.eventLock.Lock()
s.addEvent(Event{Type: EvUnregistered, Server: server}) s.addEvent(Event{Type: EvUnregistered, Server: server})
s.eventLock.Unlock()
delete(s.servers, server)
return return
} }
} }
@ -198,7 +204,6 @@ func (s *Scheduler) Stop() {
func (s *Scheduler) syncLoop() { func (s *Scheduler) syncLoop() {
for { for {
s.lock.Lock() s.lock.Lock()
s.handleEvents()
for { for {
s.processModules() s.processModules()
if !s.targetChanged() { if !s.targetChanged() {
@ -234,8 +239,15 @@ func (s *Scheduler) targetChanged() (changed bool) {
// processModules runs an entire processing round, calling the Process functions // processModules runs an entire processing round, calling the Process functions
// of all modules, passing all relevant events. // of all modules, passing all relevant events.
func (s *Scheduler) processModules() { func (s *Scheduler) processModules() {
s.eventLock.Lock()
serverEvents, requestEvents := s.serverEvents, s.requestEvents
s.serverEvents, s.requestEvents = nil, nil
s.eventLock.Unlock()
log.Debug("Processing modules", "server events", len(s.serverEvents))
for _, module := range s.modules { for _, module := range s.modules {
module.Process() log.Debug("Processing module", "name", s.names[module], "request events", len(s.requestEvents[module]))
module.Process(append(serverEvents, requestEvents[module]...))
} }
} }
@ -246,15 +258,16 @@ func (s *Scheduler) sendRequests() {
servers[server] = struct{}{} servers[server] = struct{}{}
} }
} }
log.Debug("Processing modules", "servers", len(servers)) log.Debug("Generating request candidates", "servers", len(servers))
for _, module := range s.modules { for _, module := range s.modules {
for {
if len(servers) == 0 { if len(servers) == 0 {
return return
} }
s.eventLock.Lock() // ensure that EvRequest is added first
if req, sent := s.tryRequest(module, servers); sent { if req, sent := s.tryRequest(module, servers); sent {
module.HandleEvent(Event{ log.Debug("Sent request", "module", s.names[module])
s.addEvent(Event{
Type: EvRequest, Type: EvRequest,
Server: req.Server, Server: req.Server,
Data: RequestResponse{ Data: RequestResponse{
@ -262,10 +275,8 @@ func (s *Scheduler) sendRequests() {
Request: req.Request, Request: req.Request,
}, },
}) })
} else {
break
}
} }
s.eventLock.Unlock()
} }
} }
@ -314,38 +325,21 @@ func (s *Scheduler) Trigger() {
} }
} }
// addEvent adds an Event either as a request event or a server event, depending
// on its type. In case of an EvUnregistered server event it also closes all
// pending requests to the given server by adding a failed request event (EvFail),
// ensuring that all requests get finalized and thereby allowing the module logic
// to be safe and simple.
func (s *Scheduler) addEvent(event Event) { func (s *Scheduler) addEvent(event Event) {
s.eventLock.Lock() if _, ok := s.servers[event.Server.(server)]; !ok {
s.events = append(s.events, event) return
}
s.Trigger() s.Trigger()
s.eventLock.Unlock()
}
func (s *Scheduler) handleEvents() {
s.eventLock.Lock()
events := s.events
s.events = nil
s.eventLock.Unlock()
for _, event := range events {
if _, ok := s.servers[event.Server.(server)]; ok {
s.handleEvent(event)
} else {
log.Error("Event received from unsubscribed server")
}
}
}
// handleEvent processes an Event and adds it either as a request event or a
// server event, depending on its type. In case of an EvUnregistered server event
// it also closes all pending requests to the given server by emitting a failed
// request event (EvFail), ensuring that all requests get finalized and thereby
// allowing the module logic to be safe and simple.
func (s *Scheduler) handleEvent(event Event) {
if event.IsRequestEvent() { if event.IsRequestEvent() {
sid, _, _ := event.RequestInfo() sid, _, _ := event.RequestInfo()
if pr, ok := s.pending[sid]; ok { if pr, ok := s.pending[sid]; ok {
pr.module.HandleEvent(event) s.addRequestEvent(pr.module, event)
if event.Type != EvTimeout { if event.Type == EvResponse || event.Type == EvFail {
delete(s.pending, sid) delete(s.pending, sid)
} }
} }
@ -356,7 +350,7 @@ func (s *Scheduler) handleEvent(event Event) {
if id.Server != event.Server { if id.Server != event.Server {
continue continue
} }
pending.module.HandleEvent(Event{ s.addRequestEvent(pending.module, Event{
Type: EvFail, Type: EvFail,
Server: event.Server, Server: event.Server,
Data: RequestResponse{ Data: RequestResponse{
@ -366,7 +360,12 @@ func (s *Scheduler) handleEvent(event Event) {
}) })
} }
} }
for _, module := range s.modules { s.serverEvents = append(s.serverEvents, event)
module.HandleEvent(event) }
}
func (s *Scheduler) addRequestEvent(module Module, event Event) {
if s.requestEvents == nil {
s.requestEvents = make(map[Module][]Event)
}
s.requestEvents[module] = append(s.requestEvents[module], event)
} }

View file

@ -67,7 +67,8 @@ func NewHeadSync(headTracker headTracker, chain committeeChain) *HeadSync {
return s return s
} }
func (s *HeadSync) HandleEvent(event request.Event) { func (s *HeadSync) Process(events []request.Event) {
for _, event := range events {
switch event.Type { switch event.Type {
case EvNewHead: case EvNewHead:
s.setServerHead(event.Server, event.Data.(types.HeadInfo)) s.setServerHead(event.Server, event.Data.(types.HeadInfo))
@ -78,9 +79,8 @@ func (s *HeadSync) HandleEvent(event request.Event) {
delete(s.serverHeads, event.Server) delete(s.serverHeads, event.Server)
delete(s.unvalidatedHeads, event.Server) delete(s.unvalidatedHeads, event.Server)
} }
} }
func (s *HeadSync) Process() {
nextPeriod, chainInit := s.chain.NextSyncPeriod() nextPeriod, chainInit := s.chain.NextSyncPeriod()
if nextPeriod != s.nextSyncPeriod || chainInit != s.chainInit { if nextPeriod != s.nextSyncPeriod || chainInit != s.chainInit {
s.nextSyncPeriod, s.chainInit = nextPeriod, chainInit s.nextSyncPeriod, s.chainInit = nextPeriod, chainInit

View file

@ -52,7 +52,8 @@ func NewCheckpointInit(chain committeeChain, checkpointHash common.Hash) *Checkp
} }
} }
func (s *CheckpointInit) HandleEvent(event request.Event) { func (s *CheckpointInit) Process(events []request.Event) {
for _, event := range events {
if !event.IsRequestEvent() { if !event.IsRequestEvent() {
return return
} }
@ -72,10 +73,9 @@ func (s *CheckpointInit) HandleEvent(event request.Event) {
} }
event.Server.Fail("invalid checkpoint data") event.Server.Fail("invalid checkpoint data")
} }
}
} }
func (s *CheckpointInit) Process() {}
func (s *CheckpointInit) MakeRequest(server request.Server) (request.Request, float32) { func (s *CheckpointInit) MakeRequest(server request.Server) (request.Request, float32) {
if s.initialized || s.locked != (request.ServerAndID{}) { if s.initialized || s.locked != (request.ServerAndID{}) {
return nil, 0 return nil, 0
@ -226,7 +226,8 @@ func (u updateResponseList) Less(i, j int) bool {
u[j].Data.(request.RequestResponse).Request.(ReqUpdates).FirstPeriod u[j].Data.(request.RequestResponse).Request.(ReqUpdates).FirstPeriod
} }
func (s *ForwardUpdateSync) HandleEvent(event request.Event) { func (s *ForwardUpdateSync) Process(events []request.Event) {
for _, event := range events {
switch event.Type { switch event.Type {
case request.EvRequest: case request.EvRequest:
sid, req, _ := event.RequestInfo() sid, req, _ := event.RequestInfo()
@ -250,9 +251,8 @@ func (s *ForwardUpdateSync) HandleEvent(event request.Event) {
case request.EvUnregistered: case request.EvUnregistered:
delete(s.nextSyncPeriod, event.Server) delete(s.nextSyncPeriod, event.Server)
} }
} }
func (s *ForwardUpdateSync) Process() {
// try processing ordered list of available responses // try processing ordered list of available responses
sort.Sort(updateResponseList(s.processQueue)) //TODO sort.Sort(updateResponseList(s.processQueue)) //TODO
for s.processQueue != nil { for s.processQueue != nil {

View file

@ -53,7 +53,8 @@ func newBeaconBlockSync(headTracker headTracker) *beaconBlockSync {
} }
} }
func (s *beaconBlockSync) HandleEvent(event request.Event) { func (s *beaconBlockSync) Process(events []request.Event) {
for _, event := range events {
switch event.Type { switch event.Type {
case request.EvRequest: case request.EvRequest:
_, req, _ := event.RequestInfo() _, req, _ := event.RequestInfo()
@ -72,9 +73,8 @@ func (s *beaconBlockSync) HandleEvent(event request.Event) {
case request.EvUnregistered: case request.EvUnregistered:
delete(s.serverHeads, event.Server) delete(s.serverHeads, event.Server)
} }
} }
func (s *beaconBlockSync) Process() {
// send validated head block // send validated head block
if vh := s.headTracker.ValidatedHead(); vh != (types.SignedHeader{}) { if vh := s.headTracker.ValidatedHead(); vh != (types.SignedHeader{}) {
validatedHead := vh.Header.Hash() validatedHead := vh.Header.Hash()