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	"log"
20	"sync"
21	"time"
22
23	"go.etcd.io/etcd/raft"
24)
25
26// ServerStats encapsulates various statistics about an EtcdServer and its
27// communication with other members of the cluster
28type ServerStats struct {
29	serverStats
30	sync.Mutex
31}
32
33func NewServerStats(name, id string) *ServerStats {
34	ss := &ServerStats{
35		serverStats: serverStats{
36			Name: name,
37			ID:   id,
38		},
39	}
40	now := time.Now()
41	ss.StartTime = now
42	ss.LeaderInfo.StartTime = now
43	ss.sendRateQueue = &statsQueue{back: -1}
44	ss.recvRateQueue = &statsQueue{back: -1}
45	return ss
46}
47
48type serverStats struct {
49	Name string `json:"name"`
50	// ID is the raft ID of the node.
51	// TODO(jonboulle): use ID instead of name?
52	ID        string         `json:"id"`
53	State     raft.StateType `json:"state"`
54	StartTime time.Time      `json:"startTime"`
55
56	LeaderInfo struct {
57		Name      string    `json:"leader"`
58		Uptime    string    `json:"uptime"`
59		StartTime time.Time `json:"startTime"`
60	} `json:"leaderInfo"`
61
62	RecvAppendRequestCnt uint64  `json:"recvAppendRequestCnt,"`
63	RecvingPkgRate       float64 `json:"recvPkgRate,omitempty"`
64	RecvingBandwidthRate float64 `json:"recvBandwidthRate,omitempty"`
65
66	SendAppendRequestCnt uint64  `json:"sendAppendRequestCnt"`
67	SendingPkgRate       float64 `json:"sendPkgRate,omitempty"`
68	SendingBandwidthRate float64 `json:"sendBandwidthRate,omitempty"`
69
70	sendRateQueue *statsQueue
71	recvRateQueue *statsQueue
72}
73
74func (ss *ServerStats) JSON() []byte {
75	ss.Lock()
76	stats := ss.serverStats
77	stats.SendingPkgRate, stats.SendingBandwidthRate = stats.sendRateQueue.Rate()
78	stats.RecvingPkgRate, stats.RecvingBandwidthRate = stats.recvRateQueue.Rate()
79	stats.LeaderInfo.Uptime = time.Since(stats.LeaderInfo.StartTime).String()
80	ss.Unlock()
81	b, err := json.Marshal(stats)
82	// TODO(jonboulle): appropriate error handling?
83	if err != nil {
84		log.Printf("stats: error marshalling server stats: %v", err)
85	}
86	return b
87}
88
89// RecvAppendReq updates the ServerStats in response to an AppendRequest
90// from the given leader being received
91func (ss *ServerStats) RecvAppendReq(leader string, reqSize int) {
92	ss.Lock()
93	defer ss.Unlock()
94
95	now := time.Now()
96
97	ss.State = raft.StateFollower
98	if leader != ss.LeaderInfo.Name {
99		ss.LeaderInfo.Name = leader
100		ss.LeaderInfo.StartTime = now
101	}
102
103	ss.recvRateQueue.Insert(
104		&RequestStats{
105			SendingTime: now,
106			Size:        reqSize,
107		},
108	)
109	ss.RecvAppendRequestCnt++
110}
111
112// SendAppendReq updates the ServerStats in response to an AppendRequest
113// being sent by this server
114func (ss *ServerStats) SendAppendReq(reqSize int) {
115	ss.Lock()
116	defer ss.Unlock()
117
118	ss.becomeLeader()
119
120	ss.sendRateQueue.Insert(
121		&RequestStats{
122			SendingTime: time.Now(),
123			Size:        reqSize,
124		},
125	)
126
127	ss.SendAppendRequestCnt++
128}
129
130func (ss *ServerStats) BecomeLeader() {
131	ss.Lock()
132	defer ss.Unlock()
133	ss.becomeLeader()
134}
135
136func (ss *ServerStats) becomeLeader() {
137	if ss.State != raft.StateLeader {
138		ss.State = raft.StateLeader
139		ss.LeaderInfo.Name = ss.ID
140		ss.LeaderInfo.StartTime = time.Now()
141	}
142}
143