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  }