github.1485827954.workers.dev/ethereum/go-ethereum@v1.14.3/beacon/light/request/scheduler.go (about)

     1  // Copyright 2023 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-ethereum library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package request
    18  
    19  import (
    20  	"sync"
    21  
    22  	"github.com/ethereum/go-ethereum/log"
    23  )
    24  
    25  // Module represents a mechanism which is typically responsible for downloading
    26  // and updating a passive data structure. It does not directly interact with the
    27  // servers. It can start requests using the Requester interface, maintain its
    28  // internal state by receiving and processing Events and update its target data
    29  // structure based on the obtained data.
    30  // It is the Scheduler's responsibility to feed events to the modules, call
    31  // Process as long as there might be something to process and then generate request
    32  // candidates using MakeRequest and start the best possible requests.
    33  // Modules are called by Scheduler whenever a global trigger is fired. All events
    34  // fire the trigger. Changing a target data structure also triggers a next
    35  // processing round as it could make further actions possible either by the same
    36  // or another Module.
    37  type Module interface {
    38  	// Process is a non-blocking function responsible for starting requests,
    39  	// processing events and updating the target data structures(s) and the
    40  	// internal state of the module. Module state typically consists of information
    41  	// about pending requests and registered servers.
    42  	// Process is always called after an event is received or after a target data
    43  	// structure has been changed.
    44  	//
    45  	// Note: Process functions of different modules are never called concurrently;
    46  	// they are called by Scheduler in the same order of priority as they were
    47  	// registered in.
    48  	Process(Requester, []Event)
    49  }
    50  
    51  // Requester allows Modules to obtain the list of momentarily available servers,
    52  // start new requests and report server failure when a response has been proven
    53  // to be invalid in the processing phase.
    54  // Note that all Requester functions should be safe to call from Module.Process.
    55  type Requester interface {
    56  	CanSendTo() []Server
    57  	Send(Server, Request) ID
    58  	Fail(Server, string)
    59  }
    60  
    61  // Scheduler is a modular network data retrieval framework that coordinates multiple
    62  // servers and retrieval mechanisms (modules). It implements a trigger mechanism
    63  // that calls the Process function of registered modules whenever either the state
    64  // of existing data structures or events coming from registered servers could
    65  // allow new operations.
    66  type Scheduler struct {
    67  	lock    sync.Mutex
    68  	modules []Module // first has the highest priority
    69  	names   map[Module]string
    70  	servers map[server]struct{}
    71  	targets map[targetData]uint64
    72  
    73  	requesterLock sync.RWMutex
    74  	serverOrder   []server
    75  	pending       map[ServerAndID]pendingRequest
    76  
    77  	// eventLock guards access to the events list. Note that eventLock can be
    78  	// locked either while lock is locked or unlocked but lock cannot be locked
    79  	// while eventLock is locked.
    80  	eventLock sync.Mutex
    81  	events    []Event
    82  	stopCh    chan chan struct{}
    83  
    84  	triggerCh chan struct{} // restarts waiting sync loop
    85  	// if trigger has already been fired then send to testWaitCh blocks until
    86  	// the triggered processing round is finished
    87  	testWaitCh chan struct{}
    88  }
    89  
    90  type (
    91  	// Server identifies a server without allowing any direct interaction.
    92  	// Note: server interface is used by Scheduler and Tracker but not used by
    93  	// the modules that do not interact with them directly.
    94  	// In order to make module testing easier, Server interface is used in
    95  	// events and modules.
    96  	Server interface {
    97  		Name() string
    98  	}
    99  	Request     any
   100  	Response    any
   101  	ID          uint64
   102  	ServerAndID struct {
   103  		Server Server
   104  		ID     ID
   105  	}
   106  )
   107  
   108  // targetData represents a registered target data structure that increases its
   109  // ChangeCounter whenever it has been changed.
   110  type targetData interface {
   111  	ChangeCounter() uint64
   112  }
   113  
   114  // pendingRequest keeps track of sent and not yet finalized requests and their
   115  // sender modules.
   116  type pendingRequest struct {
   117  	request Request
   118  	module  Module
   119  }
   120  
   121  // NewScheduler creates a new Scheduler.
   122  func NewScheduler() *Scheduler {
   123  	s := &Scheduler{
   124  		servers: make(map[server]struct{}),
   125  		names:   make(map[Module]string),
   126  		pending: make(map[ServerAndID]pendingRequest),
   127  		targets: make(map[targetData]uint64),
   128  		stopCh:  make(chan chan struct{}),
   129  		// Note: testWaitCh should not have capacity in order to ensure
   130  		// that after a trigger happens testWaitCh will block until the resulting
   131  		// processing round has been finished
   132  		triggerCh:  make(chan struct{}, 1),
   133  		testWaitCh: make(chan struct{}),
   134  	}
   135  	return s
   136  }
   137  
   138  // RegisterTarget registers a target data structure, ensuring that any changes
   139  // made to it trigger a new round of Module.Process calls, giving a chance to
   140  // modules to react to the changes.
   141  func (s *Scheduler) RegisterTarget(t targetData) {
   142  	s.lock.Lock()
   143  	defer s.lock.Unlock()
   144  
   145  	s.targets[t] = 0
   146  }
   147  
   148  // RegisterModule registers a module. Should be called before starting the scheduler.
   149  // In each processing round the order of module processing depends on the order of
   150  // registration.
   151  func (s *Scheduler) RegisterModule(m Module, name string) {
   152  	s.lock.Lock()
   153  	defer s.lock.Unlock()
   154  
   155  	s.modules = append(s.modules, m)
   156  	s.names[m] = name
   157  }
   158  
   159  // RegisterServer registers a new server.
   160  func (s *Scheduler) RegisterServer(server server) {
   161  	s.lock.Lock()
   162  	defer s.lock.Unlock()
   163  
   164  	s.addEvent(Event{Type: EvRegistered, Server: server})
   165  	server.subscribe(func(event Event) {
   166  		event.Server = server
   167  		s.addEvent(event)
   168  	})
   169  }
   170  
   171  // UnregisterServer removes a registered server.
   172  func (s *Scheduler) UnregisterServer(server server) {
   173  	s.lock.Lock()
   174  	defer s.lock.Unlock()
   175  
   176  	server.unsubscribe()
   177  	s.addEvent(Event{Type: EvUnregistered, Server: server})
   178  }
   179  
   180  // Start starts the scheduler. It should be called after registering all modules
   181  // and before registering any servers.
   182  func (s *Scheduler) Start() {
   183  	go s.syncLoop()
   184  }
   185  
   186  // Stop stops the scheduler.
   187  func (s *Scheduler) Stop() {
   188  	stop := make(chan struct{})
   189  	s.stopCh <- stop
   190  	<-stop
   191  	s.lock.Lock()
   192  	for server := range s.servers {
   193  		server.unsubscribe()
   194  	}
   195  	s.servers = nil
   196  	s.lock.Unlock()
   197  }
   198  
   199  // syncLoop is the main event loop responsible for event/data processing and
   200  // sending new requests.
   201  // A round of processing starts whenever the global trigger is fired. Triggers
   202  // fired during a processing round ensure that there is going to be a next round.
   203  func (s *Scheduler) syncLoop() {
   204  	for {
   205  		s.lock.Lock()
   206  		s.processRound()
   207  		s.lock.Unlock()
   208  	loop:
   209  		for {
   210  			select {
   211  			case stop := <-s.stopCh:
   212  				close(stop)
   213  				return
   214  			case <-s.triggerCh:
   215  				break loop
   216  			case <-s.testWaitCh:
   217  			}
   218  		}
   219  	}
   220  }
   221  
   222  // targetChanged returns true if a registered target data structure has been
   223  // changed since the last call to this function.
   224  func (s *Scheduler) targetChanged() (changed bool) {
   225  	for target, counter := range s.targets {
   226  		if newCounter := target.ChangeCounter(); newCounter != counter {
   227  			s.targets[target] = newCounter
   228  			changed = true
   229  		}
   230  	}
   231  	return
   232  }
   233  
   234  // processRound runs an entire processing round. It calls the Process functions
   235  // of all modules, passing all relevant events and repeating Process calls as
   236  // long as any changes have been made to the registered target data structures.
   237  // Once all events have been processed and a stable state has been achieved,
   238  // requests are generated and sent if necessary and possible.
   239  func (s *Scheduler) processRound() {
   240  	for {
   241  		log.Trace("Processing modules")
   242  		filteredEvents := s.filterEvents()
   243  		for _, module := range s.modules {
   244  			log.Trace("Processing module", "name", s.names[module], "events", len(filteredEvents[module]))
   245  			module.Process(requester{s, module}, filteredEvents[module])
   246  		}
   247  		if !s.targetChanged() {
   248  			break
   249  		}
   250  	}
   251  }
   252  
   253  // Trigger starts a new processing round. If fired during processing, it ensures
   254  // another full round of processing all modules.
   255  func (s *Scheduler) Trigger() {
   256  	select {
   257  	case s.triggerCh <- struct{}{}:
   258  	default:
   259  	}
   260  }
   261  
   262  // addEvent adds an event to be processed in the next round. Note that it can be
   263  // called regardless of the state of the lock mutex, making it safe for use in
   264  // the server event callback.
   265  func (s *Scheduler) addEvent(event Event) {
   266  	s.eventLock.Lock()
   267  	s.events = append(s.events, event)
   268  	s.eventLock.Unlock()
   269  	s.Trigger()
   270  }
   271  
   272  // filterEvent sorts each Event either as a request event or a server event,
   273  // depending on its type. Request events are also sorted in a map based on the
   274  // module that originally initiated the request. It also ensures that no events
   275  // related to a server are returned before EvRegistered or after EvUnregistered.
   276  // In case of an EvUnregistered server event it also closes all pending requests
   277  // to the given server by adding a failed request event (EvFail), ensuring that
   278  // all requests get finalized and thereby allowing the module logic to be safe
   279  // and simple.
   280  func (s *Scheduler) filterEvents() map[Module][]Event {
   281  	s.eventLock.Lock()
   282  	events := s.events
   283  	s.events = nil
   284  	s.eventLock.Unlock()
   285  
   286  	s.requesterLock.Lock()
   287  	defer s.requesterLock.Unlock()
   288  
   289  	filteredEvents := make(map[Module][]Event)
   290  	for _, event := range events {
   291  		server := event.Server.(server)
   292  		if _, ok := s.servers[server]; !ok && event.Type != EvRegistered {
   293  			continue // before EvRegister or after EvUnregister, discard
   294  		}
   295  
   296  		if event.IsRequestEvent() {
   297  			sid, _, _ := event.RequestInfo()
   298  			pending, ok := s.pending[sid]
   299  			if !ok {
   300  				continue // request already closed, ignore further events
   301  			}
   302  			if event.Type == EvResponse || event.Type == EvFail {
   303  				delete(s.pending, sid) // final event, close pending request
   304  			}
   305  			filteredEvents[pending.module] = append(filteredEvents[pending.module], event)
   306  		} else {
   307  			switch event.Type {
   308  			case EvRegistered:
   309  				s.servers[server] = struct{}{}
   310  				s.serverOrder = append(s.serverOrder, nil)
   311  				copy(s.serverOrder[1:], s.serverOrder[:len(s.serverOrder)-1])
   312  				s.serverOrder[0] = server
   313  			case EvUnregistered:
   314  				s.closePending(event.Server, filteredEvents)
   315  				delete(s.servers, server)
   316  				for i, srv := range s.serverOrder {
   317  					if srv == server {
   318  						copy(s.serverOrder[i:len(s.serverOrder)-1], s.serverOrder[i+1:])
   319  						s.serverOrder = s.serverOrder[:len(s.serverOrder)-1]
   320  						break
   321  					}
   322  				}
   323  			}
   324  			for _, module := range s.modules {
   325  				filteredEvents[module] = append(filteredEvents[module], event)
   326  			}
   327  		}
   328  	}
   329  	return filteredEvents
   330  }
   331  
   332  // closePending closes all pending requests to the given server and adds an EvFail
   333  // event to properly finalize them
   334  func (s *Scheduler) closePending(server Server, filteredEvents map[Module][]Event) {
   335  	for sid, pending := range s.pending {
   336  		if sid.Server == server {
   337  			filteredEvents[pending.module] = append(filteredEvents[pending.module], Event{
   338  				Type:   EvFail,
   339  				Server: server,
   340  				Data: RequestResponse{
   341  					ID:      sid.ID,
   342  					Request: pending.request,
   343  				},
   344  			})
   345  			delete(s.pending, sid)
   346  		}
   347  	}
   348  }
   349  
   350  // requester implements Requester. Note that while requester basically wraps
   351  // Scheduler (with the added information of the currently processed Module), all
   352  // functions are safe to call from Module.Process which is running while
   353  // the Scheduler.lock mutex is held.
   354  type requester struct {
   355  	*Scheduler
   356  	module Module
   357  }
   358  
   359  // CanSendTo returns the list of currently available servers. It also returns
   360  // them in an order of least to most recently used, ensuring a round-robin usage
   361  // of suitable servers if the module always chooses the first suitable one.
   362  func (s requester) CanSendTo() []Server {
   363  	s.requesterLock.RLock()
   364  	defer s.requesterLock.RUnlock()
   365  
   366  	list := make([]Server, 0, len(s.serverOrder))
   367  	for _, server := range s.serverOrder {
   368  		if server.canRequestNow() {
   369  			list = append(list, server)
   370  		}
   371  	}
   372  	return list
   373  }
   374  
   375  // Send sends a request and adds an entry to Scheduler.pending map, ensuring that
   376  // related request events will be delivered to the sender Module.
   377  func (s requester) Send(srv Server, req Request) ID {
   378  	s.requesterLock.Lock()
   379  	defer s.requesterLock.Unlock()
   380  
   381  	server := srv.(server)
   382  	id := server.sendRequest(req)
   383  	sid := ServerAndID{Server: srv, ID: id}
   384  	s.pending[sid] = pendingRequest{request: req, module: s.module}
   385  	for i, ss := range s.serverOrder {
   386  		if ss == server {
   387  			copy(s.serverOrder[i:len(s.serverOrder)-1], s.serverOrder[i+1:])
   388  			s.serverOrder[len(s.serverOrder)-1] = server
   389  			return id
   390  		}
   391  	}
   392  	log.Error("Target server not found in ordered list of registered servers")
   393  	return id
   394  }
   395  
   396  // Fail should be called when a server delivers invalid or useless information.
   397  // Calling Fail disables the given server for a period that is initially short
   398  // but is exponentially growing if it happens frequently. This results in a
   399  // somewhat fault tolerant operation that avoids hammering servers with requests
   400  // that they cannot serve but still gives them a chance periodically.
   401  func (s requester) Fail(srv Server, desc string) {
   402  	srv.(server).fail(desc)
   403  }