Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

force new cluster, learner to leader #13213

Closed
Closed
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 54 additions & 12 deletions server/etcdserver/raft.go
Original file line number Diff line number Diff line change
Expand Up @@ -552,8 +552,9 @@ func (b *bootstrappedRaft) newRaftNode(ss *snap.Snapshotter) *raftNode {
// - ConfChangeAddNode, in which case the contained ID will be added into the set.
// - ConfChangeRemoveNode, in which case the contained ID will be removed from the set.
// - ConfChangeAddLearnerNode, in which the contained ID will be added into the set.
func getIDs(lg *zap.Logger, snap *raftpb.Snapshot, ents []raftpb.Entry) []uint64 {
func getIDs(lg *zap.Logger, snap *raftpb.Snapshot, ents []raftpb.Entry) ([]uint64, map[uint64]bool) {
ids := make(map[uint64]bool)
isLearnerMap := make(map[uint64]bool)
if snap != nil {
for _, id := range snap.Metadata.ConfState.Voters {
ids[id] = true
Expand All @@ -568,6 +569,7 @@ func getIDs(lg *zap.Logger, snap *raftpb.Snapshot, ents []raftpb.Entry) []uint64
switch cc.Type {
case raftpb.ConfChangeAddLearnerNode:
ids[cc.NodeID] = true
isLearnerMap[cc.NodeID] = true
case raftpb.ConfChangeAddNode:
ids[cc.NodeID] = true
case raftpb.ConfChangeRemoveNode:
Expand All @@ -583,15 +585,15 @@ func getIDs(lg *zap.Logger, snap *raftpb.Snapshot, ents []raftpb.Entry) []uint64
sids = append(sids, id)
}
sort.Sort(sids)
return []uint64(sids)
return []uint64(sids), isLearnerMap
}

// createConfigChangeEnts creates a series of Raft entries (i.e.
// EntryConfChange) to remove the set of given IDs from the cluster. The ID
// `self` is _not_ removed, even if present in the set.
// If `self` is not inside the given ids, it creates a Raft entry to add a
// default member with the given `self`.
func createConfigChangeEnts(lg *zap.Logger, ids []uint64, self uint64, term, index uint64) []raftpb.Entry {
func createConfigChangeEnts(lg *zap.Logger, ids []uint64, isLearnerMap map[uint64]bool, self uint64, term, index uint64) []raftpb.Entry {
found := false
for _, id := range ids {
if id == self {
Expand All @@ -602,20 +604,18 @@ func createConfigChangeEnts(lg *zap.Logger, ids []uint64, self uint64, term, ind
var ents []raftpb.Entry
next := index + 1

// NB: always add self first, then remove other nodes. Raft will panic if the
// set of voters ever becomes empty.
if !found {
addNodeFunc := func(id uint64, peerUrls []string) {
m := membership.Member{
ID: types.ID(self),
RaftAttributes: membership.RaftAttributes{PeerURLs: []string{"http://localhost:2380"}},
RaftAttributes: membership.RaftAttributes{PeerURLs: peerUrls},
}
ctx, err := json.Marshal(m)
if err != nil {
lg.Panic("failed to marshal member", zap.Error(err))
}
cc := &raftpb.ConfChange{
Type: raftpb.ConfChangeAddNode,
NodeID: self,
NodeID: id,
Context: ctx,
}
e := raftpb.Entry{
Expand All @@ -628,10 +628,7 @@ func createConfigChangeEnts(lg *zap.Logger, ids []uint64, self uint64, term, ind
next++
}

for _, id := range ids {
if id == self {
continue
}
delNodeFunc := func(id uint64) {
cc := &raftpb.ConfChange{
Type: raftpb.ConfChangeRemoveNode,
NodeID: id,
Expand All @@ -646,5 +643,50 @@ func createConfigChangeEnts(lg *zap.Logger, ids []uint64, self uint64, term, ind
next++
}

promoteNodeFunc := func(id uint64) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like a big change to me, I would expect some tests at least for this.

// build the context for the promote confChange. mark IsLearner to false and IsPromote to true.
promoteChangeContext := membership.ConfigChangeContext{
Member: membership.Member{
ID: types.ID(id),
},
IsPromote: true,
}

b, err := json.Marshal(promoteChangeContext)
if err != nil {
lg.Panic("failed to marshal member", zap.Error(err))
}

cc := &raftpb.ConfChange{
Type: raftpb.ConfChangeAddNode,
NodeID: id,
Context: b,
}

e := raftpb.Entry{
Type: raftpb.EntryConfChange,
Data: pbutil.MustMarshal(cc),
Term: term,
Index: next,
}
ents = append(ents, e)
next++
}

// NB: always add self first, then remove other nodes. Raft will panic if the
// set of voters ever becomes empty.
if !found {
addNodeFunc(self, []string{"http://localhost:2380"})
} else if isLearnerMap[self] {
promoteNodeFunc(self)
}

for _, id := range ids {
if id == self {
continue
}
delNodeFunc(id)
}

return ents
}
4 changes: 2 additions & 2 deletions server/etcdserver/raft_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ func TestGetIDs(t *testing.T) {
if tt.confState != nil {
snap.Metadata.ConfState = *tt.confState
}
idSet := getIDs(testLogger, &snap, tt.ents)
idSet, _ := getIDs(testLogger, &snap, tt.ents)
if !reflect.DeepEqual(idSet, tt.widSet) {
t.Errorf("#%d: idset = %#v, want %#v", i, idSet, tt.widSet)
}
Expand Down Expand Up @@ -146,7 +146,7 @@ func TestCreateConfigChangeEnts(t *testing.T) {
}

for i, tt := range tests {
gents := createConfigChangeEnts(testLogger, tt.ids, tt.self, tt.term, tt.index)
gents := createConfigChangeEnts(testLogger, tt.ids, make(map[uint64]bool), tt.self, tt.term, tt.index)
if !reflect.DeepEqual(gents, tt.wents) {
t.Errorf("#%d: ents = %v, want %v", i, gents, tt.wents)
}
Expand Down
4 changes: 3 additions & 1 deletion server/etcdserver/storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -192,9 +192,11 @@ func (wal *bootstrappedWAL) CommitedEntries() []raftpb.Entry {
}

func (wal *bootstrappedWAL) ConfigChangeEntries() []raftpb.Entry {
ids, isLearnerMap := getIDs(wal.lg, wal.snapshot, wal.ents)
return createConfigChangeEnts(
wal.lg,
getIDs(wal.lg, wal.snapshot, wal.ents),
ids,
isLearnerMap,
uint64(wal.id),
wal.st.Term,
wal.st.Commit,
Expand Down