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 raft 16 17import ( 18 "bytes" 19 "fmt" 20 "strings" 21 22 pb "go.etcd.io/etcd/raft/raftpb" 23) 24 25func (st StateType) MarshalJSON() ([]byte, error) { 26 return []byte(fmt.Sprintf("%q", st.String())), nil 27} 28 29func min(a, b uint64) uint64 { 30 if a > b { 31 return b 32 } 33 return a 34} 35 36func max(a, b uint64) uint64 { 37 if a > b { 38 return a 39 } 40 return b 41} 42 43func IsLocalMsg(msgt pb.MessageType) bool { 44 return msgt == pb.MsgHup || msgt == pb.MsgBeat || msgt == pb.MsgUnreachable || 45 msgt == pb.MsgSnapStatus || msgt == pb.MsgCheckQuorum 46} 47 48func IsResponseMsg(msgt pb.MessageType) bool { 49 return msgt == pb.MsgAppResp || msgt == pb.MsgVoteResp || msgt == pb.MsgHeartbeatResp || msgt == pb.MsgUnreachable || msgt == pb.MsgPreVoteResp 50} 51 52// voteResponseType maps vote and prevote message types to their corresponding responses. 53func voteRespMsgType(msgt pb.MessageType) pb.MessageType { 54 switch msgt { 55 case pb.MsgVote: 56 return pb.MsgVoteResp 57 case pb.MsgPreVote: 58 return pb.MsgPreVoteResp 59 default: 60 panic(fmt.Sprintf("not a vote message: %s", msgt)) 61 } 62} 63 64func DescribeHardState(hs pb.HardState) string { 65 var buf strings.Builder 66 fmt.Fprintf(&buf, "Term:%d", hs.Term) 67 if hs.Vote != 0 { 68 fmt.Fprintf(&buf, " Vote:%d", hs.Vote) 69 } 70 fmt.Fprintf(&buf, " Commit:%d", hs.Commit) 71 return buf.String() 72} 73 74func DescribeSoftState(ss SoftState) string { 75 return fmt.Sprintf("Lead:%d State:%s", ss.Lead, ss.RaftState) 76} 77 78func DescribeConfState(state pb.ConfState) string { 79 return fmt.Sprintf( 80 "Voters:%v VotersOutgoing:%v Learners:%v LearnersNext:%v AutoLeave:%v", 81 state.Voters, state.VotersOutgoing, state.Learners, state.LearnersNext, state.AutoLeave, 82 ) 83} 84 85func DescribeSnapshot(snap pb.Snapshot) string { 86 m := snap.Metadata 87 return fmt.Sprintf("Index:%d Term:%d ConfState:%s", m.Index, m.Term, DescribeConfState(m.ConfState)) 88} 89 90func DescribeReady(rd Ready, f EntryFormatter) string { 91 var buf strings.Builder 92 if rd.SoftState != nil { 93 fmt.Fprint(&buf, DescribeSoftState(*rd.SoftState)) 94 buf.WriteByte('\n') 95 } 96 if !IsEmptyHardState(rd.HardState) { 97 fmt.Fprintf(&buf, "HardState %s", DescribeHardState(rd.HardState)) 98 buf.WriteByte('\n') 99 } 100 if len(rd.ReadStates) > 0 { 101 fmt.Fprintf(&buf, "ReadStates %v\n", rd.ReadStates) 102 } 103 if len(rd.Entries) > 0 { 104 buf.WriteString("Entries:\n") 105 fmt.Fprint(&buf, DescribeEntries(rd.Entries, f)) 106 } 107 if !IsEmptySnap(rd.Snapshot) { 108 fmt.Fprintf(&buf, "Snapshot %s\n", DescribeSnapshot(rd.Snapshot)) 109 } 110 if len(rd.CommittedEntries) > 0 { 111 buf.WriteString("CommittedEntries:\n") 112 fmt.Fprint(&buf, DescribeEntries(rd.CommittedEntries, f)) 113 } 114 if len(rd.Messages) > 0 { 115 buf.WriteString("Messages:\n") 116 for _, msg := range rd.Messages { 117 fmt.Fprint(&buf, DescribeMessage(msg, f)) 118 buf.WriteByte('\n') 119 } 120 } 121 if buf.Len() > 0 { 122 return fmt.Sprintf("Ready MustSync=%t:\n%s", rd.MustSync, buf.String()) 123 } 124 return "<empty Ready>" 125} 126 127// EntryFormatter can be implemented by the application to provide human-readable formatting 128// of entry data. Nil is a valid EntryFormatter and will use a default format. 129type EntryFormatter func([]byte) string 130 131// DescribeMessage returns a concise human-readable description of a 132// Message for debugging. 133func DescribeMessage(m pb.Message, f EntryFormatter) string { 134 var buf bytes.Buffer 135 fmt.Fprintf(&buf, "%x->%x %v Term:%d Log:%d/%d", m.From, m.To, m.Type, m.Term, m.LogTerm, m.Index) 136 if m.Reject { 137 fmt.Fprintf(&buf, " Rejected (Hint: %d)", m.RejectHint) 138 } 139 if m.Commit != 0 { 140 fmt.Fprintf(&buf, " Commit:%d", m.Commit) 141 } 142 if len(m.Entries) > 0 { 143 fmt.Fprintf(&buf, " Entries:[") 144 for i, e := range m.Entries { 145 if i != 0 { 146 buf.WriteString(", ") 147 } 148 buf.WriteString(DescribeEntry(e, f)) 149 } 150 fmt.Fprintf(&buf, "]") 151 } 152 if !IsEmptySnap(m.Snapshot) { 153 fmt.Fprintf(&buf, " Snapshot: %s", DescribeSnapshot(m.Snapshot)) 154 } 155 return buf.String() 156} 157 158// PayloadSize is the size of the payload of this Entry. Notably, it does not 159// depend on its Index or Term. 160func PayloadSize(e pb.Entry) int { 161 return len(e.Data) 162} 163 164// DescribeEntry returns a concise human-readable description of an 165// Entry for debugging. 166func DescribeEntry(e pb.Entry, f EntryFormatter) string { 167 if f == nil { 168 f = func(data []byte) string { return fmt.Sprintf("%q", data) } 169 } 170 171 formatConfChange := func(cc pb.ConfChangeI) string { 172 // TODO(tbg): give the EntryFormatter a type argument so that it gets 173 // a chance to expose the Context. 174 return pb.ConfChangesToString(cc.AsV2().Changes) 175 } 176 177 var formatted string 178 switch e.Type { 179 case pb.EntryNormal: 180 formatted = f(e.Data) 181 case pb.EntryConfChange: 182 var cc pb.ConfChange 183 if err := cc.Unmarshal(e.Data); err != nil { 184 formatted = err.Error() 185 } else { 186 formatted = formatConfChange(cc) 187 } 188 case pb.EntryConfChangeV2: 189 var cc pb.ConfChangeV2 190 if err := cc.Unmarshal(e.Data); err != nil { 191 formatted = err.Error() 192 } else { 193 formatted = formatConfChange(cc) 194 } 195 } 196 if formatted != "" { 197 formatted = " " + formatted 198 } 199 return fmt.Sprintf("%d/%d %s%s", e.Term, e.Index, e.Type, formatted) 200} 201 202// DescribeEntries calls DescribeEntry for each Entry, adding a newline to 203// each. 204func DescribeEntries(ents []pb.Entry, f EntryFormatter) string { 205 var buf bytes.Buffer 206 for _, e := range ents { 207 _, _ = buf.WriteString(DescribeEntry(e, f) + "\n") 208 } 209 return buf.String() 210} 211 212func limitSize(ents []pb.Entry, maxSize uint64) []pb.Entry { 213 if len(ents) == 0 { 214 return ents 215 } 216 size := ents[0].Size() 217 var limit int 218 for limit = 1; limit < len(ents); limit++ { 219 size += ents[limit].Size() 220 if uint64(size) > maxSize { 221 break 222 } 223 } 224 return ents[:limit] 225} 226 227func assertConfStatesEquivalent(l Logger, cs1, cs2 pb.ConfState) { 228 err := cs1.Equivalent(cs2) 229 if err == nil { 230 return 231 } 232 l.Panic(err) 233} 234