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 rafthttp
16
17import (
18	"errors"
19	"fmt"
20	"sync"
21	"time"
22
23	"go.etcd.io/etcd/pkg/types"
24
25	"go.uber.org/zap"
26)
27
28type failureType struct {
29	source string
30	action string
31}
32
33type peerStatus struct {
34	lg     *zap.Logger
35	local  types.ID
36	id     types.ID
37	mu     sync.Mutex // protect variables below
38	active bool
39	since  time.Time
40}
41
42func newPeerStatus(lg *zap.Logger, local, id types.ID) *peerStatus {
43	return &peerStatus{lg: lg, local: local, id: id}
44}
45
46func (s *peerStatus) activate() {
47	s.mu.Lock()
48	defer s.mu.Unlock()
49	if !s.active {
50		if s.lg != nil {
51			s.lg.Info("peer became active", zap.String("peer-id", s.id.String()))
52		} else {
53			plog.Infof("peer %s became active", s.id)
54		}
55		s.active = true
56		s.since = time.Now()
57
58		activePeers.WithLabelValues(s.local.String(), s.id.String()).Inc()
59	}
60}
61
62func (s *peerStatus) deactivate(failure failureType, reason string) {
63	s.mu.Lock()
64	defer s.mu.Unlock()
65	msg := fmt.Sprintf("failed to %s %s on %s (%s)", failure.action, s.id, failure.source, reason)
66	if s.active {
67		if s.lg != nil {
68			s.lg.Warn("peer became inactive (message send to peer failed)", zap.String("peer-id", s.id.String()), zap.Error(errors.New(msg)))
69		} else {
70			plog.Errorf(msg)
71			plog.Infof("peer %s became inactive (message send to peer failed)", s.id)
72		}
73		s.active = false
74		s.since = time.Time{}
75
76		activePeers.WithLabelValues(s.local.String(), s.id.String()).Dec()
77		disconnectedPeers.WithLabelValues(s.local.String(), s.id.String()).Inc()
78		return
79	}
80
81	if s.lg != nil {
82		s.lg.Debug("peer deactivated again", zap.String("peer-id", s.id.String()), zap.Error(errors.New(msg)))
83	}
84}
85
86func (s *peerStatus) isActive() bool {
87	s.mu.Lock()
88	defer s.mu.Unlock()
89	return s.active
90}
91
92func (s *peerStatus) activeSince() time.Time {
93	s.mu.Lock()
94	defer s.mu.Unlock()
95	return s.since
96}
97