1// Copyright 2015 The etcd Authors 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package v2stats 16 17import ( 18 "encoding/json" 19 "math" 20 "sync" 21 "time" 22 23 "go.uber.org/zap" 24) 25 26// LeaderStats is used by the leader in an etcd cluster, and encapsulates 27// statistics about communication with its followers 28type LeaderStats struct { 29 lg *zap.Logger 30 leaderStats 31 sync.Mutex 32} 33 34type leaderStats struct { 35 // Leader is the ID of the leader in the etcd cluster. 36 // TODO(jonboulle): clarify that these are IDs, not names 37 Leader string `json:"leader"` 38 Followers map[string]*FollowerStats `json:"followers"` 39} 40 41// NewLeaderStats generates a new LeaderStats with the given id as leader 42func NewLeaderStats(lg *zap.Logger, id string) *LeaderStats { 43 if lg == nil { 44 lg = zap.NewNop() 45 } 46 return &LeaderStats{ 47 lg: lg, 48 leaderStats: leaderStats{ 49 Leader: id, 50 Followers: make(map[string]*FollowerStats), 51 }, 52 } 53} 54 55func (ls *LeaderStats) JSON() []byte { 56 ls.Lock() 57 stats := ls.leaderStats 58 ls.Unlock() 59 b, err := json.Marshal(stats) 60 // TODO(jonboulle): appropriate error handling? 61 if err != nil { 62 ls.lg.Error("failed to marshal leader stats", zap.Error(err)) 63 } 64 return b 65} 66 67func (ls *LeaderStats) Follower(name string) *FollowerStats { 68 ls.Lock() 69 defer ls.Unlock() 70 fs, ok := ls.Followers[name] 71 if !ok { 72 fs = &FollowerStats{} 73 fs.Latency.Minimum = 1 << 63 74 ls.Followers[name] = fs 75 } 76 return fs 77} 78 79// FollowerStats encapsulates various statistics about a follower in an etcd cluster 80type FollowerStats struct { 81 Latency LatencyStats `json:"latency"` 82 Counts CountsStats `json:"counts"` 83 84 sync.Mutex 85} 86 87// LatencyStats encapsulates latency statistics. 88type LatencyStats 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} 96 97// CountsStats encapsulates raft statistics. 98type CountsStats struct { 99 Fail uint64 `json:"fail"` 100 Success uint64 `json:"success"` 101} 102 103// Succ updates the FollowerStats with a successful send 104func (fs *FollowerStats) Succ(d time.Duration) { 105 fs.Lock() 106 defer fs.Unlock() 107 108 total := float64(fs.Counts.Success) * fs.Latency.Average 109 totalSquare := float64(fs.Counts.Success) * fs.Latency.averageSquare 110 111 fs.Counts.Success++ 112 113 fs.Latency.Current = float64(d) / (1000000.0) 114 115 if fs.Latency.Current > fs.Latency.Maximum { 116 fs.Latency.Maximum = fs.Latency.Current 117 } 118 119 if fs.Latency.Current < fs.Latency.Minimum { 120 fs.Latency.Minimum = fs.Latency.Current 121 } 122 123 fs.Latency.Average = (total + fs.Latency.Current) / float64(fs.Counts.Success) 124 fs.Latency.averageSquare = (totalSquare + fs.Latency.Current*fs.Latency.Current) / float64(fs.Counts.Success) 125 126 // sdv = sqrt(avg(x^2) - avg(x)^2) 127 fs.Latency.StandardDeviation = math.Sqrt(fs.Latency.averageSquare - fs.Latency.Average*fs.Latency.Average) 128} 129 130// Fail updates the FollowerStats with an unsuccessful send 131func (fs *FollowerStats) Fail() { 132 fs.Lock() 133 defer fs.Unlock() 134 fs.Counts.Fail++ 135} 136