github.com/kafkaliu/etcd@v0.1.2-0.20131007164923-44c16dd30d69/raft_stats.go (about)

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