github.com/arieschain/arieschain@v0.0.0-20191023063405-37c074544356/les/flowcontrol/control.go (about)

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