github.com/daeglee/go-ethereum@v0.0.0-20190504220456-cad3e8d18e9b/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 "fmt" 22 "sync" 23 "time" 24 25 "github.com/ethereum/go-ethereum/common/mclock" 26 "github.com/ethereum/go-ethereum/log" 27 ) 28 29 const ( 30 // fcTimeConst is the time constant applied for MinRecharge during linear 31 // buffer recharge period 32 fcTimeConst = time.Millisecond 33 // DecParamDelay is applied at server side when decreasing capacity in order to 34 // avoid a buffer underrun error due to requests sent by the client before 35 // receiving the capacity update announcement 36 DecParamDelay = time.Second * 2 37 // keepLogs is the duration of keeping logs; logging is not used if zero 38 keepLogs = 0 39 ) 40 41 // ServerParams are the flow control parameters specified by a server for a client 42 // 43 // Note: a server can assign different amounts of capacity to each client by giving 44 // different parameters to them. 45 type ServerParams struct { 46 BufLimit, MinRecharge uint64 47 } 48 49 // scheduledUpdate represents a delayed flow control parameter update 50 type scheduledUpdate struct { 51 time mclock.AbsTime 52 params ServerParams 53 } 54 55 // ClientNode is the flow control system's representation of a client 56 // (used in server mode only) 57 type ClientNode struct { 58 params ServerParams 59 bufValue uint64 60 lastTime mclock.AbsTime 61 updateSchedule []scheduledUpdate 62 sumCost uint64 // sum of req costs received from this client 63 accepted map[uint64]uint64 // value = sumCost after accepting the given req 64 lock sync.Mutex 65 cm *ClientManager 66 log *logger 67 cmNodeFields 68 } 69 70 // NewClientNode returns a new ClientNode 71 func NewClientNode(cm *ClientManager, params ServerParams) *ClientNode { 72 node := &ClientNode{ 73 cm: cm, 74 params: params, 75 bufValue: params.BufLimit, 76 lastTime: cm.clock.Now(), 77 accepted: make(map[uint64]uint64), 78 } 79 if keepLogs > 0 { 80 node.log = newLogger(keepLogs) 81 } 82 cm.connect(node) 83 return node 84 } 85 86 // Disconnect should be called when a client is disconnected 87 func (node *ClientNode) Disconnect() { 88 node.cm.disconnect(node) 89 } 90 91 // update recalculates the buffer value at a specified time while also performing 92 // scheduled flow control parameter updates if necessary 93 func (node *ClientNode) update(now mclock.AbsTime) { 94 for len(node.updateSchedule) > 0 && node.updateSchedule[0].time <= now { 95 node.recalcBV(node.updateSchedule[0].time) 96 node.updateParams(node.updateSchedule[0].params, now) 97 node.updateSchedule = node.updateSchedule[1:] 98 } 99 node.recalcBV(now) 100 } 101 102 // recalcBV recalculates the buffer value at a specified time 103 func (node *ClientNode) recalcBV(now mclock.AbsTime) { 104 dt := uint64(now - node.lastTime) 105 if now < node.lastTime { 106 dt = 0 107 } 108 node.bufValue += node.params.MinRecharge * dt / uint64(fcTimeConst) 109 if node.bufValue > node.params.BufLimit { 110 node.bufValue = node.params.BufLimit 111 } 112 if node.log != nil { 113 node.log.add(now, fmt.Sprintf("updated bv=%d MRR=%d BufLimit=%d", node.bufValue, node.params.MinRecharge, node.params.BufLimit)) 114 } 115 node.lastTime = now 116 } 117 118 // UpdateParams updates the flow control parameters of a client node 119 func (node *ClientNode) UpdateParams(params ServerParams) { 120 node.lock.Lock() 121 defer node.lock.Unlock() 122 123 now := node.cm.clock.Now() 124 node.update(now) 125 if params.MinRecharge >= node.params.MinRecharge { 126 node.updateSchedule = nil 127 node.updateParams(params, now) 128 } else { 129 for i, s := range node.updateSchedule { 130 if params.MinRecharge >= s.params.MinRecharge { 131 s.params = params 132 node.updateSchedule = node.updateSchedule[:i+1] 133 return 134 } 135 } 136 node.updateSchedule = append(node.updateSchedule, scheduledUpdate{time: now + mclock.AbsTime(DecParamDelay), params: params}) 137 } 138 } 139 140 // updateParams updates the flow control parameters of the node 141 func (node *ClientNode) updateParams(params ServerParams, now mclock.AbsTime) { 142 diff := params.BufLimit - node.params.BufLimit 143 if int64(diff) > 0 { 144 node.bufValue += diff 145 } else if node.bufValue > params.BufLimit { 146 node.bufValue = params.BufLimit 147 } 148 node.cm.updateParams(node, params, now) 149 } 150 151 // AcceptRequest returns whether a new request can be accepted and the missing 152 // buffer amount if it was rejected due to a buffer underrun. If accepted, maxCost 153 // is deducted from the flow control buffer. 154 func (node *ClientNode) AcceptRequest(reqID, index, maxCost uint64) (accepted bool, bufShort uint64, priority int64) { 155 node.lock.Lock() 156 defer node.lock.Unlock() 157 158 now := node.cm.clock.Now() 159 node.update(now) 160 if maxCost > node.bufValue { 161 if node.log != nil { 162 node.log.add(now, fmt.Sprintf("rejected reqID=%d bv=%d maxCost=%d", reqID, node.bufValue, maxCost)) 163 node.log.dump(now) 164 } 165 return false, maxCost - node.bufValue, 0 166 } 167 node.bufValue -= maxCost 168 node.sumCost += maxCost 169 if node.log != nil { 170 node.log.add(now, fmt.Sprintf("accepted reqID=%d bv=%d maxCost=%d sumCost=%d", reqID, node.bufValue, maxCost, node.sumCost)) 171 } 172 node.accepted[index] = node.sumCost 173 return true, 0, node.cm.accepted(node, maxCost, now) 174 } 175 176 // RequestProcessed should be called when the request has been processed 177 func (node *ClientNode) RequestProcessed(reqID, index, maxCost, realCost uint64) (bv uint64) { 178 node.lock.Lock() 179 defer node.lock.Unlock() 180 181 now := node.cm.clock.Now() 182 node.update(now) 183 node.cm.processed(node, maxCost, realCost, now) 184 bv = node.bufValue + node.sumCost - node.accepted[index] 185 if node.log != nil { 186 node.log.add(now, fmt.Sprintf("processed reqID=%d bv=%d maxCost=%d realCost=%d sumCost=%d oldSumCost=%d reportedBV=%d", reqID, node.bufValue, maxCost, realCost, node.sumCost, node.accepted[index], bv)) 187 } 188 delete(node.accepted, index) 189 return 190 } 191 192 // ServerNode is the flow control system's representation of a server 193 // (used in client mode only) 194 type ServerNode struct { 195 clock mclock.Clock 196 bufEstimate uint64 197 bufRecharge bool 198 lastTime mclock.AbsTime 199 params ServerParams 200 sumCost uint64 // sum of req costs sent to this server 201 pending map[uint64]uint64 // value = sumCost after sending the given req 202 log *logger 203 lock sync.RWMutex 204 } 205 206 // NewServerNode returns a new ServerNode 207 func NewServerNode(params ServerParams, clock mclock.Clock) *ServerNode { 208 node := &ServerNode{ 209 clock: clock, 210 bufEstimate: params.BufLimit, 211 bufRecharge: false, 212 lastTime: clock.Now(), 213 params: params, 214 pending: make(map[uint64]uint64), 215 } 216 if keepLogs > 0 { 217 node.log = newLogger(keepLogs) 218 } 219 return node 220 } 221 222 // UpdateParams updates the flow control parameters of the node 223 func (node *ServerNode) UpdateParams(params ServerParams) { 224 node.lock.Lock() 225 defer node.lock.Unlock() 226 227 node.recalcBLE(mclock.Now()) 228 if params.BufLimit > node.params.BufLimit { 229 node.bufEstimate += params.BufLimit - node.params.BufLimit 230 } else { 231 if node.bufEstimate > params.BufLimit { 232 node.bufEstimate = params.BufLimit 233 } 234 } 235 node.params = params 236 } 237 238 // recalcBLE recalculates the lowest estimate for the client's buffer value at 239 // the given server at the specified time 240 func (node *ServerNode) recalcBLE(now mclock.AbsTime) { 241 if now < node.lastTime { 242 return 243 } 244 if node.bufRecharge { 245 dt := uint64(now - node.lastTime) 246 node.bufEstimate += node.params.MinRecharge * dt / uint64(fcTimeConst) 247 if node.bufEstimate >= node.params.BufLimit { 248 node.bufEstimate = node.params.BufLimit 249 node.bufRecharge = false 250 } 251 } 252 node.lastTime = now 253 if node.log != nil { 254 node.log.add(now, fmt.Sprintf("updated bufEst=%d MRR=%d BufLimit=%d", node.bufEstimate, node.params.MinRecharge, node.params.BufLimit)) 255 } 256 } 257 258 // safetyMargin is added to the flow control waiting time when estimated buffer value is low 259 const safetyMargin = time.Millisecond 260 261 // CanSend returns the minimum waiting time required before sending a request 262 // with the given maximum estimated cost. Second return value is the relative 263 // estimated buffer level after sending the request (divided by BufLimit). 264 func (node *ServerNode) CanSend(maxCost uint64) (time.Duration, float64) { 265 node.lock.RLock() 266 defer node.lock.RUnlock() 267 268 now := node.clock.Now() 269 node.recalcBLE(now) 270 maxCost += uint64(safetyMargin) * node.params.MinRecharge / uint64(fcTimeConst) 271 if maxCost > node.params.BufLimit { 272 maxCost = node.params.BufLimit 273 } 274 if node.bufEstimate >= maxCost { 275 relBuf := float64(node.bufEstimate-maxCost) / float64(node.params.BufLimit) 276 if node.log != nil { 277 node.log.add(now, fmt.Sprintf("canSend bufEst=%d maxCost=%d true relBuf=%f", node.bufEstimate, maxCost, relBuf)) 278 } 279 return 0, relBuf 280 } 281 timeLeft := time.Duration((maxCost - node.bufEstimate) * uint64(fcTimeConst) / node.params.MinRecharge) 282 if node.log != nil { 283 node.log.add(now, fmt.Sprintf("canSend bufEst=%d maxCost=%d false timeLeft=%v", node.bufEstimate, maxCost, timeLeft)) 284 } 285 return timeLeft, 0 286 } 287 288 // QueuedRequest should be called when the request has been assigned to the given 289 // server node, before putting it in the send queue. It is mandatory that requests 290 // are sent in the same order as the QueuedRequest calls are made. 291 func (node *ServerNode) QueuedRequest(reqID, maxCost uint64) { 292 node.lock.Lock() 293 defer node.lock.Unlock() 294 295 now := node.clock.Now() 296 node.recalcBLE(now) 297 // Note: we do not know when requests actually arrive to the server so bufRecharge 298 // is not turned on here if buffer was full; in this case it is going to be turned 299 // on by the first reply's bufValue feedback 300 if node.bufEstimate >= maxCost { 301 node.bufEstimate -= maxCost 302 } else { 303 log.Error("Queued request with insufficient buffer estimate") 304 node.bufEstimate = 0 305 } 306 node.sumCost += maxCost 307 node.pending[reqID] = node.sumCost 308 if node.log != nil { 309 node.log.add(now, fmt.Sprintf("queued reqID=%d bufEst=%d maxCost=%d sumCost=%d", reqID, node.bufEstimate, maxCost, node.sumCost)) 310 } 311 } 312 313 // ReceivedReply adjusts estimated buffer value according to the value included in 314 // the latest request reply. 315 func (node *ServerNode) ReceivedReply(reqID, bv uint64) { 316 node.lock.Lock() 317 defer node.lock.Unlock() 318 319 now := node.clock.Now() 320 node.recalcBLE(now) 321 if bv > node.params.BufLimit { 322 bv = node.params.BufLimit 323 } 324 sc, ok := node.pending[reqID] 325 if !ok { 326 return 327 } 328 delete(node.pending, reqID) 329 cc := node.sumCost - sc 330 newEstimate := uint64(0) 331 if bv > cc { 332 newEstimate = bv - cc 333 } 334 if newEstimate > node.bufEstimate { 335 // Note: we never reduce the buffer estimate based on the reported value because 336 // this can only happen because of the delayed delivery of the latest reply. 337 // The lowest estimate based on the previous reply can still be considered valid. 338 node.bufEstimate = newEstimate 339 } 340 341 node.bufRecharge = node.bufEstimate < node.params.BufLimit 342 node.lastTime = now 343 if node.log != nil { 344 node.log.add(now, fmt.Sprintf("received reqID=%d bufEst=%d reportedBv=%d sumCost=%d oldSumCost=%d", reqID, node.bufEstimate, bv, node.sumCost, sc)) 345 } 346 } 347 348 // DumpLogs dumps the event log if logging is used 349 func (node *ServerNode) DumpLogs() { 350 node.lock.Lock() 351 defer node.lock.Unlock() 352 353 if node.log != nil { 354 node.log.dump(node.clock.Now()) 355 } 356 }