mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-20 10:52:25 +00:00
beacon/light: added comments, made event logic nicer
This commit is contained in:
parent
2ea0421599
commit
f734747364
4 changed files with 120 additions and 108 deletions
|
|
@ -60,6 +60,7 @@ func (s *ApiServer) Subscribe(eventCallback func(event request.Event)) {
|
||||||
func (s *ApiServer) SendRequest(req request.Request) request.ID {
|
func (s *ApiServer) SendRequest(req request.Request) request.ID {
|
||||||
id := request.ID(atomic.AddUint64(&s.lastId, 1))
|
id := request.ID(atomic.AddUint64(&s.lastId, 1))
|
||||||
go func() {
|
go func() {
|
||||||
|
s.eventCallback(request.Event{Type: request.EvRequest, Data: request.RequestResponse{ID: id, Request: req}})
|
||||||
var resp request.Response
|
var resp request.Response
|
||||||
switch data := req.(type) {
|
switch data := req.(type) {
|
||||||
case sync.ReqUpdates:
|
case sync.ReqUpdates:
|
||||||
|
|
|
||||||
|
|
@ -64,11 +64,13 @@ type Scheduler struct {
|
||||||
servers map[server]struct{}
|
servers map[server]struct{}
|
||||||
targets map[targetData]uint64
|
targets map[targetData]uint64
|
||||||
|
|
||||||
pending map[ServerAndID]pendingRequest
|
pending map[ServerAndID]pendingRequest
|
||||||
eventLock sync.Mutex
|
// eventLock guards access to the events list. Note that eventLock can be
|
||||||
serverEvents []Event
|
// locked either while lock is locked or unlocked but lock cannot be locked
|
||||||
requestEvents map[Module][]Event
|
// while eventLock is locked.
|
||||||
stopCh chan chan struct{}
|
eventLock sync.Mutex
|
||||||
|
events []Event
|
||||||
|
stopCh chan chan struct{}
|
||||||
|
|
||||||
triggerCh chan struct{} // restarts waiting sync loop
|
triggerCh chan struct{} // restarts waiting sync loop
|
||||||
// testWaitCh chan struct{} // accepts sends when sync loop is waiting
|
// testWaitCh chan struct{} // accepts sends when sync loop is waiting
|
||||||
|
|
@ -150,15 +152,10 @@ 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()
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -170,10 +167,7 @@ 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()
|
||||||
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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -239,20 +233,16 @@ 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.filterEvents()
|
||||||
serverEvents, requestEvents := s.serverEvents, s.requestEvents
|
log.Debug("Processing modules", "server events", len(serverEvents))
|
||||||
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 {
|
||||||
log.Debug("Processing module", "name", s.names[module], "request events", len(s.requestEvents[module]))
|
log.Debug("Processing module", "name", s.names[module], "request events", len(requestEvents[module]))
|
||||||
module.Process(append(serverEvents, requestEvents[module]...))
|
module.Process(append(serverEvents, requestEvents[module]...))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Scheduler) sendRequests() {
|
func (s *Scheduler) sendRequests() {
|
||||||
servers := make(serverSet)
|
servers := make(map[server]struct{})
|
||||||
for server := range s.servers {
|
for server := range s.servers {
|
||||||
if ok, _ := server.canRequestNow(); ok {
|
if ok, _ := server.canRequestNow(); ok {
|
||||||
servers[server] = struct{}{}
|
servers[server] = struct{}{}
|
||||||
|
|
@ -264,23 +254,25 @@ func (s *Scheduler) sendRequests() {
|
||||||
if len(servers) == 0 {
|
if len(servers) == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
s.eventLock.Lock() // ensure that EvRequest is added first
|
if s.tryRequest(module, servers) {
|
||||||
if req, sent := s.tryRequest(module, servers); sent {
|
|
||||||
log.Debug("Sent request", "module", s.names[module])
|
log.Debug("Sent request", "module", s.names[module])
|
||||||
s.addEvent(Event{
|
|
||||||
Type: EvRequest,
|
|
||||||
Server: req.Server,
|
|
||||||
Data: RequestResponse{
|
|
||||||
ID: req.ID,
|
|
||||||
Request: req.Request,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
s.eventLock.Unlock()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Scheduler) tryRequest(module Module, servers serverSet) (RequestWithID, bool) {
|
// tryRequest tries to generate request candidates for a given module and a given
|
||||||
|
// set of servers, then selects the best candidate if there is one and sends the
|
||||||
|
// request.
|
||||||
|
// The candidates are primarily ranked based on "request priority", a number that
|
||||||
|
// Module.MakeRequest has returned along with the request candidate. This ranking
|
||||||
|
// may or may not be used depending on the type of the request, identical requests
|
||||||
|
// typically have the same priority while multiple item requests may have a
|
||||||
|
// priority based on the number of items requested.
|
||||||
|
// If there are multiple candidates with identical request priority then they are
|
||||||
|
// ranked based on "server priority" which is determined by the server. This value
|
||||||
|
// is typically higher is the server is expected to respond quicker or with a
|
||||||
|
// higher chance (typically a lower number of pending requests).
|
||||||
|
func (s *Scheduler) tryRequest(module Module, servers map[server]struct{}) bool {
|
||||||
var (
|
var (
|
||||||
maxServerPriority, maxRequestPriority float32
|
maxServerPriority, maxRequestPriority float32
|
||||||
bestServer server
|
bestServer server
|
||||||
|
|
@ -309,11 +301,11 @@ func (s *Scheduler) tryRequest(module Module, servers serverSet) (RequestWithID,
|
||||||
}
|
}
|
||||||
log.Debug("Request attempt", "serverCount", serverCount, "removedServers", removed, "requestCandidates", candidates)
|
log.Debug("Request attempt", "serverCount", serverCount, "removedServers", removed, "requestCandidates", candidates)
|
||||||
if bestServer == nil {
|
if bestServer == nil {
|
||||||
return RequestWithID{}, false
|
return false
|
||||||
}
|
}
|
||||||
id := ServerAndID{Server: bestServer, ID: bestServer.sendRequest(bestRequest)}
|
id := ServerAndID{Server: bestServer, ID: bestServer.sendRequest(bestRequest)}
|
||||||
s.pending[id] = pendingRequest{request: bestRequest, module: module}
|
s.pending[id] = pendingRequest{request: bestRequest, module: module}
|
||||||
return RequestWithID{ServerAndID: id, Request: bestRequest}, true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Trigger starts a new processing round. If fired during processing, it ensures
|
// Trigger starts a new processing round. If fired during processing, it ensures
|
||||||
|
|
@ -325,47 +317,70 @@ func (s *Scheduler) Trigger() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// addEvent adds an Event either as a request event or a server event, depending
|
// addEvent adds an event to be processed in the next round. Note that it can be
|
||||||
// on its type. In case of an EvUnregistered server event it also closes all
|
// called regardless of the state of the lock mutex, making it safe for use in
|
||||||
// pending requests to the given server by adding a failed request event (EvFail),
|
// the server event callback.
|
||||||
// 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) {
|
||||||
if _, ok := s.servers[event.Server.(server)]; !ok {
|
s.eventLock.Lock()
|
||||||
return
|
s.events = append(s.events, event)
|
||||||
}
|
s.eventLock.Unlock()
|
||||||
s.Trigger()
|
s.Trigger()
|
||||||
if event.IsRequestEvent() {
|
|
||||||
sid, _, _ := event.RequestInfo()
|
|
||||||
if pr, ok := s.pending[sid]; ok {
|
|
||||||
s.addRequestEvent(pr.module, event)
|
|
||||||
if event.Type == EvResponse || event.Type == EvFail {
|
|
||||||
delete(s.pending, sid)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if event.Type == EvUnregistered {
|
|
||||||
for id, pending := range s.pending {
|
|
||||||
if id.Server != event.Server {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
s.addRequestEvent(pending.module, Event{
|
|
||||||
Type: EvFail,
|
|
||||||
Server: event.Server,
|
|
||||||
Data: RequestResponse{
|
|
||||||
ID: id.ID,
|
|
||||||
Request: pending.request,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
s.serverEvents = append(s.serverEvents, event)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Scheduler) addRequestEvent(module Module, event Event) {
|
// filterEvent sorts each Event either as a request event or a server event,
|
||||||
if s.requestEvents == nil {
|
// depending on its type. Request events are also sorted in a map based on the
|
||||||
s.requestEvents = make(map[Module][]Event)
|
// module that originally initiated the request. It also ensures that no events
|
||||||
|
// related to a server are returned before EvRegistered or after EvUnregistered.
|
||||||
|
// 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) filterEvents() (serverEvents []Event, requestEvents map[Module][]Event) {
|
||||||
|
s.eventLock.Lock()
|
||||||
|
events := s.events
|
||||||
|
s.events = nil
|
||||||
|
s.eventLock.Unlock()
|
||||||
|
|
||||||
|
requestEvents = make(map[Module][]Event)
|
||||||
|
for _, event := range events {
|
||||||
|
server, ok := event.Server.(server)
|
||||||
|
if !ok {
|
||||||
|
log.Error("Server interface type unknown for Scheduler")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if event.Type == EvRegistered {
|
||||||
|
s.servers[server] = struct{}{}
|
||||||
|
}
|
||||||
|
if _, ok := s.servers[server]; !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if event.IsRequestEvent() {
|
||||||
|
sid, _, _ := event.RequestInfo()
|
||||||
|
if pr, ok := s.pending[sid]; ok {
|
||||||
|
requestEvents[pr.module] = append(requestEvents[pr.module], event)
|
||||||
|
if event.Type == EvResponse || event.Type == EvFail {
|
||||||
|
delete(s.pending, sid)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if event.Type == EvUnregistered {
|
||||||
|
delete(s.servers, server)
|
||||||
|
for id, pending := range s.pending {
|
||||||
|
if id.Server != event.Server {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
requestEvents[pending.module] = append(requestEvents[pending.module], Event{
|
||||||
|
Type: EvFail,
|
||||||
|
Server: event.Server,
|
||||||
|
Data: RequestResponse{
|
||||||
|
ID: id.ID,
|
||||||
|
Request: pending.request,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
serverEvents = append(serverEvents, event)
|
||||||
}
|
}
|
||||||
s.requestEvents[module] = append(s.requestEvents[module], event)
|
return
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -85,8 +85,6 @@ func newServer(rs requestServer, clock mclock.Clock) server {
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
type serverSet map[server]struct{}
|
|
||||||
|
|
||||||
// EventType identifies an event type, either related to a request or the server
|
// EventType identifies an event type, either related to a request or the server
|
||||||
// in general. Server events can also be externally defined.
|
// in general. Server events can also be externally defined.
|
||||||
type EventType struct {
|
type EventType struct {
|
||||||
|
|
@ -158,6 +156,35 @@ func (s *serverWithTimeout) eventCallback(event Event) {
|
||||||
defer s.lock.Unlock()
|
defer s.lock.Unlock()
|
||||||
|
|
||||||
switch event.Type {
|
switch event.Type {
|
||||||
|
case EvRequest:
|
||||||
|
id := event.Data.(RequestResponse).ID
|
||||||
|
s.timeouts[id] = s.clock.AfterFunc(softRequestTimeout, func() {
|
||||||
|
/*if s.testTimerResults != nil {
|
||||||
|
s.testTimerResults = append(s.testTimerResults, true) // simulated timer finished
|
||||||
|
}*/
|
||||||
|
s.lock.Lock()
|
||||||
|
if _, ok := s.timeouts[id]; !ok {
|
||||||
|
s.lock.Unlock()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s.timeouts[id] = s.clock.AfterFunc(hardRequestTimeout-softRequestTimeout, func() {
|
||||||
|
/*if s.testTimerResults != nil {
|
||||||
|
s.testTimerResults = append(s.testTimerResults, true) // simulated timer finished
|
||||||
|
}*/
|
||||||
|
s.lock.Lock()
|
||||||
|
if _, ok := s.timeouts[id]; !ok {
|
||||||
|
s.lock.Unlock()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
delete(s.timeouts, id)
|
||||||
|
childEventCb := s.childEventCb
|
||||||
|
s.lock.Unlock()
|
||||||
|
childEventCb(Event{Type: EvFail, Data: event.Data})
|
||||||
|
})
|
||||||
|
childEventCb := s.childEventCb
|
||||||
|
s.lock.Unlock()
|
||||||
|
childEventCb(Event{Type: EvTimeout, Data: event.Data})
|
||||||
|
})
|
||||||
case EvResponse, EvFail:
|
case EvResponse, EvFail:
|
||||||
id := event.Data.(RequestResponse).ID
|
id := event.Data.(RequestResponse).ID
|
||||||
if timer, ok := s.timeouts[id]; ok {
|
if timer, ok := s.timeouts[id]; ok {
|
||||||
|
|
@ -175,38 +202,7 @@ func (s *serverWithTimeout) eventCallback(event Event) {
|
||||||
// sendRequest sends a request through the parent (requestServer) and starts a
|
// sendRequest sends a request through the parent (requestServer) and starts a
|
||||||
// timer for request timeout.
|
// timer for request timeout.
|
||||||
func (s *serverWithTimeout) sendRequest(request Request) (reqId ID) {
|
func (s *serverWithTimeout) sendRequest(request Request) (reqId ID) {
|
||||||
s.lock.Lock()
|
return s.parent.SendRequest(request)
|
||||||
defer s.lock.Unlock()
|
|
||||||
|
|
||||||
reqId = s.parent.SendRequest(request)
|
|
||||||
s.timeouts[reqId] = s.clock.AfterFunc(softRequestTimeout, func() {
|
|
||||||
/*if s.testTimerResults != nil {
|
|
||||||
s.testTimerResults = append(s.testTimerResults, true) // simulated timer finished
|
|
||||||
}*/
|
|
||||||
s.lock.Lock()
|
|
||||||
if _, ok := s.timeouts[reqId]; !ok {
|
|
||||||
s.lock.Unlock()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
s.timeouts[reqId] = s.clock.AfterFunc(hardRequestTimeout-softRequestTimeout, func() {
|
|
||||||
/*if s.testTimerResults != nil {
|
|
||||||
s.testTimerResults = append(s.testTimerResults, true) // simulated timer finished
|
|
||||||
}*/
|
|
||||||
s.lock.Lock()
|
|
||||||
if _, ok := s.timeouts[reqId]; !ok {
|
|
||||||
s.lock.Unlock()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
delete(s.timeouts, reqId)
|
|
||||||
childEventCb := s.childEventCb
|
|
||||||
s.lock.Unlock()
|
|
||||||
childEventCb(Event{Type: EvFail, Data: RequestResponse{ID: reqId, Request: request}})
|
|
||||||
})
|
|
||||||
childEventCb := s.childEventCb
|
|
||||||
s.lock.Unlock()
|
|
||||||
childEventCb(Event{Type: EvTimeout, Data: RequestResponse{ID: reqId, Request: request}})
|
|
||||||
})
|
|
||||||
return reqId
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// stop stops all goroutines associated with the server.
|
// stop stops all goroutines associated with the server.
|
||||||
|
|
|
||||||
|
|
@ -254,7 +254,7 @@ func (s *ForwardUpdateSync) Process(events []request.Event) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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))
|
||||||
for s.processQueue != nil {
|
for s.processQueue != nil {
|
||||||
event := s.processQueue[0]
|
event := s.processQueue[0]
|
||||||
if !s.processResponse(event) {
|
if !s.processResponse(event) {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue