[etcd]raftpd confstate.go
_________________看前须知__________________
message ConfState {
// The voters in the incoming config. (If the configuration is not joint,
// then the outgoing config is empty).
repeated uint64 voters = 1;
// The learners in the incoming config.
repeated uint64 learners = 2;
// The voters in the outgoing config.
repeated uint64 voters_outgoing = 3;
// The nodes that will become learners when the outgoing config is removed.
// These nodes are necessarily currently in nodes_joint (or they would have
// been added to the incoming config right away).
repeated uint64 learners_next = 4;
// If set, the config is joint and Raft will automatically transition into
// the final config (i.e. remove the outgoing config) when this is safe.
optional bool auto_leave = 5 [(gogoproto.nullable) = false];
}
这个是raftpd的proto文件中对于ConfState的结构定义,包括集群中的配置状态,voter、learner有多少个,移除了多少个voter等等。
在paxos中,Learners可以看做是所有被确认消息的执行器,一旦有Client的消息请求被Acceptors确认之后,Learners会做相应的处理(如:执行消息内容,发送回复给Client)。Learner可以有多个。Acceptor (Voters)可以看做是消息请求的存储器。一般来说Acceptors是由一定数量的服务组成的,当消息被发送给Acceptor, 只有大部分Acceptor确认接收此消息,该消息才会被存储,否则该消息将被丢弃。
_________________________________________
在confstate.go文件中只有如下一个函数,Equivalent函数,它是ConfState这个接口的一种方法,这个函数字面上理解就是相等,用于判断当前的confstate cs和传入的cs2这个confstate状态参数是否相等。相等就给error类型返回一个nil,表示没有错误;如果不相等就给error返回错误显示不同的地方在哪。其中用到了sort.Slice这个方法,就是一种排序的工具,这里i<j,就是从小到大的方式排序。
// Equivalent returns a nil error if the inputs describe the same configuration.
// On mismatch, returns a descriptive error showing the differences.
func (cs ConfState) Equivalent(cs2 ConfState) error {
cs1 := cs
orig1, orig2 := cs1, cs2
s := func(sl *[]uint64) {
*sl = append([]uint64(nil), *sl...)
sort.Slice(*sl, func(i, j int) bool { return (*sl)[i] < (*sl)[j] })
}
for _, cs := range []*ConfState{&cs1, &cs2} {
s(&cs.Voters)
s(&cs.Learners)
s(&cs.VotersOutgoing)
s(&cs.LearnersNext)
cs.XXX_unrecognized = nil
}
if !reflect.DeepEqual(cs1, cs2) {
return fmt.Errorf("ConfStates not equivalent after sorting:\n%+#v\n%+#v\nInputs were:\n%+#v\n%+#v", cs1, cs2, orig1, orig2)
}
return nil
}
下面我们来看看测试,首先定义一个测试案例的结构体, 包括两个参数,第一个是confstate状态参数,有2个state,第二个是是否相等的结果。
然后在测试案例中实例化一个testCases切片存储几种特殊的状态,看看那个Equivalent函数写的对不对,自己写了几种状态
case0:对于confstate中的值进行打乱顺序,但是值的相等的。
case1:对nil和empty的state不敏感
case2:不相等的voters
case3:不相等的learners
case4:对auto_leave 这个配置敏感
func TestConfState_Equivalent(t *testing.T) {
type testCase struct {
cs, cs2 ConfState
ok bool
}
testCases := []testCase{
// Reordered voters and learners.
{ConfState{
Voters: []uint64{1, 2, 3},
Learners: []uint64{5, 4, 6},
VotersOutgoing: []uint64{9, 8, 7},
LearnersNext: []uint64{10, 20, 15},
}, ConfState{
Voters: []uint64{1, 2, 3},
Learners: []uint64{4, 5, 6},
VotersOutgoing: []uint64{7, 9, 8},
LearnersNext: []uint64{20, 10, 15},
}, true},
// Not sensitive to nil vs empty slice.
{ConfState{Voters: []uint64{}}, ConfState{Voters: []uint64(nil)}, true},
// Non-equivalent voters.
{ConfState{Voters: []uint64{1, 2, 3, 4}}, ConfState{Voters: []uint64{2, 1, 3}}, false},
{ConfState{Voters: []uint64{1, 4, 3}}, ConfState{Voters: []uint64{2, 1, 3}}, false},
// Non-equivalent learners.
{ConfState{Voters: []uint64{1, 2, 3, 4}}, ConfState{Voters: []uint64{2, 1, 3}}, false},
// Sensitive to AutoLeave flag.
{ConfState{AutoLeave: true}, ConfState{}, false},
}
for _, tc := range testCases {
t.Run("", func(t *testing.T) {
if err := tc.cs.Equivalent(tc.cs2); (err == nil) != tc.ok {
t.Fatalf("wanted error: %t, got:\n%s", tc.ok, err)
}
})
}
}