github.com/ethereum/go-ethereum@v1.16.1/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 if s.childEventCb != nil { 190 s.childEventCb(event) 191 } 192 } 193 default: 194 if s.childEventCb != nil { 195 s.childEventCb(event) 196 } 197 } 198 } 199 200 // startTimeout starts a timeout timer for the given request. 201 func (s *serverWithTimeout) startTimeout(reqData RequestResponse) { 202 id := reqData.ID 203 s.timeouts[id] = s.clock.AfterFunc(softRequestTimeout, func() { 204 s.lock.Lock() 205 if _, ok := s.timeouts[id]; !ok { 206 s.lock.Unlock() 207 return 208 } 209 s.timeouts[id] = s.clock.AfterFunc(hardRequestTimeout-softRequestTimeout, func() { 210 s.lock.Lock() 211 if _, ok := s.timeouts[id]; !ok { 212 s.lock.Unlock() 213 return 214 } 215 delete(s.timeouts, id) 216 childEventCb := s.childEventCb 217 s.lock.Unlock() 218 if childEventCb != nil { 219 childEventCb(Event{Type: EvFail, Data: reqData}) 220 } 221 }) 222 childEventCb := s.childEventCb 223 s.lock.Unlock() 224 if childEventCb != nil { 225 childEventCb(Event{Type: EvTimeout, Data: reqData}) 226 } 227 }) 228 } 229 230 // unsubscribe stops all goroutines associated with the server. 231 func (s *serverWithTimeout) unsubscribe() { 232 s.lock.Lock() 233 for _, timer := range s.timeouts { 234 if timer != nil { 235 timer.Stop() 236 } 237 } 238 s.lock.Unlock() 239 s.parent.Unsubscribe() 240 } 241 242 // serverWithLimits wraps serverWithTimeout and implements server. It limits the 243 // number of parallel in-flight requests and prevents sending new requests when a 244 // pending one has already timed out. Server events are also rate limited. 245 // It also implements a failure delay mechanism that adds an exponentially growing 246 // delay each time a request fails (wrong answer or hard timeout). This makes the 247 // syncing mechanism less brittle as temporary failures of the server might happen 248 // sometimes, but still avoids hammering a non-functional server with requests. 249 type serverWithLimits struct { 250 serverWithTimeout 251 lock sync.Mutex 252 childEventCb func(event Event) 253 softTimeouts map[ID]struct{} 254 pendingCount, timeoutCount int 255 parallelLimit float32 256 sendEvent bool 257 delayTimer mclock.Timer 258 delayCounter int 259 failureDelayEnd mclock.AbsTime 260 failureDelay float64 261 serverEventBuffer int 262 eventBufferUpdated mclock.AbsTime 263 } 264 265 // init initializes serverWithLimits 266 func (s *serverWithLimits) init() { 267 s.softTimeouts = make(map[ID]struct{}) 268 s.parallelLimit = defaultParallelLimit 269 s.serverEventBuffer = maxServerEventBuffer 270 } 271 272 // subscribe subscribes to events which include parent (serverWithTimeout) events 273 // plus EvCanRequestAgain. 274 func (s *serverWithLimits) subscribe(eventCallback func(event Event)) { 275 s.lock.Lock() 276 defer s.lock.Unlock() 277 278 s.childEventCb = eventCallback 279 s.serverWithTimeout.subscribe(s.eventCallback) 280 } 281 282 // eventCallback is called by parent (serverWithTimeout) event subscription. 283 func (s *serverWithLimits) eventCallback(event Event) { 284 s.lock.Lock() 285 var sendCanRequestAgain bool 286 passEvent := true 287 switch event.Type { 288 case EvTimeout: 289 id := event.Data.(RequestResponse).ID 290 s.softTimeouts[id] = struct{}{} 291 s.timeoutCount++ 292 s.parallelLimit -= parallelAdjustDown 293 if s.parallelLimit < minParallelLimit { 294 s.parallelLimit = minParallelLimit 295 } 296 log.Debug("Server timeout", "count", s.timeoutCount, "parallelLimit", s.parallelLimit) 297 case EvResponse, EvFail: 298 id := event.Data.(RequestResponse).ID 299 if _, ok := s.softTimeouts[id]; ok { 300 delete(s.softTimeouts, id) 301 s.timeoutCount-- 302 log.Debug("Server timeout finalized", "count", s.timeoutCount, "parallelLimit", s.parallelLimit) 303 } 304 if event.Type == EvResponse && s.pendingCount >= int(s.parallelLimit) { 305 s.parallelLimit += parallelAdjustUp 306 } 307 s.pendingCount-- 308 if s.canRequest() { 309 sendCanRequestAgain = s.sendEvent 310 s.sendEvent = false 311 } 312 if event.Type == EvFail { 313 s.failLocked("failed request") 314 } 315 default: 316 // server event; check rate limit 317 if s.serverEventBuffer < maxServerEventBuffer { 318 now := s.clock.Now() 319 sinceUpdate := time.Duration(now - s.eventBufferUpdated) 320 if sinceUpdate >= maxServerEventRate*time.Duration(maxServerEventBuffer-s.serverEventBuffer) { 321 s.serverEventBuffer = maxServerEventBuffer 322 s.eventBufferUpdated = now 323 } else { 324 addBuffer := int(sinceUpdate / maxServerEventRate) 325 s.serverEventBuffer += addBuffer 326 s.eventBufferUpdated += mclock.AbsTime(maxServerEventRate * time.Duration(addBuffer)) 327 } 328 } 329 if s.serverEventBuffer > 0 { 330 s.serverEventBuffer-- 331 } else { 332 passEvent = false 333 } 334 } 335 childEventCb := s.childEventCb 336 s.lock.Unlock() 337 if passEvent && childEventCb != nil { 338 childEventCb(event) 339 } 340 if sendCanRequestAgain && childEventCb != nil { 341 childEventCb(Event{Type: EvCanRequestAgain}) 342 } 343 } 344 345 // sendRequest sends a request through the parent (serverWithTimeout). 346 func (s *serverWithLimits) sendRequest(request Request) (reqId ID) { 347 s.lock.Lock() 348 s.pendingCount++ 349 s.lock.Unlock() 350 return s.serverWithTimeout.sendRequest(request) 351 } 352 353 // unsubscribe stops all goroutines associated with the server. 354 func (s *serverWithLimits) unsubscribe() { 355 s.lock.Lock() 356 if s.delayTimer != nil { 357 s.delayTimer.Stop() 358 s.delayTimer = nil 359 } 360 s.childEventCb = nil 361 s.lock.Unlock() 362 s.serverWithTimeout.unsubscribe() 363 } 364 365 // canRequest checks whether a new request can be started. 366 func (s *serverWithLimits) canRequest() bool { 367 if s.delayTimer != nil || s.pendingCount >= int(s.parallelLimit) || s.timeoutCount > 0 { 368 return false 369 } 370 if s.parallelLimit < minParallelLimit { 371 s.parallelLimit = minParallelLimit 372 } 373 return true 374 } 375 376 // canRequestNow checks whether a new request can be started, according to the 377 // current in-flight request count and parallelLimit, and also the failure delay 378 // timer. 379 // If it returns false then it is guaranteed that an EvCanRequestAgain will be 380 // sent whenever the server becomes available for requesting again. 381 func (s *serverWithLimits) canRequestNow() bool { 382 var sendCanRequestAgain bool 383 s.lock.Lock() 384 canRequest := s.canRequest() 385 if canRequest { 386 sendCanRequestAgain = s.sendEvent 387 s.sendEvent = false 388 } 389 childEventCb := s.childEventCb 390 s.lock.Unlock() 391 if sendCanRequestAgain && childEventCb != nil { 392 childEventCb(Event{Type: EvCanRequestAgain}) 393 } 394 return canRequest 395 } 396 397 // delay sets the delay timer to the given duration, disabling new requests for 398 // the given period. 399 func (s *serverWithLimits) delay(delay time.Duration) { 400 if s.delayTimer != nil { 401 // Note: if stopping the timer is unsuccessful then the resulting AfterFunc 402 // call will just do nothing 403 s.delayTimer.Stop() 404 s.delayTimer = nil 405 } 406 407 s.delayCounter++ 408 delayCounter := s.delayCounter 409 log.Debug("Server delay started", "length", delay) 410 s.delayTimer = s.clock.AfterFunc(delay, func() { 411 log.Debug("Server delay ended", "length", delay) 412 var sendCanRequestAgain bool 413 s.lock.Lock() 414 if s.delayTimer != nil && s.delayCounter == delayCounter { // do nothing if there is a new timer now 415 s.delayTimer = nil 416 if s.canRequest() { 417 sendCanRequestAgain = s.sendEvent 418 s.sendEvent = false 419 } 420 } 421 childEventCb := s.childEventCb 422 s.lock.Unlock() 423 if sendCanRequestAgain && childEventCb != nil { 424 childEventCb(Event{Type: EvCanRequestAgain}) 425 } 426 }) 427 } 428 429 // fail reports that a response from the server was found invalid by the processing 430 // Module, disabling new requests for a dynamically adjusted time period. 431 func (s *serverWithLimits) fail(desc string) { 432 s.lock.Lock() 433 defer s.lock.Unlock() 434 435 s.failLocked(desc) 436 } 437 438 // failLocked calculates the dynamic failure delay and applies it. 439 func (s *serverWithLimits) failLocked(desc string) { 440 log.Debug("Server error", "description", desc) 441 s.failureDelay *= 2 442 now := s.clock.Now() 443 if now > s.failureDelayEnd { 444 s.failureDelay *= math.Pow(2, -float64(now-s.failureDelayEnd)/float64(maxFailureDelay)) 445 } 446 if s.failureDelay < float64(minFailureDelay) { 447 s.failureDelay = float64(minFailureDelay) 448 } 449 s.failureDelayEnd = now + mclock.AbsTime(s.failureDelay) 450 s.delay(time.Duration(s.failureDelay)) 451 }