github.com/supragya/TendermintConnector@v0.0.0-20210619045051-113e32b84fb1/_deprecated_chains/cosmos/libs/flowrate/flowrate.go (about)

     1  //
     2  // Written by Maxim Khitrov (November 2012)
     3  //
     4  
     5  // Package flowrate provides the tools for monitoring and limiting the flow rate
     6  // of an arbitrary data stream.
     7  package flowrate
     8  
     9  import (
    10  	"math"
    11  	"sync"
    12  	"time"
    13  )
    14  
    15  // Monitor monitors and limits the transfer rate of a data stream.
    16  type Monitor struct {
    17  	mu      sync.Mutex    // Mutex guarding access to all internal fields
    18  	active  bool          // Flag indicating an active transfer
    19  	start   time.Duration // Transfer start time (clock() value)
    20  	bytes   int64         // Total number of bytes transferred
    21  	samples int64         // Total number of samples taken
    22  
    23  	rSample float64 // Most recent transfer rate sample (bytes per second)
    24  	rEMA    float64 // Exponential moving average of rSample
    25  	rPeak   float64 // Peak transfer rate (max of all rSamples)
    26  	rWindow float64 // rEMA window (seconds)
    27  
    28  	sBytes int64         // Number of bytes transferred since sLast
    29  	sLast  time.Duration // Most recent sample time (stop time when inactive)
    30  	sRate  time.Duration // Sampling rate
    31  
    32  	tBytes int64         // Number of bytes expected in the current transfer
    33  	tLast  time.Duration // Time of the most recent transfer of at least 1 byte
    34  }
    35  
    36  // New creates a new flow control monitor. Instantaneous transfer rate is
    37  // measured and updated for each sampleRate interval. windowSize determines the
    38  // weight of each sample in the exponential moving average (EMA) calculation.
    39  // The exact formulas are:
    40  //
    41  // 	sampleTime = currentTime - prevSampleTime
    42  // 	sampleRate = byteCount / sampleTime
    43  // 	weight     = 1 - exp(-sampleTime/windowSize)
    44  // 	newRate    = weight*sampleRate + (1-weight)*oldRate
    45  //
    46  // The default values for sampleRate and windowSize (if <= 0) are 100ms and 1s,
    47  // respectively.
    48  func New(sampleRate, windowSize time.Duration) *Monitor {
    49  	if sampleRate = clockRound(sampleRate); sampleRate <= 0 {
    50  		sampleRate = 5 * clockRate
    51  	}
    52  	if windowSize <= 0 {
    53  		windowSize = 1 * time.Second
    54  	}
    55  	now := clock()
    56  	return &Monitor{
    57  		active:  true,
    58  		start:   now,
    59  		rWindow: windowSize.Seconds(),
    60  		sLast:   now,
    61  		sRate:   sampleRate,
    62  		tLast:   now,
    63  	}
    64  }
    65  
    66  // Update records the transfer of n bytes and returns n. It should be called
    67  // after each Read/Write operation, even if n is 0.
    68  func (m *Monitor) Update(n int) int {
    69  	m.mu.Lock()
    70  	m.update(n)
    71  	m.mu.Unlock()
    72  	return n
    73  }
    74  
    75  // Hack to set the current rEMA.
    76  func (m *Monitor) SetREMA(rEMA float64) {
    77  	m.mu.Lock()
    78  	m.rEMA = rEMA
    79  	m.samples++
    80  	m.mu.Unlock()
    81  }
    82  
    83  // IO is a convenience method intended to wrap io.Reader and io.Writer method
    84  // execution. It calls m.Update(n) and then returns (n, err) unmodified.
    85  func (m *Monitor) IO(n int, err error) (int, error) {
    86  	return m.Update(n), err
    87  }
    88  
    89  // Done marks the transfer as finished and prevents any further updates or
    90  // limiting. Instantaneous and current transfer rates drop to 0. Update, IO, and
    91  // Limit methods become NOOPs. It returns the total number of bytes transferred.
    92  func (m *Monitor) Done() int64 {
    93  	m.mu.Lock()
    94  	if now := m.update(0); m.sBytes > 0 {
    95  		m.reset(now)
    96  	}
    97  	m.active = false
    98  	m.tLast = 0
    99  	n := m.bytes
   100  	m.mu.Unlock()
   101  	return n
   102  }
   103  
   104  // timeRemLimit is the maximum Status.TimeRem value.
   105  const timeRemLimit = 999*time.Hour + 59*time.Minute + 59*time.Second
   106  
   107  // Status represents the current Monitor status. All transfer rates are in bytes
   108  // per second rounded to the nearest byte.
   109  type Status struct {
   110  	Active   bool          // Flag indicating an active transfer
   111  	Start    time.Time     // Transfer start time
   112  	Duration time.Duration // Time period covered by the statistics
   113  	Idle     time.Duration // Time since the last transfer of at least 1 byte
   114  	Bytes    int64         // Total number of bytes transferred
   115  	Samples  int64         // Total number of samples taken
   116  	InstRate int64         // Instantaneous transfer rate
   117  	CurRate  int64         // Current transfer rate (EMA of InstRate)
   118  	AvgRate  int64         // Average transfer rate (Bytes / Duration)
   119  	PeakRate int64         // Maximum instantaneous transfer rate
   120  	BytesRem int64         // Number of bytes remaining in the transfer
   121  	TimeRem  time.Duration // Estimated time to completion
   122  	Progress Percent       // Overall transfer progress
   123  }
   124  
   125  // Status returns current transfer status information. The returned value
   126  // becomes static after a call to Done.
   127  func (m *Monitor) Status() Status {
   128  	m.mu.Lock()
   129  	now := m.update(0)
   130  	s := Status{
   131  		Active:   m.active,
   132  		Start:    clockToTime(m.start),
   133  		Duration: m.sLast - m.start,
   134  		Idle:     now - m.tLast,
   135  		Bytes:    m.bytes,
   136  		Samples:  m.samples,
   137  		PeakRate: round(m.rPeak),
   138  		BytesRem: m.tBytes - m.bytes,
   139  		Progress: percentOf(float64(m.bytes), float64(m.tBytes)),
   140  	}
   141  	if s.BytesRem < 0 {
   142  		s.BytesRem = 0
   143  	}
   144  	if s.Duration > 0 {
   145  		rAvg := float64(s.Bytes) / s.Duration.Seconds()
   146  		s.AvgRate = round(rAvg)
   147  		if s.Active {
   148  			s.InstRate = round(m.rSample)
   149  			s.CurRate = round(m.rEMA)
   150  			if s.BytesRem > 0 {
   151  				if tRate := 0.8*m.rEMA + 0.2*rAvg; tRate > 0 {
   152  					ns := float64(s.BytesRem) / tRate * 1e9
   153  					if ns > float64(timeRemLimit) {
   154  						ns = float64(timeRemLimit)
   155  					}
   156  					s.TimeRem = clockRound(time.Duration(ns))
   157  				}
   158  			}
   159  		}
   160  	}
   161  	m.mu.Unlock()
   162  	return s
   163  }
   164  
   165  // Limit restricts the instantaneous (per-sample) data flow to rate bytes per
   166  // second. It returns the maximum number of bytes (0 <= n <= want) that may be
   167  // transferred immediately without exceeding the limit. If block == true, the
   168  // call blocks until n > 0. want is returned unmodified if want < 1, rate < 1,
   169  // or the transfer is inactive (after a call to Done).
   170  //
   171  // At least one byte is always allowed to be transferred in any given sampling
   172  // period. Thus, if the sampling rate is 100ms, the lowest achievable flow rate
   173  // is 10 bytes per second.
   174  //
   175  // For usage examples, see the implementation of Reader and Writer in io.go.
   176  func (m *Monitor) Limit(want int, rate int64, block bool) (n int) {
   177  	if want < 1 || rate < 1 {
   178  		return want
   179  	}
   180  	m.mu.Lock()
   181  
   182  	// Determine the maximum number of bytes that can be sent in one sample
   183  	limit := round(float64(rate) * m.sRate.Seconds())
   184  	if limit <= 0 {
   185  		limit = 1
   186  	}
   187  
   188  	// If block == true, wait until m.sBytes < limit
   189  	if now := m.update(0); block {
   190  		for m.sBytes >= limit && m.active {
   191  			now = m.waitNextSample(now)
   192  		}
   193  	}
   194  
   195  	// Make limit <= want (unlimited if the transfer is no longer active)
   196  	if limit -= m.sBytes; limit > int64(want) || !m.active {
   197  		limit = int64(want)
   198  	}
   199  	m.mu.Unlock()
   200  
   201  	if limit < 0 {
   202  		limit = 0
   203  	}
   204  	return int(limit)
   205  }
   206  
   207  // SetTransferSize specifies the total size of the data transfer, which allows
   208  // the Monitor to calculate the overall progress and time to completion.
   209  func (m *Monitor) SetTransferSize(bytes int64) {
   210  	if bytes < 0 {
   211  		bytes = 0
   212  	}
   213  	m.mu.Lock()
   214  	m.tBytes = bytes
   215  	m.mu.Unlock()
   216  }
   217  
   218  // update accumulates the transferred byte count for the current sample until
   219  // clock() - m.sLast >= m.sRate. The monitor status is updated once the current
   220  // sample is done.
   221  func (m *Monitor) update(n int) (now time.Duration) {
   222  	if !m.active {
   223  		return
   224  	}
   225  	if now = clock(); n > 0 {
   226  		m.tLast = now
   227  	}
   228  	m.sBytes += int64(n)
   229  	if sTime := now - m.sLast; sTime >= m.sRate {
   230  		t := sTime.Seconds()
   231  		if m.rSample = float64(m.sBytes) / t; m.rSample > m.rPeak {
   232  			m.rPeak = m.rSample
   233  		}
   234  
   235  		// Exponential moving average using a method similar to *nix load
   236  		// average calculation. Longer sampling periods carry greater weight.
   237  		if m.samples > 0 {
   238  			w := math.Exp(-t / m.rWindow)
   239  			m.rEMA = m.rSample + w*(m.rEMA-m.rSample)
   240  		} else {
   241  			m.rEMA = m.rSample
   242  		}
   243  		m.reset(now)
   244  	}
   245  	return
   246  }
   247  
   248  // reset clears the current sample state in preparation for the next sample.
   249  func (m *Monitor) reset(sampleTime time.Duration) {
   250  	m.bytes += m.sBytes
   251  	m.samples++
   252  	m.sBytes = 0
   253  	m.sLast = sampleTime
   254  }
   255  
   256  // waitNextSample sleeps for the remainder of the current sample. The lock is
   257  // released and reacquired during the actual sleep period, so it's possible for
   258  // the transfer to be inactive when this method returns.
   259  func (m *Monitor) waitNextSample(now time.Duration) time.Duration {
   260  	const minWait = 5 * time.Millisecond
   261  	current := m.sLast
   262  
   263  	// sleep until the last sample time changes (ideally, just one iteration)
   264  	for m.sLast == current && m.active {
   265  		d := current + m.sRate - now
   266  		m.mu.Unlock()
   267  		if d < minWait {
   268  			d = minWait
   269  		}
   270  		time.Sleep(d)
   271  		m.mu.Lock()
   272  		now = m.update(0)
   273  	}
   274  	return now
   275  }