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