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 }