github.com/qorio/etcd@v0.1.2-0.20131003183127-5cc585af9618/raft_stats.go (about)

     1  package main
     2  
     3  import (
     4  	"math"
     5  	"sync"
     6  	"time"
     7  
     8  	"github.com/coreos/go-raft"
     9  )
    10  
    11  const (
    12  	queueCapacity = 200
    13  )
    14  
    15  // packageStats represent the stats we need for a package.
    16  // It has sending time and the size of the package.
    17  type packageStats struct {
    18  	sendingTime time.Time
    19  	size        int
    20  }
    21  
    22  // NewPackageStats creates a pacakgeStats and return the pointer to it.
    23  func NewPackageStats(now time.Time, size int) *packageStats {
    24  	return &packageStats{
    25  		sendingTime: now,
    26  		size:        size,
    27  	}
    28  }
    29  
    30  // Time return the sending time of the package.
    31  func (ps *packageStats) Time() time.Time {
    32  	return ps.sendingTime
    33  }
    34  
    35  type raftServerStats struct {
    36  	State     string    `json:"state"`
    37  	StartTime time.Time `json:"startTime"`
    38  
    39  	LeaderInfo struct {
    40  		Name      string `json:"leader"`
    41  		Uptime    string `json:"uptime"`
    42  		startTime time.Time
    43  	} `json:"leaderInfo"`
    44  
    45  	RecvAppendRequestCnt uint64  `json:"recvAppendRequestCnt,"`
    46  	RecvingPkgRate       float64 `json:"recvPkgRate,omitempty"`
    47  	RecvingBandwidthRate float64 `json:"recvBandwidthRate,omitempty"`
    48  
    49  	SendAppendRequestCnt uint64  `json:"sendAppendRequestCnt"`
    50  	SendingPkgRate       float64 `json:"sendPkgRate,omitempty"`
    51  	SendingBandwidthRate float64 `json:"sendBandwidthRate,omitempty"`
    52  
    53  	sendRateQueue *statsQueue
    54  	recvRateQueue *statsQueue
    55  }
    56  
    57  func (ss *raftServerStats) RecvAppendReq(leaderName string, pkgSize int) {
    58  	ss.State = raft.Follower
    59  	if leaderName != ss.LeaderInfo.Name {
    60  		ss.LeaderInfo.Name = leaderName
    61  		ss.LeaderInfo.startTime = time.Now()
    62  	}
    63  
    64  	ss.recvRateQueue.Insert(NewPackageStats(time.Now(), pkgSize))
    65  	ss.RecvAppendRequestCnt++
    66  }
    67  
    68  func (ss *raftServerStats) SendAppendReq(pkgSize int) {
    69  	now := time.Now()
    70  
    71  	if ss.State != raft.Leader {
    72  		ss.State = raft.Leader
    73  		ss.LeaderInfo.Name = r.Name()
    74  		ss.LeaderInfo.startTime = now
    75  	}
    76  
    77  	ss.sendRateQueue.Insert(NewPackageStats(now, pkgSize))
    78  
    79  	ss.SendAppendRequestCnt++
    80  }
    81  
    82  type raftFollowersStats struct {
    83  	Leader    string                        `json:"leader"`
    84  	Followers map[string]*raftFollowerStats `json:"followers"`
    85  }
    86  
    87  type raftFollowerStats struct {
    88  	Latency struct {
    89  		Current           float64 `json:"current"`
    90  		Average           float64 `json:"average"`
    91  		averageSquare     float64
    92  		StandardDeviation float64 `json:"standardDeviation"`
    93  		Minimum           float64 `json:"minimum"`
    94  		Maximum           float64 `json:"maximum"`
    95  	} `json:"latency"`
    96  
    97  	Counts struct {
    98  		Fail    uint64 `json:"fail"`
    99  		Success uint64 `json:"success"`
   100  	} `json:"counts"`
   101  }
   102  
   103  // Succ function update the raftFollowerStats with a successful send
   104  func (ps *raftFollowerStats) Succ(d time.Duration) {
   105  	total := float64(ps.Counts.Success) * ps.Latency.Average
   106  	totalSquare := float64(ps.Counts.Success) * ps.Latency.averageSquare
   107  
   108  	ps.Counts.Success++
   109  
   110  	ps.Latency.Current = float64(d) / (1000000.0)
   111  
   112  	if ps.Latency.Current > ps.Latency.Maximum {
   113  		ps.Latency.Maximum = ps.Latency.Current
   114  	}
   115  
   116  	if ps.Latency.Current < ps.Latency.Minimum {
   117  		ps.Latency.Minimum = ps.Latency.Current
   118  	}
   119  
   120  	ps.Latency.Average = (total + ps.Latency.Current) / float64(ps.Counts.Success)
   121  	ps.Latency.averageSquare = (totalSquare + ps.Latency.Current*ps.Latency.Current) / float64(ps.Counts.Success)
   122  
   123  	// sdv = sqrt(avg(x^2) - avg(x)^2)
   124  	ps.Latency.StandardDeviation = math.Sqrt(ps.Latency.averageSquare - ps.Latency.Average*ps.Latency.Average)
   125  }
   126  
   127  // Fail function update the raftFollowerStats with a unsuccessful send
   128  func (ps *raftFollowerStats) Fail() {
   129  	ps.Counts.Fail++
   130  }
   131  
   132  type statsQueue struct {
   133  	items        [queueCapacity]*packageStats
   134  	size         int
   135  	front        int
   136  	back         int
   137  	totalPkgSize int
   138  	rwl          sync.RWMutex
   139  }
   140  
   141  func (q *statsQueue) Len() int {
   142  	return q.size
   143  }
   144  
   145  func (q *statsQueue) PkgSize() int {
   146  	return q.totalPkgSize
   147  }
   148  
   149  // FrontAndBack gets the front and back elements in the queue
   150  // We must grab front and back together with the protection of the lock
   151  func (q *statsQueue) frontAndBack() (*packageStats, *packageStats) {
   152  	q.rwl.RLock()
   153  	defer q.rwl.RUnlock()
   154  	if q.size != 0 {
   155  		return q.items[q.front], q.items[q.back]
   156  	}
   157  	return nil, nil
   158  }
   159  
   160  // Insert function insert a packageStats into the queue and update the records
   161  func (q *statsQueue) Insert(p *packageStats) {
   162  	q.rwl.Lock()
   163  	defer q.rwl.Unlock()
   164  
   165  	q.back = (q.back + 1) % queueCapacity
   166  
   167  	if q.size == queueCapacity { //dequeue
   168  		q.totalPkgSize -= q.items[q.front].size
   169  		q.front = (q.back + 1) % queueCapacity
   170  	} else {
   171  		q.size++
   172  	}
   173  
   174  	q.items[q.back] = p
   175  	q.totalPkgSize += q.items[q.back].size
   176  
   177  }
   178  
   179  // Rate function returns the package rate and byte rate
   180  func (q *statsQueue) Rate() (float64, float64) {
   181  	front, back := q.frontAndBack()
   182  
   183  	if front == nil || back == nil {
   184  		return 0, 0
   185  	}
   186  
   187  	if time.Now().Sub(back.Time()) > time.Second {
   188  		q.Clear()
   189  		return 0, 0
   190  	}
   191  
   192  	sampleDuration := back.Time().Sub(front.Time())
   193  
   194  	pr := float64(q.Len()) / float64(sampleDuration) * float64(time.Second)
   195  
   196  	br := float64(q.PkgSize()) / float64(sampleDuration) * float64(time.Second)
   197  
   198  	return pr, br
   199  }
   200  
   201  // Clear function clear up the statsQueue
   202  func (q *statsQueue) Clear() {
   203  	q.rwl.Lock()
   204  	defer q.rwl.Unlock()
   205  	q.back = -1
   206  	q.front = 0
   207  	q.size = 0
   208  	q.totalPkgSize = 0
   209  }