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	if lg == nil {
44		lg = zap.NewNop()
45	}
46	return &peerStatus{lg: lg, local: local, id: id}
47}
48
49func (s *peerStatus) activate() {
50	s.mu.Lock()
51	defer s.mu.Unlock()
52	if !s.active {
53		s.lg.Info("peer became active", zap.String("peer-id", s.id.String()))
54		s.active = true
55		s.since = time.Now()
56
57		activePeers.WithLabelValues(s.local.String(), s.id.String()).Inc()
58	}
59}
60
61func (s *peerStatus) deactivate(failure failureType, reason string) {
62	s.mu.Lock()
63	defer s.mu.Unlock()
64	msg := fmt.Sprintf("failed to %s %s on %s (%s)", failure.action, s.id, failure.source, reason)
65	if s.active {
66		s.lg.Warn("peer became inactive (message send to peer failed)", zap.String("peer-id", s.id.String()), zap.Error(errors.New(msg)))
67		s.active = false
68		s.since = time.Time{}
69
70		activePeers.WithLabelValues(s.local.String(), s.id.String()).Dec()
71		disconnectedPeers.WithLabelValues(s.local.String(), s.id.String()).Inc()
72		return
73	}
74
75	if s.lg != nil {
76		s.lg.Debug("peer deactivated again", zap.String("peer-id", s.id.String()), zap.Error(errors.New(msg)))
77	}
78}
79
80func (s *peerStatus) isActive() bool {
81	s.mu.Lock()
82	defer s.mu.Unlock()
83	return s.active
84}
85
86func (s *peerStatus) activeSince() time.Time {
87	s.mu.Lock()
88	defer s.mu.Unlock()
89	return s.since
90}
91