github.com/sberex/go-sberex@v1.8.2-0.20181113200658-ed96ac38f7d7/les/flowcontrol/manager.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 rcConst = 1000000 23 24 type cmNode struct { 25 node *ClientNode 26 lastUpdate mclock.AbsTime 27 serving, recharging bool 28 rcWeight uint64 29 rcValue, rcDelta, startValue int64 30 finishRecharge mclock.AbsTime 31 } 32 33 func (node *cmNode) update(time mclock.AbsTime) { 34 dt := int64(time - node.lastUpdate) 35 node.rcValue += node.rcDelta * dt / rcConst 36 node.lastUpdate = time 37 if node.recharging && time >= node.finishRecharge { 38 node.recharging = false 39 node.rcDelta = 0 40 node.rcValue = 0 41 } 42 } 43 44 func (node *cmNode) set(serving bool, simReqCnt, sumWeight uint64) { 45 if node.serving && !serving { 46 node.recharging = true 47 sumWeight += node.rcWeight 48 } 49 node.serving = serving 50 if node.recharging && serving { 51 node.recharging = false 52 sumWeight -= node.rcWeight 53 } 54 55 node.rcDelta = 0 56 if serving { 57 node.rcDelta = int64(rcConst / simReqCnt) 58 } 59 if node.recharging { 60 node.rcDelta = -int64(node.node.cm.rcRecharge * node.rcWeight / sumWeight) 61 node.finishRecharge = node.lastUpdate + mclock.AbsTime(node.rcValue*rcConst/(-node.rcDelta)) 62 } 63 } 64 65 type ClientManager struct { 66 lock sync.Mutex 67 nodes map[*cmNode]struct{} 68 simReqCnt, sumWeight, rcSumValue uint64 69 maxSimReq, maxRcSum uint64 70 rcRecharge uint64 71 resumeQueue chan chan bool 72 time mclock.AbsTime 73 } 74 75 func NewClientManager(rcTarget, maxSimReq, maxRcSum uint64) *ClientManager { 76 cm := &ClientManager{ 77 nodes: make(map[*cmNode]struct{}), 78 resumeQueue: make(chan chan bool), 79 rcRecharge: rcConst * rcConst / (100*rcConst/rcTarget - rcConst), 80 maxSimReq: maxSimReq, 81 maxRcSum: maxRcSum, 82 } 83 go cm.queueProc() 84 return cm 85 } 86 87 func (self *ClientManager) Stop() { 88 self.lock.Lock() 89 defer self.lock.Unlock() 90 91 // signal any waiting accept routines to return false 92 self.nodes = make(map[*cmNode]struct{}) 93 close(self.resumeQueue) 94 } 95 96 func (self *ClientManager) addNode(cnode *ClientNode) *cmNode { 97 time := mclock.Now() 98 node := &cmNode{ 99 node: cnode, 100 lastUpdate: time, 101 finishRecharge: time, 102 rcWeight: 1, 103 } 104 self.lock.Lock() 105 defer self.lock.Unlock() 106 107 self.nodes[node] = struct{}{} 108 self.update(mclock.Now()) 109 return node 110 } 111 112 func (self *ClientManager) removeNode(node *cmNode) { 113 self.lock.Lock() 114 defer self.lock.Unlock() 115 116 time := mclock.Now() 117 self.stop(node, time) 118 delete(self.nodes, node) 119 self.update(time) 120 } 121 122 // recalc sumWeight 123 func (self *ClientManager) updateNodes(time mclock.AbsTime) (rce bool) { 124 var sumWeight, rcSum uint64 125 for node := range self.nodes { 126 rc := node.recharging 127 node.update(time) 128 if rc && !node.recharging { 129 rce = true 130 } 131 if node.recharging { 132 sumWeight += node.rcWeight 133 } 134 rcSum += uint64(node.rcValue) 135 } 136 self.sumWeight = sumWeight 137 self.rcSumValue = rcSum 138 return 139 } 140 141 func (self *ClientManager) update(time mclock.AbsTime) { 142 for { 143 firstTime := time 144 for node := range self.nodes { 145 if node.recharging && node.finishRecharge < firstTime { 146 firstTime = node.finishRecharge 147 } 148 } 149 if self.updateNodes(firstTime) { 150 for node := range self.nodes { 151 if node.recharging { 152 node.set(node.serving, self.simReqCnt, self.sumWeight) 153 } 154 } 155 } else { 156 self.time = time 157 return 158 } 159 } 160 } 161 162 func (self *ClientManager) canStartReq() bool { 163 return self.simReqCnt < self.maxSimReq && self.rcSumValue < self.maxRcSum 164 } 165 166 func (self *ClientManager) queueProc() { 167 for rc := range self.resumeQueue { 168 for { 169 time.Sleep(time.Millisecond * 10) 170 self.lock.Lock() 171 self.update(mclock.Now()) 172 cs := self.canStartReq() 173 self.lock.Unlock() 174 if cs { 175 break 176 } 177 } 178 close(rc) 179 } 180 } 181 182 func (self *ClientManager) accept(node *cmNode, time mclock.AbsTime) bool { 183 self.lock.Lock() 184 defer self.lock.Unlock() 185 186 self.update(time) 187 if !self.canStartReq() { 188 resume := make(chan bool) 189 self.lock.Unlock() 190 self.resumeQueue <- resume 191 <-resume 192 self.lock.Lock() 193 if _, ok := self.nodes[node]; !ok { 194 return false // reject if node has been removed or manager has been stopped 195 } 196 } 197 self.simReqCnt++ 198 node.set(true, self.simReqCnt, self.sumWeight) 199 node.startValue = node.rcValue 200 self.update(self.time) 201 return true 202 } 203 204 func (self *ClientManager) stop(node *cmNode, time mclock.AbsTime) { 205 if node.serving { 206 self.update(time) 207 self.simReqCnt-- 208 node.set(false, self.simReqCnt, self.sumWeight) 209 self.update(time) 210 } 211 } 212 213 func (self *ClientManager) processed(node *cmNode, time mclock.AbsTime) (rcValue, rcCost uint64) { 214 self.lock.Lock() 215 defer self.lock.Unlock() 216 217 self.stop(node, time) 218 return uint64(node.rcValue), uint64(node.rcValue - node.startValue) 219 }