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