github.com/hardtosaygoodbye/go-ethereum@v1.10.16-0.20220122011429-97003b9e6c15/les/vflux/server/prioritypool.go (about) 1 // Copyright 2020 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 server 18 19 import ( 20 "math" 21 "sync" 22 "time" 23 24 "github.com/hardtosaygoodbye/go-ethereum/common/mclock" 25 "github.com/hardtosaygoodbye/go-ethereum/common/prque" 26 "github.com/hardtosaygoodbye/go-ethereum/log" 27 "github.com/hardtosaygoodbye/go-ethereum/p2p/enode" 28 "github.com/hardtosaygoodbye/go-ethereum/p2p/nodestate" 29 ) 30 31 const ( 32 lazyQueueRefresh = time.Second * 10 // refresh period of the active queue 33 ) 34 35 // priorityPool handles a set of nodes where each node has a capacity (a scalar value) 36 // and a priority (which can change over time and can also depend on the capacity). 37 // A node is active if it has at least the necessary minimal amount of capacity while 38 // inactive nodes have 0 capacity (values between 0 and the minimum are not allowed). 39 // The pool ensures that the number and total capacity of all active nodes are limited 40 // and the highest priority nodes are active at all times (limits can be changed 41 // during operation with immediate effect). 42 // 43 // When activating clients a priority bias is applied in favor of the already active 44 // nodes in order to avoid nodes quickly alternating between active and inactive states 45 // when their priorities are close to each other. The bias is specified in terms of 46 // duration (time) because priorities are expected to usually get lower over time and 47 // therefore a future minimum prediction (see EstMinPriority) should monotonously 48 // decrease with the specified time parameter. 49 // This time bias can be interpreted as minimum expected active time at the given 50 // capacity (if the threshold priority stays the same). 51 // 52 // Nodes in the pool always have either inactiveFlag or activeFlag set. A new node is 53 // added to the pool by externally setting inactiveFlag. priorityPool can switch a node 54 // between inactiveFlag and activeFlag at any time. Nodes can be removed from the pool 55 // by externally resetting both flags. activeFlag should not be set externally. 56 // 57 // The highest priority nodes in "inactive" state are moved to "active" state as soon as 58 // the minimum capacity can be granted for them. The capacity of lower priority active 59 // nodes is reduced or they are demoted to "inactive" state if their priority is 60 // insufficient even at minimal capacity. 61 type priorityPool struct { 62 setup *serverSetup 63 ns *nodestate.NodeStateMachine 64 clock mclock.Clock 65 lock sync.Mutex 66 maxCount, maxCap uint64 67 minCap uint64 68 activeBias time.Duration 69 capacityStepDiv, fineStepDiv uint64 70 71 // The snapshot of priority pool for query. 72 cachedCurve *capacityCurve 73 ccUpdatedAt mclock.AbsTime 74 ccUpdateForced bool 75 76 // Runtime status of prioritypool, represents the 77 // temporary state if tempState is not empty 78 tempState []*ppNodeInfo 79 activeCount, activeCap uint64 80 activeQueue *prque.LazyQueue 81 inactiveQueue *prque.Prque 82 } 83 84 // ppNodeInfo is the internal node descriptor of priorityPool 85 type ppNodeInfo struct { 86 nodePriority nodePriority 87 node *enode.Node 88 connected bool 89 capacity uint64 // only changed when temporary state is committed 90 activeIndex, inactiveIndex int 91 92 tempState bool // should only be true while the priorityPool lock is held 93 tempCapacity uint64 // equals capacity when tempState is false 94 95 // the following fields only affect the temporary state and they are set to their 96 // default value when leaving the temp state 97 minTarget, stepDiv uint64 98 bias time.Duration 99 } 100 101 // newPriorityPool creates a new priorityPool 102 func newPriorityPool(ns *nodestate.NodeStateMachine, setup *serverSetup, clock mclock.Clock, minCap uint64, activeBias time.Duration, capacityStepDiv, fineStepDiv uint64) *priorityPool { 103 pp := &priorityPool{ 104 setup: setup, 105 ns: ns, 106 clock: clock, 107 inactiveQueue: prque.New(inactiveSetIndex), 108 minCap: minCap, 109 activeBias: activeBias, 110 capacityStepDiv: capacityStepDiv, 111 fineStepDiv: fineStepDiv, 112 } 113 if pp.activeBias < time.Duration(1) { 114 pp.activeBias = time.Duration(1) 115 } 116 pp.activeQueue = prque.NewLazyQueue(activeSetIndex, activePriority, pp.activeMaxPriority, clock, lazyQueueRefresh) 117 118 ns.SubscribeField(pp.setup.balanceField, func(node *enode.Node, state nodestate.Flags, oldValue, newValue interface{}) { 119 if newValue != nil { 120 c := &ppNodeInfo{ 121 node: node, 122 nodePriority: newValue.(nodePriority), 123 activeIndex: -1, 124 inactiveIndex: -1, 125 } 126 ns.SetFieldSub(node, pp.setup.queueField, c) 127 ns.SetStateSub(node, setup.inactiveFlag, nodestate.Flags{}, 0) 128 } else { 129 ns.SetStateSub(node, nodestate.Flags{}, pp.setup.activeFlag.Or(pp.setup.inactiveFlag), 0) 130 if n, _ := pp.ns.GetField(node, pp.setup.queueField).(*ppNodeInfo); n != nil { 131 pp.disconnectNode(n) 132 } 133 ns.SetFieldSub(node, pp.setup.capacityField, nil) 134 ns.SetFieldSub(node, pp.setup.queueField, nil) 135 } 136 }) 137 ns.SubscribeState(pp.setup.activeFlag.Or(pp.setup.inactiveFlag), func(node *enode.Node, oldState, newState nodestate.Flags) { 138 if c, _ := pp.ns.GetField(node, pp.setup.queueField).(*ppNodeInfo); c != nil { 139 if oldState.IsEmpty() { 140 pp.connectNode(c) 141 } 142 if newState.IsEmpty() { 143 pp.disconnectNode(c) 144 } 145 } 146 }) 147 ns.SubscribeState(pp.setup.updateFlag, func(node *enode.Node, oldState, newState nodestate.Flags) { 148 if !newState.IsEmpty() { 149 pp.updatePriority(node) 150 } 151 }) 152 return pp 153 } 154 155 // requestCapacity tries to set the capacity of a connected node to the highest possible 156 // value inside the given target range. If maxTarget is not reachable then the capacity is 157 // iteratively reduced in fine steps based on the fineStepDiv parameter until minTarget is reached. 158 // The function returns the new capacity if successful and the original capacity otherwise. 159 // Note: this function should run inside a NodeStateMachine operation 160 func (pp *priorityPool) requestCapacity(node *enode.Node, minTarget, maxTarget uint64, bias time.Duration) uint64 { 161 pp.lock.Lock() 162 pp.activeQueue.Refresh() 163 164 if minTarget < pp.minCap { 165 minTarget = pp.minCap 166 } 167 if maxTarget < minTarget { 168 maxTarget = minTarget 169 } 170 if bias < pp.activeBias { 171 bias = pp.activeBias 172 } 173 c, _ := pp.ns.GetField(node, pp.setup.queueField).(*ppNodeInfo) 174 if c == nil { 175 log.Error("requestCapacity called for unknown node", "id", node.ID()) 176 pp.lock.Unlock() 177 return 0 178 } 179 pp.setTempState(c) 180 if maxTarget > c.capacity { 181 pp.setTempStepDiv(c, pp.fineStepDiv) 182 pp.setTempBias(c, bias) 183 } 184 pp.setTempCapacity(c, maxTarget) 185 c.minTarget = minTarget 186 pp.activeQueue.Remove(c.activeIndex) 187 pp.inactiveQueue.Remove(c.inactiveIndex) 188 pp.activeQueue.Push(c) 189 pp.enforceLimits() 190 updates := pp.finalizeChanges(c.tempCapacity >= minTarget && c.tempCapacity <= maxTarget && c.tempCapacity != c.capacity) 191 pp.lock.Unlock() 192 pp.updateFlags(updates) 193 return c.capacity 194 } 195 196 // SetLimits sets the maximum number and total capacity of simultaneously active nodes 197 func (pp *priorityPool) SetLimits(maxCount, maxCap uint64) { 198 pp.lock.Lock() 199 pp.activeQueue.Refresh() 200 inc := (maxCount > pp.maxCount) || (maxCap > pp.maxCap) 201 dec := (maxCount < pp.maxCount) || (maxCap < pp.maxCap) 202 pp.maxCount, pp.maxCap = maxCount, maxCap 203 204 var updates []capUpdate 205 if dec { 206 pp.enforceLimits() 207 updates = pp.finalizeChanges(true) 208 } 209 if inc { 210 updates = append(updates, pp.tryActivate(false)...) 211 } 212 pp.lock.Unlock() 213 pp.ns.Operation(func() { pp.updateFlags(updates) }) 214 } 215 216 // setActiveBias sets the bias applied when trying to activate inactive nodes 217 func (pp *priorityPool) setActiveBias(bias time.Duration) { 218 pp.lock.Lock() 219 pp.activeBias = bias 220 if pp.activeBias < time.Duration(1) { 221 pp.activeBias = time.Duration(1) 222 } 223 updates := pp.tryActivate(false) 224 pp.lock.Unlock() 225 pp.ns.Operation(func() { pp.updateFlags(updates) }) 226 } 227 228 // Active returns the number and total capacity of currently active nodes 229 func (pp *priorityPool) Active() (uint64, uint64) { 230 pp.lock.Lock() 231 defer pp.lock.Unlock() 232 233 return pp.activeCount, pp.activeCap 234 } 235 236 // Inactive returns the number of currently inactive nodes 237 func (pp *priorityPool) Inactive() int { 238 pp.lock.Lock() 239 defer pp.lock.Unlock() 240 241 return pp.inactiveQueue.Size() 242 } 243 244 // Limits returns the maximum allowed number and total capacity of active nodes 245 func (pp *priorityPool) Limits() (uint64, uint64) { 246 pp.lock.Lock() 247 defer pp.lock.Unlock() 248 249 return pp.maxCount, pp.maxCap 250 } 251 252 // inactiveSetIndex callback updates ppNodeInfo item index in inactiveQueue 253 func inactiveSetIndex(a interface{}, index int) { 254 a.(*ppNodeInfo).inactiveIndex = index 255 } 256 257 // activeSetIndex callback updates ppNodeInfo item index in activeQueue 258 func activeSetIndex(a interface{}, index int) { 259 a.(*ppNodeInfo).activeIndex = index 260 } 261 262 // invertPriority inverts a priority value. The active queue uses inverted priorities 263 // because the node on the top is the first to be deactivated. 264 func invertPriority(p int64) int64 { 265 if p == math.MinInt64 { 266 return math.MaxInt64 267 } 268 return -p 269 } 270 271 // activePriority callback returns actual priority of ppNodeInfo item in activeQueue 272 func activePriority(a interface{}) int64 { 273 c := a.(*ppNodeInfo) 274 if c.bias == 0 { 275 return invertPriority(c.nodePriority.priority(c.tempCapacity)) 276 } else { 277 return invertPriority(c.nodePriority.estimatePriority(c.tempCapacity, 0, 0, c.bias, true)) 278 } 279 } 280 281 // activeMaxPriority callback returns estimated maximum priority of ppNodeInfo item in activeQueue 282 func (pp *priorityPool) activeMaxPriority(a interface{}, until mclock.AbsTime) int64 { 283 c := a.(*ppNodeInfo) 284 future := time.Duration(until - pp.clock.Now()) 285 if future < 0 { 286 future = 0 287 } 288 return invertPriority(c.nodePriority.estimatePriority(c.tempCapacity, 0, future, c.bias, false)) 289 } 290 291 // inactivePriority callback returns actual priority of ppNodeInfo item in inactiveQueue 292 func (pp *priorityPool) inactivePriority(p *ppNodeInfo) int64 { 293 return p.nodePriority.priority(pp.minCap) 294 } 295 296 // connectNode is called when a new node has been added to the pool (inactiveFlag set) 297 // Note: this function should run inside a NodeStateMachine operation 298 func (pp *priorityPool) connectNode(c *ppNodeInfo) { 299 pp.lock.Lock() 300 pp.activeQueue.Refresh() 301 if c.connected { 302 pp.lock.Unlock() 303 return 304 } 305 c.connected = true 306 pp.inactiveQueue.Push(c, pp.inactivePriority(c)) 307 updates := pp.tryActivate(false) 308 pp.lock.Unlock() 309 pp.updateFlags(updates) 310 } 311 312 // disconnectNode is called when a node has been removed from the pool (both inactiveFlag 313 // and activeFlag reset) 314 // Note: this function should run inside a NodeStateMachine operation 315 func (pp *priorityPool) disconnectNode(c *ppNodeInfo) { 316 pp.lock.Lock() 317 pp.activeQueue.Refresh() 318 if !c.connected { 319 pp.lock.Unlock() 320 return 321 } 322 c.connected = false 323 pp.activeQueue.Remove(c.activeIndex) 324 pp.inactiveQueue.Remove(c.inactiveIndex) 325 326 var updates []capUpdate 327 if c.capacity != 0 { 328 pp.setTempState(c) 329 pp.setTempCapacity(c, 0) 330 updates = pp.tryActivate(true) 331 } 332 pp.lock.Unlock() 333 pp.updateFlags(updates) 334 } 335 336 // setTempState internally puts a node in a temporary state that can either be reverted 337 // or confirmed later. This temporary state allows changing the capacity of a node and 338 // moving it between the active and inactive queue. activeFlag/inactiveFlag and 339 // capacityField are not changed while the changes are still temporary. 340 func (pp *priorityPool) setTempState(c *ppNodeInfo) { 341 if c.tempState { 342 return 343 } 344 c.tempState = true 345 if c.tempCapacity != c.capacity { // should never happen 346 log.Error("tempCapacity != capacity when entering tempState") 347 } 348 // Assign all the defaults to the temp state. 349 c.minTarget = pp.minCap 350 c.stepDiv = pp.capacityStepDiv 351 c.bias = 0 352 pp.tempState = append(pp.tempState, c) 353 } 354 355 // unsetTempState revokes the temp status of the node and reset all internal 356 // fields to the default value. 357 func (pp *priorityPool) unsetTempState(c *ppNodeInfo) { 358 if !c.tempState { 359 return 360 } 361 c.tempState = false 362 if c.tempCapacity != c.capacity { // should never happen 363 log.Error("tempCapacity != capacity when leaving tempState") 364 } 365 c.minTarget = pp.minCap 366 c.stepDiv = pp.capacityStepDiv 367 c.bias = 0 368 } 369 370 // setTempCapacity changes the capacity of a node in the temporary state and adjusts 371 // activeCap and activeCount accordingly. Since this change is performed in the temporary 372 // state it should be called after setTempState and before finalizeChanges. 373 func (pp *priorityPool) setTempCapacity(c *ppNodeInfo, cap uint64) { 374 if !c.tempState { // should never happen 375 log.Error("Node is not in temporary state") 376 return 377 } 378 pp.activeCap += cap - c.tempCapacity 379 if c.tempCapacity == 0 { 380 pp.activeCount++ 381 } 382 if cap == 0 { 383 pp.activeCount-- 384 } 385 c.tempCapacity = cap 386 } 387 388 // setTempBias changes the connection bias of a node in the temporary state. 389 func (pp *priorityPool) setTempBias(c *ppNodeInfo, bias time.Duration) { 390 if !c.tempState { // should never happen 391 log.Error("Node is not in temporary state") 392 return 393 } 394 c.bias = bias 395 } 396 397 // setTempStepDiv changes the capacity divisor of a node in the temporary state. 398 func (pp *priorityPool) setTempStepDiv(c *ppNodeInfo, stepDiv uint64) { 399 if !c.tempState { // should never happen 400 log.Error("Node is not in temporary state") 401 return 402 } 403 c.stepDiv = stepDiv 404 } 405 406 // enforceLimits enforces active node count and total capacity limits. It returns the 407 // lowest active node priority. Note that this function is performed on the temporary 408 // internal state. 409 func (pp *priorityPool) enforceLimits() (*ppNodeInfo, int64) { 410 if pp.activeCap <= pp.maxCap && pp.activeCount <= pp.maxCount { 411 return nil, math.MinInt64 412 } 413 var ( 414 c *ppNodeInfo 415 maxActivePriority int64 416 ) 417 pp.activeQueue.MultiPop(func(data interface{}, priority int64) bool { 418 c = data.(*ppNodeInfo) 419 pp.setTempState(c) 420 maxActivePriority = priority 421 if c.tempCapacity == c.minTarget || pp.activeCount > pp.maxCount { 422 pp.setTempCapacity(c, 0) 423 } else { 424 sub := c.tempCapacity / c.stepDiv 425 if sub == 0 { 426 sub = 1 427 } 428 if c.tempCapacity-sub < c.minTarget { 429 sub = c.tempCapacity - c.minTarget 430 } 431 pp.setTempCapacity(c, c.tempCapacity-sub) 432 pp.activeQueue.Push(c) 433 } 434 return pp.activeCap > pp.maxCap || pp.activeCount > pp.maxCount 435 }) 436 return c, invertPriority(maxActivePriority) 437 } 438 439 // finalizeChanges either commits or reverts temporary changes. The necessary capacity 440 // field and according flag updates are not performed here but returned in a list because 441 // they should be performed while the mutex is not held. 442 func (pp *priorityPool) finalizeChanges(commit bool) (updates []capUpdate) { 443 for _, c := range pp.tempState { 444 // always remove and push back in order to update biased priority 445 pp.activeQueue.Remove(c.activeIndex) 446 pp.inactiveQueue.Remove(c.inactiveIndex) 447 oldCapacity := c.capacity 448 if commit { 449 c.capacity = c.tempCapacity 450 } else { 451 pp.setTempCapacity(c, c.capacity) // revert activeCount/activeCap 452 } 453 pp.unsetTempState(c) 454 455 if c.connected { 456 if c.capacity != 0 { 457 pp.activeQueue.Push(c) 458 } else { 459 pp.inactiveQueue.Push(c, pp.inactivePriority(c)) 460 } 461 if c.capacity != oldCapacity { 462 updates = append(updates, capUpdate{c.node, oldCapacity, c.capacity}) 463 } 464 } 465 } 466 pp.tempState = nil 467 if commit { 468 pp.ccUpdateForced = true 469 } 470 return 471 } 472 473 // capUpdate describes a capacityField and activeFlag/inactiveFlag update 474 type capUpdate struct { 475 node *enode.Node 476 oldCap, newCap uint64 477 } 478 479 // updateFlags performs capacityField and activeFlag/inactiveFlag updates while the 480 // pool mutex is not held 481 // Note: this function should run inside a NodeStateMachine operation 482 func (pp *priorityPool) updateFlags(updates []capUpdate) { 483 for _, f := range updates { 484 if f.oldCap == 0 { 485 pp.ns.SetStateSub(f.node, pp.setup.activeFlag, pp.setup.inactiveFlag, 0) 486 } 487 if f.newCap == 0 { 488 pp.ns.SetStateSub(f.node, pp.setup.inactiveFlag, pp.setup.activeFlag, 0) 489 pp.ns.SetFieldSub(f.node, pp.setup.capacityField, nil) 490 } else { 491 pp.ns.SetFieldSub(f.node, pp.setup.capacityField, f.newCap) 492 } 493 } 494 } 495 496 // tryActivate tries to activate inactive nodes if possible 497 func (pp *priorityPool) tryActivate(commit bool) []capUpdate { 498 for pp.inactiveQueue.Size() > 0 { 499 c := pp.inactiveQueue.PopItem().(*ppNodeInfo) 500 pp.setTempState(c) 501 pp.setTempBias(c, pp.activeBias) 502 pp.setTempCapacity(c, pp.minCap) 503 pp.activeQueue.Push(c) 504 pp.enforceLimits() 505 if c.tempCapacity > 0 { 506 commit = true 507 pp.setTempBias(c, 0) 508 } else { 509 break 510 } 511 } 512 pp.ccUpdateForced = true 513 return pp.finalizeChanges(commit) 514 } 515 516 // updatePriority gets the current priority value of the given node from the nodePriority 517 // interface and performs the necessary changes. It is triggered by updateFlag. 518 // Note: this function should run inside a NodeStateMachine operation 519 func (pp *priorityPool) updatePriority(node *enode.Node) { 520 pp.lock.Lock() 521 pp.activeQueue.Refresh() 522 c, _ := pp.ns.GetField(node, pp.setup.queueField).(*ppNodeInfo) 523 if c == nil || !c.connected { 524 pp.lock.Unlock() 525 return 526 } 527 pp.activeQueue.Remove(c.activeIndex) 528 pp.inactiveQueue.Remove(c.inactiveIndex) 529 if c.capacity != 0 { 530 pp.activeQueue.Push(c) 531 } else { 532 pp.inactiveQueue.Push(c, pp.inactivePriority(c)) 533 } 534 updates := pp.tryActivate(false) 535 pp.lock.Unlock() 536 pp.updateFlags(updates) 537 } 538 539 // capacityCurve is a snapshot of the priority pool contents in a format that can efficiently 540 // estimate how much capacity could be granted to a given node at a given priority level. 541 type capacityCurve struct { 542 points []curvePoint // curve points sorted in descending order of priority 543 index map[enode.ID][]int // curve point indexes belonging to each node 544 excludeList []int // curve point indexes of excluded node 545 excludeFirst bool // true if activeCount == maxCount 546 } 547 548 type curvePoint struct { 549 freeCap uint64 // available capacity and node count at the current priority level 550 nextPri int64 // next priority level where more capacity will be available 551 } 552 553 // getCapacityCurve returns a new or recently cached capacityCurve based on the contents of the pool 554 func (pp *priorityPool) getCapacityCurve() *capacityCurve { 555 pp.lock.Lock() 556 defer pp.lock.Unlock() 557 558 now := pp.clock.Now() 559 dt := time.Duration(now - pp.ccUpdatedAt) 560 if !pp.ccUpdateForced && pp.cachedCurve != nil && dt < time.Second*10 { 561 return pp.cachedCurve 562 } 563 564 pp.ccUpdateForced = false 565 pp.ccUpdatedAt = now 566 curve := &capacityCurve{ 567 index: make(map[enode.ID][]int), 568 } 569 pp.cachedCurve = curve 570 571 var excludeID enode.ID 572 excludeFirst := pp.maxCount == pp.activeCount 573 // reduce node capacities or remove nodes until nothing is left in the queue; 574 // record the available capacity and the necessary priority after each step 575 lastPri := int64(math.MinInt64) 576 for pp.activeCap > 0 { 577 cp := curvePoint{} 578 if pp.activeCap > pp.maxCap { 579 log.Error("Active capacity is greater than allowed maximum", "active", pp.activeCap, "maximum", pp.maxCap) 580 } else { 581 cp.freeCap = pp.maxCap - pp.activeCap 582 } 583 // temporarily increase activeCap to enforce reducing or removing a node capacity 584 tempCap := cp.freeCap + 1 585 pp.activeCap += tempCap 586 var next *ppNodeInfo 587 // enforceLimits removes the lowest priority node if it has minimal capacity, 588 // otherwise reduces its capacity 589 next, cp.nextPri = pp.enforceLimits() 590 if cp.nextPri < lastPri { 591 // enforce monotonicity which may be broken by continuously changing priorities 592 cp.nextPri = lastPri 593 } else { 594 lastPri = cp.nextPri 595 } 596 pp.activeCap -= tempCap 597 if next == nil { 598 log.Error("getCapacityCurve: cannot remove next element from the priority queue") 599 break 600 } 601 id := next.node.ID() 602 if excludeFirst { 603 // if the node count limit is already reached then mark the node with the 604 // lowest priority for exclusion 605 curve.excludeFirst = true 606 excludeID = id 607 excludeFirst = false 608 } 609 // multiple curve points and therefore multiple indexes may belong to a node 610 // if it was removed in multiple steps (if its capacity was more than the minimum) 611 curve.index[id] = append(curve.index[id], len(curve.points)) 612 curve.points = append(curve.points, cp) 613 } 614 // restore original state of the queue 615 pp.finalizeChanges(false) 616 curve.points = append(curve.points, curvePoint{ 617 freeCap: pp.maxCap, 618 nextPri: math.MaxInt64, 619 }) 620 if curve.excludeFirst { 621 curve.excludeList = curve.index[excludeID] 622 } 623 return curve 624 } 625 626 // exclude returns a capacityCurve with the given node excluded from the original curve 627 func (cc *capacityCurve) exclude(id enode.ID) *capacityCurve { 628 if excludeList, ok := cc.index[id]; ok { 629 // return a new version of the curve (only one excluded node can be selected) 630 // Note: if the first node was excluded by default (excludeFirst == true) then 631 // we can forget about that and exclude the node with the given id instead. 632 return &capacityCurve{ 633 points: cc.points, 634 index: cc.index, 635 excludeList: excludeList, 636 } 637 } 638 return cc 639 } 640 641 func (cc *capacityCurve) getPoint(i int) curvePoint { 642 cp := cc.points[i] 643 if i == 0 && cc.excludeFirst { 644 cp.freeCap = 0 645 return cp 646 } 647 for ii := len(cc.excludeList) - 1; ii >= 0; ii-- { 648 ei := cc.excludeList[ii] 649 if ei < i { 650 break 651 } 652 e1, e2 := cc.points[ei], cc.points[ei+1] 653 cp.freeCap += e2.freeCap - e1.freeCap 654 } 655 return cp 656 } 657 658 // maxCapacity calculates the maximum capacity available for a node with a given 659 // (monotonically decreasing) priority vs. capacity function. Note that if the requesting 660 // node is already in the pool then it should be excluded from the curve in order to get 661 // the correct result. 662 func (cc *capacityCurve) maxCapacity(priority func(cap uint64) int64) uint64 { 663 min, max := 0, len(cc.points)-1 // the curve always has at least one point 664 for min < max { 665 mid := (min + max) / 2 666 cp := cc.getPoint(mid) 667 if cp.freeCap == 0 || priority(cp.freeCap) > cp.nextPri { 668 min = mid + 1 669 } else { 670 max = mid 671 } 672 } 673 cp2 := cc.getPoint(min) 674 if cp2.freeCap == 0 || min == 0 { 675 return cp2.freeCap 676 } 677 cp1 := cc.getPoint(min - 1) 678 if priority(cp2.freeCap) > cp1.nextPri { 679 return cp2.freeCap 680 } 681 minc, maxc := cp1.freeCap, cp2.freeCap-1 682 for minc < maxc { 683 midc := (minc + maxc + 1) / 2 684 if midc == 0 || priority(midc) > cp1.nextPri { 685 minc = midc 686 } else { 687 maxc = midc - 1 688 } 689 } 690 return maxc 691 }