github.1485827954.workers.dev/ethereum/go-ethereum@v1.14.3/beacon/light/request/server.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  	"math"
    21  	"sync"
    22  	"time"
    23  
    24  	"github.com/ethereum/go-ethereum/common/mclock"
    25  	"github.com/ethereum/go-ethereum/log"
    26  )
    27  
    28  var (
    29  	// request events
    30  	EvResponse = &EventType{Name: "response", requestEvent: true} // data: RequestResponse; sent by requestServer
    31  	EvFail     = &EventType{Name: "fail", requestEvent: true}     // data: RequestResponse; sent by requestServer
    32  	EvTimeout  = &EventType{Name: "timeout", requestEvent: true}  // data: RequestResponse; sent by serverWithTimeout
    33  	// server events
    34  	EvRegistered      = &EventType{Name: "registered"}      // data: nil; sent by Scheduler
    35  	EvUnregistered    = &EventType{Name: "unregistered"}    // data: nil; sent by Scheduler
    36  	EvCanRequestAgain = &EventType{Name: "canRequestAgain"} // data: nil; sent by serverWithLimits
    37  )
    38  
    39  const (
    40  	softRequestTimeout = time.Second      // allow resending request to a different server but do not cancel yet
    41  	hardRequestTimeout = time.Second * 10 // cancel request
    42  )
    43  
    44  const (
    45  	// serverWithLimits parameters
    46  	parallelAdjustUp     = 0.1                    // adjust parallelLimit up in case of success under full load
    47  	parallelAdjustDown   = 1                      // adjust parallelLimit down in case of timeout/failure
    48  	minParallelLimit     = 1                      // parallelLimit lower bound
    49  	defaultParallelLimit = 3                      // parallelLimit initial value
    50  	minFailureDelay      = time.Millisecond * 100 // minimum disable time in case of request failure
    51  	maxFailureDelay      = time.Minute            // maximum disable time in case of request failure
    52  	maxServerEventBuffer = 5                      // server event allowance buffer limit
    53  	maxServerEventRate   = time.Second            // server event allowance buffer recharge rate
    54  )
    55  
    56  // requestServer can send requests in a non-blocking way and feed back events
    57  // through the event callback. After each request it should send back either
    58  // EvResponse or EvFail. Additionally, it may also send application-defined
    59  // events that the Modules can interpret.
    60  type requestServer interface {
    61  	Name() string
    62  	Subscribe(eventCallback func(Event))
    63  	SendRequest(ID, Request)
    64  	Unsubscribe()
    65  }
    66  
    67  // server is implemented by a requestServer wrapped into serverWithTimeout and
    68  // serverWithLimits and is used by Scheduler.
    69  // In addition to requestServer functionality, server can also handle timeouts,
    70  // limit the number of parallel in-flight requests and temporarily disable
    71  // new requests based on timeouts and response failures.
    72  type server interface {
    73  	Server
    74  	subscribe(eventCallback func(Event))
    75  	canRequestNow() bool
    76  	sendRequest(Request) ID
    77  	fail(string)
    78  	unsubscribe()
    79  }
    80  
    81  // NewServer wraps a requestServer and returns a server
    82  func NewServer(rs requestServer, clock mclock.Clock) server {
    83  	s := &serverWithLimits{}
    84  	s.parent = rs
    85  	s.serverWithTimeout.init(clock)
    86  	s.init()
    87  	return s
    88  }
    89  
    90  // EventType identifies an event type, either related to a request or the server
    91  // in general. Server events can also be externally defined.
    92  type EventType struct {
    93  	Name         string
    94  	requestEvent bool // all request events are pre-defined in request package
    95  }
    96  
    97  // Event describes an event where the type of Data depends on Type.
    98  // Server field is not required when sent through the event callback; it is filled
    99  // out when processed by the Scheduler. Note that the Scheduler can also create
   100  // and send events (EvRegistered, EvUnregistered) directly.
   101  type Event struct {
   102  	Type   *EventType
   103  	Server Server // filled by Scheduler
   104  	Data   any
   105  }
   106  
   107  // IsRequestEvent returns true if the event is a request event
   108  func (e *Event) IsRequestEvent() bool {
   109  	return e.Type.requestEvent
   110  }
   111  
   112  // RequestInfo assumes that the event is a request event and returns its contents
   113  // in a convenient form.
   114  func (e *Event) RequestInfo() (ServerAndID, Request, Response) {
   115  	data := e.Data.(RequestResponse)
   116  	return ServerAndID{Server: e.Server, ID: data.ID}, data.Request, data.Response
   117  }
   118  
   119  // RequestResponse is the Data type of request events.
   120  type RequestResponse struct {
   121  	ID       ID
   122  	Request  Request
   123  	Response Response
   124  }
   125  
   126  // serverWithTimeout wraps a requestServer and introduces timeouts.
   127  // The request's lifecycle is concluded if EvResponse or EvFail emitted by the
   128  // parent requestServer. If this does not happen until softRequestTimeout then
   129  // EvTimeout is emitted, after which the final EvResponse or EvFail is still
   130  // guaranteed to follow.
   131  // If the parent fails to send this final event for hardRequestTimeout then
   132  // serverWithTimeout emits EvFail and discards any further events from the
   133  // parent related to the given request.
   134  type serverWithTimeout struct {
   135  	parent       requestServer
   136  	lock         sync.Mutex
   137  	clock        mclock.Clock
   138  	childEventCb func(event Event)
   139  	timeouts     map[ID]mclock.Timer
   140  	lastID       ID
   141  }
   142  
   143  // Name implements request.Server
   144  func (s *serverWithTimeout) Name() string {
   145  	return s.parent.Name()
   146  }
   147  
   148  // init initializes serverWithTimeout
   149  func (s *serverWithTimeout) init(clock mclock.Clock) {
   150  	s.clock = clock
   151  	s.timeouts = make(map[ID]mclock.Timer)
   152  }
   153  
   154  // subscribe subscribes to events which include parent (requestServer) events
   155  // plus EvTimeout.
   156  func (s *serverWithTimeout) subscribe(eventCallback func(event Event)) {
   157  	s.lock.Lock()
   158  	defer s.lock.Unlock()
   159  
   160  	s.childEventCb = eventCallback
   161  	s.parent.Subscribe(s.eventCallback)
   162  }
   163  
   164  // sendRequest generated a new request ID, emits EvRequest, sets up the timeout
   165  // timer, then sends the request through the parent (requestServer).
   166  func (s *serverWithTimeout) sendRequest(request Request) (reqId ID) {
   167  	s.lock.Lock()
   168  	s.lastID++
   169  	id := s.lastID
   170  	s.startTimeout(RequestResponse{ID: id, Request: request})
   171  	s.lock.Unlock()
   172  	s.parent.SendRequest(id, request)
   173  	return id
   174  }
   175  
   176  // eventCallback is called by parent (requestServer) event subscription.
   177  func (s *serverWithTimeout) eventCallback(event Event) {
   178  	s.lock.Lock()
   179  	defer s.lock.Unlock()
   180  
   181  	switch event.Type {
   182  	case EvResponse, EvFail:
   183  		id := event.Data.(RequestResponse).ID
   184  		if timer, ok := s.timeouts[id]; ok {
   185  			// Note: if stopping the timer is unsuccessful then the resulting AfterFunc
   186  			// call will just do nothing
   187  			timer.Stop()
   188  			delete(s.timeouts, id)
   189  			s.childEventCb(event)
   190  		}
   191  	default:
   192  		s.childEventCb(event)
   193  	}
   194  }
   195  
   196  // startTimeout starts a timeout timer for the given request.
   197  func (s *serverWithTimeout) startTimeout(reqData RequestResponse) {
   198  	id := reqData.ID
   199  	s.timeouts[id] = s.clock.AfterFunc(softRequestTimeout, func() {
   200  		s.lock.Lock()
   201  		if _, ok := s.timeouts[id]; !ok {
   202  			s.lock.Unlock()
   203  			return
   204  		}
   205  		s.timeouts[id] = s.clock.AfterFunc(hardRequestTimeout-softRequestTimeout, func() {
   206  			s.lock.Lock()
   207  			if _, ok := s.timeouts[id]; !ok {
   208  				s.lock.Unlock()
   209  				return
   210  			}
   211  			delete(s.timeouts, id)
   212  			childEventCb := s.childEventCb
   213  			s.lock.Unlock()
   214  			childEventCb(Event{Type: EvFail, Data: reqData})
   215  		})
   216  		childEventCb := s.childEventCb
   217  		s.lock.Unlock()
   218  		childEventCb(Event{Type: EvTimeout, Data: reqData})
   219  	})
   220  }
   221  
   222  // unsubscribe stops all goroutines associated with the server.
   223  func (s *serverWithTimeout) unsubscribe() {
   224  	s.lock.Lock()
   225  	defer s.lock.Unlock()
   226  
   227  	for _, timer := range s.timeouts {
   228  		if timer != nil {
   229  			timer.Stop()
   230  		}
   231  	}
   232  	s.childEventCb = nil
   233  	s.parent.Unsubscribe()
   234  }
   235  
   236  // serverWithLimits wraps serverWithTimeout and implements server. It limits the
   237  // number of parallel in-flight requests and prevents sending new requests when a
   238  // pending one has already timed out. Server events are also rate limited.
   239  // It also implements a failure delay mechanism that adds an exponentially growing
   240  // delay each time a request fails (wrong answer or hard timeout). This makes the
   241  // syncing mechanism less brittle as temporary failures of the server might happen
   242  // sometimes, but still avoids hammering a non-functional server with requests.
   243  type serverWithLimits struct {
   244  	serverWithTimeout
   245  	lock                       sync.Mutex
   246  	childEventCb               func(event Event)
   247  	softTimeouts               map[ID]struct{}
   248  	pendingCount, timeoutCount int
   249  	parallelLimit              float32
   250  	sendEvent                  bool
   251  	delayTimer                 mclock.Timer
   252  	delayCounter               int
   253  	failureDelayEnd            mclock.AbsTime
   254  	failureDelay               float64
   255  	serverEventBuffer          int
   256  	eventBufferUpdated         mclock.AbsTime
   257  }
   258  
   259  // init initializes serverWithLimits
   260  func (s *serverWithLimits) init() {
   261  	s.softTimeouts = make(map[ID]struct{})
   262  	s.parallelLimit = defaultParallelLimit
   263  	s.serverEventBuffer = maxServerEventBuffer
   264  }
   265  
   266  // subscribe subscribes to events which include parent (serverWithTimeout) events
   267  // plus EvCanRequestAgain.
   268  func (s *serverWithLimits) subscribe(eventCallback func(event Event)) {
   269  	s.lock.Lock()
   270  	defer s.lock.Unlock()
   271  
   272  	s.childEventCb = eventCallback
   273  	s.serverWithTimeout.subscribe(s.eventCallback)
   274  }
   275  
   276  // eventCallback is called by parent (serverWithTimeout) event subscription.
   277  func (s *serverWithLimits) eventCallback(event Event) {
   278  	s.lock.Lock()
   279  	var sendCanRequestAgain bool
   280  	passEvent := true
   281  	switch event.Type {
   282  	case EvTimeout:
   283  		id := event.Data.(RequestResponse).ID
   284  		s.softTimeouts[id] = struct{}{}
   285  		s.timeoutCount++
   286  		s.parallelLimit -= parallelAdjustDown
   287  		if s.parallelLimit < minParallelLimit {
   288  			s.parallelLimit = minParallelLimit
   289  		}
   290  		log.Debug("Server timeout", "count", s.timeoutCount, "parallelLimit", s.parallelLimit)
   291  	case EvResponse, EvFail:
   292  		id := event.Data.(RequestResponse).ID
   293  		if _, ok := s.softTimeouts[id]; ok {
   294  			delete(s.softTimeouts, id)
   295  			s.timeoutCount--
   296  			log.Debug("Server timeout finalized", "count", s.timeoutCount, "parallelLimit", s.parallelLimit)
   297  		}
   298  		if event.Type == EvResponse && s.pendingCount >= int(s.parallelLimit) {
   299  			s.parallelLimit += parallelAdjustUp
   300  		}
   301  		s.pendingCount--
   302  		if s.canRequest() {
   303  			sendCanRequestAgain = s.sendEvent
   304  			s.sendEvent = false
   305  		}
   306  		if event.Type == EvFail {
   307  			s.failLocked("failed request")
   308  		}
   309  	default:
   310  		// server event; check rate limit
   311  		if s.serverEventBuffer < maxServerEventBuffer {
   312  			now := s.clock.Now()
   313  			sinceUpdate := time.Duration(now - s.eventBufferUpdated)
   314  			if sinceUpdate >= maxServerEventRate*time.Duration(maxServerEventBuffer-s.serverEventBuffer) {
   315  				s.serverEventBuffer = maxServerEventBuffer
   316  				s.eventBufferUpdated = now
   317  			} else {
   318  				addBuffer := int(sinceUpdate / maxServerEventRate)
   319  				s.serverEventBuffer += addBuffer
   320  				s.eventBufferUpdated += mclock.AbsTime(maxServerEventRate * time.Duration(addBuffer))
   321  			}
   322  		}
   323  		if s.serverEventBuffer > 0 {
   324  			s.serverEventBuffer--
   325  		} else {
   326  			passEvent = false
   327  		}
   328  	}
   329  	childEventCb := s.childEventCb
   330  	s.lock.Unlock()
   331  	if passEvent {
   332  		childEventCb(event)
   333  	}
   334  	if sendCanRequestAgain {
   335  		childEventCb(Event{Type: EvCanRequestAgain})
   336  	}
   337  }
   338  
   339  // sendRequest sends a request through the parent (serverWithTimeout).
   340  func (s *serverWithLimits) sendRequest(request Request) (reqId ID) {
   341  	s.lock.Lock()
   342  	s.pendingCount++
   343  	s.lock.Unlock()
   344  	return s.serverWithTimeout.sendRequest(request)
   345  }
   346  
   347  // unsubscribe stops all goroutines associated with the server.
   348  func (s *serverWithLimits) unsubscribe() {
   349  	s.lock.Lock()
   350  	defer s.lock.Unlock()
   351  
   352  	if s.delayTimer != nil {
   353  		s.delayTimer.Stop()
   354  		s.delayTimer = nil
   355  	}
   356  	s.childEventCb = nil
   357  	s.serverWithTimeout.unsubscribe()
   358  }
   359  
   360  // canRequest checks whether a new request can be started.
   361  func (s *serverWithLimits) canRequest() bool {
   362  	if s.delayTimer != nil || s.pendingCount >= int(s.parallelLimit) || s.timeoutCount > 0 {
   363  		return false
   364  	}
   365  	if s.parallelLimit < minParallelLimit {
   366  		s.parallelLimit = minParallelLimit
   367  	}
   368  	return true
   369  }
   370  
   371  // canRequestNow checks whether a new request can be started, according to the
   372  // current in-flight request count and parallelLimit, and also the failure delay
   373  // timer.
   374  // If it returns false then it is guaranteed that an EvCanRequestAgain will be
   375  // sent whenever the server becomes available for requesting again.
   376  func (s *serverWithLimits) canRequestNow() bool {
   377  	var sendCanRequestAgain bool
   378  	s.lock.Lock()
   379  	canRequest := s.canRequest()
   380  	if canRequest {
   381  		sendCanRequestAgain = s.sendEvent
   382  		s.sendEvent = false
   383  	}
   384  	childEventCb := s.childEventCb
   385  	s.lock.Unlock()
   386  	if sendCanRequestAgain {
   387  		childEventCb(Event{Type: EvCanRequestAgain})
   388  	}
   389  	return canRequest
   390  }
   391  
   392  // delay sets the delay timer to the given duration, disabling new requests for
   393  // the given period.
   394  func (s *serverWithLimits) delay(delay time.Duration) {
   395  	if s.delayTimer != nil {
   396  		// Note: if stopping the timer is unsuccessful then the resulting AfterFunc
   397  		// call will just do nothing
   398  		s.delayTimer.Stop()
   399  		s.delayTimer = nil
   400  	}
   401  
   402  	s.delayCounter++
   403  	delayCounter := s.delayCounter
   404  	log.Debug("Server delay started", "length", delay)
   405  	s.delayTimer = s.clock.AfterFunc(delay, func() {
   406  		log.Debug("Server delay ended", "length", delay)
   407  		var sendCanRequestAgain bool
   408  		s.lock.Lock()
   409  		if s.delayTimer != nil && s.delayCounter == delayCounter { // do nothing if there is a new timer now
   410  			s.delayTimer = nil
   411  			if s.canRequest() {
   412  				sendCanRequestAgain = s.sendEvent
   413  				s.sendEvent = false
   414  			}
   415  		}
   416  		childEventCb := s.childEventCb
   417  		s.lock.Unlock()
   418  		if sendCanRequestAgain {
   419  			childEventCb(Event{Type: EvCanRequestAgain})
   420  		}
   421  	})
   422  }
   423  
   424  // fail reports that a response from the server was found invalid by the processing
   425  // Module, disabling new requests for a dynamically adjusted time period.
   426  func (s *serverWithLimits) fail(desc string) {
   427  	s.lock.Lock()
   428  	defer s.lock.Unlock()
   429  
   430  	s.failLocked(desc)
   431  }
   432  
   433  // failLocked calculates the dynamic failure delay and applies it.
   434  func (s *serverWithLimits) failLocked(desc string) {
   435  	log.Debug("Server error", "description", desc)
   436  	s.failureDelay *= 2
   437  	now := s.clock.Now()
   438  	if now > s.failureDelayEnd {
   439  		s.failureDelay *= math.Pow(2, -float64(now-s.failureDelayEnd)/float64(maxFailureDelay))
   440  	}
   441  	if s.failureDelay < float64(minFailureDelay) {
   442  		s.failureDelay = float64(minFailureDelay)
   443  	}
   444  	s.failureDelayEnd = now + mclock.AbsTime(s.failureDelay)
   445  	s.delay(time.Duration(s.failureDelay))
   446  }