|
|
@@ -0,0 +1,195 @@
|
|
|
+package main
|
|
|
+
|
|
|
+import (
|
|
|
+ "math"
|
|
|
+ "sync"
|
|
|
+ "time"
|
|
|
+
|
|
|
+ "github.com/coreos/go-raft"
|
|
|
+)
|
|
|
+
|
|
|
+const (
|
|
|
+ queueCapacity = 200
|
|
|
+)
|
|
|
+
|
|
|
+// packageStats represent the stats we need for a package.
|
|
|
+// It has sending time and the size of the package.
|
|
|
+type packageStats struct {
|
|
|
+ sendingTime time.Time
|
|
|
+ size int
|
|
|
+}
|
|
|
+
|
|
|
+// NewPackageStats creates a pacakgeStats and return the pointer to it.
|
|
|
+func NewPackageStats(now time.Time, size int) *packageStats {
|
|
|
+ return &packageStats{
|
|
|
+ sendingTime: now,
|
|
|
+ size: size,
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// Time return the sending time of the package.
|
|
|
+func (ps *packageStats) Time() time.Time {
|
|
|
+ return ps.sendingTime
|
|
|
+}
|
|
|
+
|
|
|
+type raftServerStats struct {
|
|
|
+ State string `json:"state"`
|
|
|
+ StartTime time.Time `json:"startTime"`
|
|
|
+ Leader string `json:"leader"`
|
|
|
+ LeaderUptime string `json:"leaderUptime"`
|
|
|
+
|
|
|
+ RecvAppendRequestCnt uint64 `json:"recvAppendRequestCnt,"`
|
|
|
+ RecvingPkgRate float64 `json:"recvPkgRate,omitempty"`
|
|
|
+ RecvingBandwidthRate float64 `json:"recvBandwidthRate,omitempty"`
|
|
|
+
|
|
|
+ SendAppendRequestCnt uint64 `json:"sendAppendRequestCnt"`
|
|
|
+ SendingPkgRate float64 `json:"sendPkgRate,omitempty"`
|
|
|
+ SendingBandwidthRate float64 `json:"sendBandwidthRate,omitempty"`
|
|
|
+
|
|
|
+ leaderStartTime time.Time
|
|
|
+ sendRateQueue *statsQueue
|
|
|
+ recvRateQueue *statsQueue
|
|
|
+}
|
|
|
+
|
|
|
+func (ss *raftServerStats) RecvAppendReq(leaderName string, pkgSize int) {
|
|
|
+ ss.State = raft.Follower
|
|
|
+ if leaderName != ss.Leader {
|
|
|
+ ss.Leader = leaderName
|
|
|
+ ss.leaderStartTime = time.Now()
|
|
|
+ }
|
|
|
+
|
|
|
+ ss.recvRateQueue.Insert(NewPackageStats(time.Now(), pkgSize))
|
|
|
+ ss.RecvAppendRequestCnt++
|
|
|
+}
|
|
|
+
|
|
|
+func (ss *raftServerStats) SendAppendReq(pkgSize int) {
|
|
|
+ now := time.Now()
|
|
|
+ if ss.State != raft.Leader {
|
|
|
+ ss.State = raft.Leader
|
|
|
+ ss.Leader = r.Name()
|
|
|
+ ss.leaderStartTime = now
|
|
|
+ }
|
|
|
+
|
|
|
+ ss.sendRateQueue.Insert(NewPackageStats(time.Now(), pkgSize))
|
|
|
+
|
|
|
+ ss.SendAppendRequestCnt++
|
|
|
+}
|
|
|
+
|
|
|
+type raftPeerStats struct {
|
|
|
+ Latency float64 `json:"latency"`
|
|
|
+ AvgLatency float64 `json:"averageLatency"`
|
|
|
+ avgLatencySquare float64
|
|
|
+ SdvLatency float64 `json:"sdvLatency"`
|
|
|
+ MinLatency float64 `json:"minLatency"`
|
|
|
+ MaxLatency float64 `json:"maxLatency"`
|
|
|
+ FailCnt uint64 `json:"failsCount"`
|
|
|
+ SuccCnt uint64 `json:"successCount"`
|
|
|
+}
|
|
|
+
|
|
|
+// Succ function update the raftPeerStats with a successful send
|
|
|
+func (ps *raftPeerStats) Succ(d time.Duration) {
|
|
|
+ total := float64(ps.SuccCnt) * ps.AvgLatency
|
|
|
+ totalSquare := float64(ps.SuccCnt) * ps.avgLatencySquare
|
|
|
+
|
|
|
+ ps.SuccCnt++
|
|
|
+
|
|
|
+ ps.Latency = float64(d) / (1000000.0)
|
|
|
+
|
|
|
+ if ps.Latency > ps.MaxLatency {
|
|
|
+ ps.MaxLatency = ps.Latency
|
|
|
+ }
|
|
|
+
|
|
|
+ if ps.Latency < ps.MinLatency {
|
|
|
+ ps.MinLatency = ps.Latency
|
|
|
+ }
|
|
|
+
|
|
|
+ ps.AvgLatency = (total + ps.Latency) / float64(ps.SuccCnt)
|
|
|
+ ps.avgLatencySquare = (totalSquare + ps.Latency*ps.Latency) / float64(ps.SuccCnt)
|
|
|
+
|
|
|
+ // sdv = sqrt(avg(x^2) - avg(x)^2)
|
|
|
+ ps.SdvLatency = math.Sqrt(ps.avgLatencySquare - ps.AvgLatency*ps.AvgLatency)
|
|
|
+}
|
|
|
+
|
|
|
+// Fail function update the raftPeerStats with a unsuccessful send
|
|
|
+func (ps *raftPeerStats) Fail() {
|
|
|
+ ps.FailCnt++
|
|
|
+}
|
|
|
+
|
|
|
+type statsQueue struct {
|
|
|
+ items [queueCapacity]*packageStats
|
|
|
+ size int
|
|
|
+ front int
|
|
|
+ back int
|
|
|
+ totalPkgSize int
|
|
|
+ rwl sync.RWMutex
|
|
|
+}
|
|
|
+
|
|
|
+func (q *statsQueue) Len() int {
|
|
|
+ return q.size
|
|
|
+}
|
|
|
+
|
|
|
+func (q *statsQueue) PkgSize() int {
|
|
|
+ return q.totalPkgSize
|
|
|
+}
|
|
|
+
|
|
|
+// FrontAndBack gets the front and back elements in the queue
|
|
|
+// We must grab front and back together with the protection of the lock
|
|
|
+func (q *statsQueue) frontAndBack() (*packageStats, *packageStats) {
|
|
|
+ q.rwl.RLock()
|
|
|
+ defer q.rwl.RUnlock()
|
|
|
+ if q.size != 0 {
|
|
|
+ return q.items[q.front], q.items[q.back]
|
|
|
+ }
|
|
|
+ return nil, nil
|
|
|
+}
|
|
|
+
|
|
|
+// Insert function insert a packageStats into the queue and update the records
|
|
|
+func (q *statsQueue) Insert(p *packageStats) {
|
|
|
+ q.rwl.Lock()
|
|
|
+ defer q.rwl.Unlock()
|
|
|
+
|
|
|
+ q.back = (q.back + 1) % queueCapacity
|
|
|
+
|
|
|
+ if q.size == queueCapacity { //dequeue
|
|
|
+ q.totalPkgSize -= q.items[q.front].size
|
|
|
+ q.front = (q.back + 1) % queueCapacity
|
|
|
+ } else {
|
|
|
+ q.size++
|
|
|
+ }
|
|
|
+
|
|
|
+ q.items[q.back] = p
|
|
|
+ q.totalPkgSize += q.items[q.back].size
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+// Rate function returns the package rate and byte rate
|
|
|
+func (q *statsQueue) Rate() (float64, float64) {
|
|
|
+ front, back := q.frontAndBack()
|
|
|
+
|
|
|
+ if front == nil || back == nil {
|
|
|
+ return 0, 0
|
|
|
+ }
|
|
|
+
|
|
|
+ if time.Now().Sub(back.Time()) > time.Second {
|
|
|
+ q.Clear()
|
|
|
+ return 0, 0
|
|
|
+ }
|
|
|
+
|
|
|
+ sampleDuration := back.Time().Sub(front.Time())
|
|
|
+
|
|
|
+ pr := float64(q.Len()) / float64(sampleDuration) * float64(time.Second)
|
|
|
+
|
|
|
+ br := float64(q.PkgSize()) / float64(sampleDuration) * float64(time.Second)
|
|
|
+
|
|
|
+ return pr, br
|
|
|
+}
|
|
|
+
|
|
|
+// Clear function clear up the statsQueue
|
|
|
+func (q *statsQueue) Clear() {
|
|
|
+ q.rwl.Lock()
|
|
|
+ defer q.rwl.Unlock()
|
|
|
+ q.back = -1
|
|
|
+ q.front = 0
|
|
|
+ q.size = 0
|
|
|
+ q.totalPkgSize = 0
|
|
|
+}
|