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  }