...

Source file src/go.etcd.io/etcd/raft/v3/util.go

Documentation: go.etcd.io/etcd/raft/v3

     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  
    15  package raft
    16  
    17  import (
    18  	"bytes"
    19  	"fmt"
    20  	"strings"
    21  
    22  	pb "go.etcd.io/etcd/raft/v3/raftpb"
    23  )
    24  
    25  func (st StateType) MarshalJSON() ([]byte, error) {
    26  	return []byte(fmt.Sprintf("%q", st.String())), nil
    27  }
    28  
    29  func min(a, b uint64) uint64 {
    30  	if a > b {
    31  		return b
    32  	}
    33  	return a
    34  }
    35  
    36  func max(a, b uint64) uint64 {
    37  	if a > b {
    38  		return a
    39  	}
    40  	return b
    41  }
    42  
    43  func 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  
    48  func 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.
    53  func 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  
    64  func 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  
    74  func DescribeSoftState(ss SoftState) string {
    75  	return fmt.Sprintf("Lead:%d State:%s", ss.Lead, ss.RaftState)
    76  }
    77  
    78  func 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  
    85  func 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  
    90  func 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.
   129  type EntryFormatter func([]byte) string
   130  
   131  // DescribeMessage returns a concise human-readable description of a
   132  // Message for debugging.
   133  func 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.
   160  func 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.
   166  func 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.
   204  func 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  
   212  func 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  
   227  func 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  

View as plain text