github.com/susy-go/susy-graviton@v0.0.0-20190614130430-36cddae42305/les/flowcontrol/control.go (about)

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