github.com/lukso-network/go-ethereum@v1.8.22/les/flowcontrol/control.go (about) 1 // Copyright 2016 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 flowcontrol implements a client side flow control mechanism 18 package flowcontrol 19 20 import ( 21 "sync" 22 "time" 23 24 "github.com/ethereum/go-ethereum/common/mclock" 25 ) 26 27 const fcTimeConst = time.Millisecond 28 29 type ServerParams struct { 30 BufLimit, MinRecharge uint64 31 } 32 33 type ClientNode struct { 34 params *ServerParams 35 bufValue uint64 36 lastTime mclock.AbsTime 37 lock sync.Mutex 38 cm *ClientManager 39 cmNode *cmNode 40 } 41 42 func NewClientNode(cm *ClientManager, params *ServerParams) *ClientNode { 43 node := &ClientNode{ 44 cm: cm, 45 params: params, 46 bufValue: params.BufLimit, 47 lastTime: mclock.Now(), 48 } 49 node.cmNode = cm.addNode(node) 50 return node 51 } 52 53 func (peer *ClientNode) Remove(cm *ClientManager) { 54 cm.removeNode(peer.cmNode) 55 } 56 57 func (peer *ClientNode) recalcBV(time mclock.AbsTime) { 58 dt := uint64(time - peer.lastTime) 59 if time < peer.lastTime { 60 dt = 0 61 } 62 peer.bufValue += peer.params.MinRecharge * dt / uint64(fcTimeConst) 63 if peer.bufValue > peer.params.BufLimit { 64 peer.bufValue = peer.params.BufLimit 65 } 66 peer.lastTime = time 67 } 68 69 func (peer *ClientNode) AcceptRequest() (uint64, bool) { 70 peer.lock.Lock() 71 defer peer.lock.Unlock() 72 73 time := mclock.Now() 74 peer.recalcBV(time) 75 return peer.bufValue, peer.cm.accept(peer.cmNode, time) 76 } 77 78 func (peer *ClientNode) RequestProcessed(cost uint64) (bv, realCost uint64) { 79 peer.lock.Lock() 80 defer peer.lock.Unlock() 81 82 time := mclock.Now() 83 peer.recalcBV(time) 84 peer.bufValue -= cost 85 rcValue, rcost := peer.cm.processed(peer.cmNode, time) 86 if rcValue < peer.params.BufLimit { 87 bv := peer.params.BufLimit - rcValue 88 if bv > peer.bufValue { 89 peer.bufValue = bv 90 } 91 } 92 return peer.bufValue, rcost 93 } 94 95 type ServerNode struct { 96 bufEstimate uint64 97 lastTime mclock.AbsTime 98 params *ServerParams 99 sumCost uint64 // sum of req costs sent to this server 100 pending map[uint64]uint64 // value = sumCost after sending the given req 101 lock sync.RWMutex 102 } 103 104 func NewServerNode(params *ServerParams) *ServerNode { 105 return &ServerNode{ 106 bufEstimate: params.BufLimit, 107 lastTime: mclock.Now(), 108 params: params, 109 pending: make(map[uint64]uint64), 110 } 111 } 112 113 func (peer *ServerNode) recalcBLE(time mclock.AbsTime) { 114 dt := uint64(time - peer.lastTime) 115 if time < peer.lastTime { 116 dt = 0 117 } 118 peer.bufEstimate += peer.params.MinRecharge * dt / uint64(fcTimeConst) 119 if peer.bufEstimate > peer.params.BufLimit { 120 peer.bufEstimate = peer.params.BufLimit 121 } 122 peer.lastTime = time 123 } 124 125 // safetyMargin is added to the flow control waiting time when estimated buffer value is low 126 const safetyMargin = time.Millisecond 127 128 func (peer *ServerNode) canSend(maxCost uint64) (time.Duration, float64) { 129 peer.recalcBLE(mclock.Now()) 130 maxCost += uint64(safetyMargin) * peer.params.MinRecharge / uint64(fcTimeConst) 131 if maxCost > peer.params.BufLimit { 132 maxCost = peer.params.BufLimit 133 } 134 if peer.bufEstimate >= maxCost { 135 return 0, float64(peer.bufEstimate-maxCost) / float64(peer.params.BufLimit) 136 } 137 return time.Duration((maxCost - peer.bufEstimate) * uint64(fcTimeConst) / peer.params.MinRecharge), 0 138 } 139 140 // CanSend returns the minimum waiting time required before sending a request 141 // with the given maximum estimated cost. Second return value is the relative 142 // estimated buffer level after sending the request (divided by BufLimit). 143 func (peer *ServerNode) CanSend(maxCost uint64) (time.Duration, float64) { 144 peer.lock.RLock() 145 defer peer.lock.RUnlock() 146 147 return peer.canSend(maxCost) 148 } 149 150 // QueueRequest should be called when the request has been assigned to the given 151 // server node, before putting it in the send queue. It is mandatory that requests 152 // are sent in the same order as the QueueRequest calls are made. 153 func (peer *ServerNode) QueueRequest(reqID, maxCost uint64) { 154 peer.lock.Lock() 155 defer peer.lock.Unlock() 156 157 peer.bufEstimate -= maxCost 158 peer.sumCost += maxCost 159 peer.pending[reqID] = peer.sumCost 160 } 161 162 // GotReply adjusts estimated buffer value according to the value included in 163 // the latest request reply. 164 func (peer *ServerNode) GotReply(reqID, bv uint64) { 165 166 peer.lock.Lock() 167 defer peer.lock.Unlock() 168 169 if bv > peer.params.BufLimit { 170 bv = peer.params.BufLimit 171 } 172 sc, ok := peer.pending[reqID] 173 if !ok { 174 return 175 } 176 delete(peer.pending, reqID) 177 cc := peer.sumCost - sc 178 peer.bufEstimate = 0 179 if bv > cc { 180 peer.bufEstimate = bv - cc 181 } 182 peer.lastTime = mclock.Now() 183 }