|
|
@@ -30,6 +30,11 @@ import (
|
|
|
|
|
|
// nextEnts returns the appliable entries and updates the applied index
|
|
|
func nextEnts(r *raft) (ents []pb.Entry) {
|
|
|
+ // Transfer all unstable entries to "stable" storage.
|
|
|
+ memStorage := r.raftLog.storage.(*MemoryStorage)
|
|
|
+ memStorage.Append(r.raftLog.unstableEntries())
|
|
|
+ r.raftLog.stableTo(r.raftLog.lastIndex())
|
|
|
+
|
|
|
ents = r.raftLog.nextEnts()
|
|
|
r.raftLog.appliedTo(r.raftLog.committed)
|
|
|
return ents
|
|
|
@@ -280,9 +285,9 @@ func TestCommitWithoutNewTermEntry(t *testing.T) {
|
|
|
}
|
|
|
|
|
|
func TestDuelingCandidates(t *testing.T) {
|
|
|
- a := newRaft(1, []uint64{1, 2, 3}, 10, 1)
|
|
|
- b := newRaft(2, []uint64{1, 2, 3}, 10, 1)
|
|
|
- c := newRaft(3, []uint64{1, 2, 3}, 10, 1)
|
|
|
+ a := newRaft(1, []uint64{1, 2, 3}, 10, 1, nil)
|
|
|
+ b := newRaft(2, []uint64{1, 2, 3}, 10, 1, nil)
|
|
|
+ c := newRaft(3, []uint64{1, 2, 3}, 10, 1, nil)
|
|
|
|
|
|
nt := newNetwork(a, b, c)
|
|
|
nt.cut(1, 3)
|
|
|
@@ -293,7 +298,11 @@ func TestDuelingCandidates(t *testing.T) {
|
|
|
nt.recover()
|
|
|
nt.send(pb.Message{From: 3, To: 3, Type: pb.MsgHup})
|
|
|
|
|
|
- wlog := &raftLog{ents: []pb.Entry{{}, pb.Entry{Data: nil, Term: 1, Index: 1}}, committed: 1}
|
|
|
+ wlog := &raftLog{
|
|
|
+ storage: &MemoryStorage{ents: []pb.Entry{{}, pb.Entry{Data: nil, Term: 1, Index: 1}}},
|
|
|
+ committed: 1,
|
|
|
+ unstable: 2,
|
|
|
+ }
|
|
|
tests := []struct {
|
|
|
sm *raft
|
|
|
state StateType
|
|
|
@@ -302,7 +311,7 @@ func TestDuelingCandidates(t *testing.T) {
|
|
|
}{
|
|
|
{a, StateFollower, 2, wlog},
|
|
|
{b, StateFollower, 2, wlog},
|
|
|
- {c, StateFollower, 2, newLog()},
|
|
|
+ {c, StateFollower, 2, newLog(nil)},
|
|
|
}
|
|
|
|
|
|
for i, tt := range tests {
|
|
|
@@ -345,7 +354,13 @@ func TestCandidateConcede(t *testing.T) {
|
|
|
if g := a.Term; g != 1 {
|
|
|
t.Errorf("term = %d, want %d", g, 1)
|
|
|
}
|
|
|
- wantLog := ltoa(&raftLog{ents: []pb.Entry{{}, {Data: nil, Term: 1, Index: 1}, {Term: 1, Index: 2, Data: data}}, committed: 2})
|
|
|
+ wantLog := ltoa(&raftLog{
|
|
|
+ storage: &MemoryStorage{
|
|
|
+ ents: []pb.Entry{{}, {Data: nil, Term: 1, Index: 1}, {Term: 1, Index: 2, Data: data}},
|
|
|
+ },
|
|
|
+ unstable: 3,
|
|
|
+ committed: 2,
|
|
|
+ })
|
|
|
for i, p := range tt.peers {
|
|
|
if sm, ok := p.(*raft); ok {
|
|
|
l := ltoa(sm.raftLog)
|
|
|
@@ -378,10 +393,13 @@ func TestOldMessages(t *testing.T) {
|
|
|
tt.send(pb.Message{From: 1, To: 1, Type: pb.MsgApp, Term: 1, Entries: []pb.Entry{{Term: 1}}})
|
|
|
|
|
|
l := &raftLog{
|
|
|
- ents: []pb.Entry{
|
|
|
- {}, {Data: nil, Term: 1, Index: 1},
|
|
|
- {Data: nil, Term: 2, Index: 2}, {Data: nil, Term: 3, Index: 3},
|
|
|
+ storage: &MemoryStorage{
|
|
|
+ ents: []pb.Entry{
|
|
|
+ {}, {Data: nil, Term: 1, Index: 1},
|
|
|
+ {Data: nil, Term: 2, Index: 2}, {Data: nil, Term: 3, Index: 3},
|
|
|
+ },
|
|
|
},
|
|
|
+ unstable: 4,
|
|
|
committed: 3,
|
|
|
}
|
|
|
base := ltoa(l)
|
|
|
@@ -432,9 +450,14 @@ func TestProposal(t *testing.T) {
|
|
|
send(pb.Message{From: 1, To: 1, Type: pb.MsgHup})
|
|
|
send(pb.Message{From: 1, To: 1, Type: pb.MsgProp, Entries: []pb.Entry{{Data: data}}})
|
|
|
|
|
|
- wantLog := newLog()
|
|
|
+ wantLog := newLog(nil)
|
|
|
if tt.success {
|
|
|
- wantLog = &raftLog{ents: []pb.Entry{{}, {Data: nil, Term: 1, Index: 1}, {Term: 1, Index: 2, Data: data}}, committed: 2}
|
|
|
+ wantLog = &raftLog{
|
|
|
+ storage: &MemoryStorage{
|
|
|
+ ents: []pb.Entry{{}, {Data: nil, Term: 1, Index: 1}, {Term: 1, Index: 2, Data: data}},
|
|
|
+ },
|
|
|
+ unstable: 3,
|
|
|
+ committed: 2}
|
|
|
}
|
|
|
base := ltoa(wantLog)
|
|
|
for i, p := range tt.peers {
|
|
|
@@ -468,7 +491,12 @@ func TestProposalByProxy(t *testing.T) {
|
|
|
// propose via follower
|
|
|
tt.send(pb.Message{From: 2, To: 2, Type: pb.MsgProp, Entries: []pb.Entry{{Data: []byte("somedata")}}})
|
|
|
|
|
|
- wantLog := &raftLog{ents: []pb.Entry{{}, {Data: nil, Term: 1, Index: 1}, {Term: 1, Data: data, Index: 2}}, committed: 2}
|
|
|
+ wantLog := &raftLog{
|
|
|
+ storage: &MemoryStorage{
|
|
|
+ ents: []pb.Entry{{}, {Data: nil, Term: 1, Index: 1}, {Term: 1, Data: data, Index: 2}},
|
|
|
+ },
|
|
|
+ unstable: 3,
|
|
|
+ committed: 2}
|
|
|
base := ltoa(wantLog)
|
|
|
for i, p := range tt.peers {
|
|
|
if sm, ok := p.(*raft); ok {
|
|
|
@@ -513,13 +541,15 @@ func TestCompact(t *testing.T) {
|
|
|
raftLog: &raftLog{
|
|
|
committed: 2,
|
|
|
applied: 2,
|
|
|
- ents: []pb.Entry{{}, {Term: 1}, {Term: 1}, {Term: 1}},
|
|
|
+ storage: &MemoryStorage{
|
|
|
+ ents: []pb.Entry{{}, {Term: 1}, {Term: 1}, {Term: 1}},
|
|
|
+ },
|
|
|
},
|
|
|
}
|
|
|
sm.compact(tt.compacti, tt.nodes, tt.snapd)
|
|
|
sort.Sort(uint64Slice(sm.raftLog.snapshot.Nodes))
|
|
|
- if sm.raftLog.offset != tt.compacti {
|
|
|
- t.Errorf("%d: log.offset = %d, want %d", i, sm.raftLog.offset, tt.compacti)
|
|
|
+ if sm.raftLog.firstIndex() != tt.compacti {
|
|
|
+ t.Errorf("%d: log.firstIndex = %d, want %d", i, sm.raftLog.firstIndex(), tt.compacti)
|
|
|
}
|
|
|
if !reflect.DeepEqual(sm.raftLog.snapshot.Nodes, tt.nodes) {
|
|
|
t.Errorf("%d: snap.nodes = %v, want %v", i, sm.raftLog.snapshot.Nodes, tt.nodes)
|
|
|
@@ -564,7 +594,11 @@ func TestCommit(t *testing.T) {
|
|
|
for j := 0; j < len(tt.matches); j++ {
|
|
|
prs[uint64(j)] = &progress{tt.matches[j], tt.matches[j] + 1}
|
|
|
}
|
|
|
- sm := &raft{raftLog: &raftLog{ents: tt.logs}, prs: prs, HardState: pb.HardState{Term: tt.smTerm}}
|
|
|
+ sm := &raft{
|
|
|
+ raftLog: &raftLog{storage: &MemoryStorage{ents: tt.logs}, unstable: uint64(len(tt.logs))},
|
|
|
+ prs: prs,
|
|
|
+ HardState: pb.HardState{Term: tt.smTerm},
|
|
|
+ }
|
|
|
sm.maybeCommit()
|
|
|
if g := sm.raftLog.committed; g != tt.w {
|
|
|
t.Errorf("#%d: committed = %d, want %d", i, g, tt.w)
|
|
|
@@ -586,7 +620,7 @@ func TestIsElectionTimeout(t *testing.T) {
|
|
|
}
|
|
|
|
|
|
for i, tt := range tests {
|
|
|
- sm := newRaft(1, []uint64{1}, 10, 1)
|
|
|
+ sm := newRaft(1, []uint64{1}, 10, 1, nil)
|
|
|
sm.elapsed = tt.elapse
|
|
|
c := 0
|
|
|
for j := 0; j < 10000; j++ {
|
|
|
@@ -611,7 +645,7 @@ func TestStepIgnoreOldTermMsg(t *testing.T) {
|
|
|
fakeStep := func(r *raft, m pb.Message) {
|
|
|
called = true
|
|
|
}
|
|
|
- sm := newRaft(1, []uint64{1}, 10, 1)
|
|
|
+ sm := newRaft(1, []uint64{1}, 10, 1, nil)
|
|
|
sm.step = fakeStep
|
|
|
sm.Term = 2
|
|
|
sm.Step(pb.Message{Type: pb.MsgApp, Term: sm.Term - 1})
|
|
|
@@ -654,7 +688,11 @@ func TestHandleMsgApp(t *testing.T) {
|
|
|
sm := &raft{
|
|
|
state: StateFollower,
|
|
|
HardState: pb.HardState{Term: 2},
|
|
|
- raftLog: &raftLog{committed: 0, ents: []pb.Entry{{}, {Term: 1}, {Term: 2}}},
|
|
|
+ raftLog: &raftLog{
|
|
|
+ committed: 0,
|
|
|
+ storage: &MemoryStorage{ents: []pb.Entry{{}, {Term: 1}, {Term: 2}}},
|
|
|
+ unstable: 3,
|
|
|
+ },
|
|
|
}
|
|
|
|
|
|
sm.handleAppendEntries(tt.m)
|
|
|
@@ -709,7 +747,7 @@ func TestRecvMsgVote(t *testing.T) {
|
|
|
}
|
|
|
|
|
|
for i, tt := range tests {
|
|
|
- sm := newRaft(1, []uint64{1}, 10, 1)
|
|
|
+ sm := newRaft(1, []uint64{1}, 10, 1, nil)
|
|
|
sm.state = tt.state
|
|
|
switch tt.state {
|
|
|
case StateFollower:
|
|
|
@@ -720,7 +758,10 @@ func TestRecvMsgVote(t *testing.T) {
|
|
|
sm.step = stepLeader
|
|
|
}
|
|
|
sm.HardState = pb.HardState{Vote: tt.voteFor}
|
|
|
- sm.raftLog = &raftLog{ents: []pb.Entry{{}, {Term: 2}, {Term: 2}}}
|
|
|
+ sm.raftLog = &raftLog{
|
|
|
+ storage: &MemoryStorage{ents: []pb.Entry{{}, {Term: 2}, {Term: 2}}},
|
|
|
+ unstable: 3,
|
|
|
+ }
|
|
|
|
|
|
sm.Step(pb.Message{Type: pb.MsgVote, From: 2, Index: tt.i, LogTerm: tt.term})
|
|
|
|
|
|
@@ -766,7 +807,7 @@ func TestStateTransition(t *testing.T) {
|
|
|
}
|
|
|
}()
|
|
|
|
|
|
- sm := newRaft(1, []uint64{1}, 10, 1)
|
|
|
+ sm := newRaft(1, []uint64{1}, 10, 1, nil)
|
|
|
sm.state = tt.from
|
|
|
|
|
|
switch tt.to {
|
|
|
@@ -805,7 +846,7 @@ func TestAllServerStepdown(t *testing.T) {
|
|
|
tterm := uint64(3)
|
|
|
|
|
|
for i, tt := range tests {
|
|
|
- sm := newRaft(1, []uint64{1, 2, 3}, 10, 1)
|
|
|
+ sm := newRaft(1, []uint64{1, 2, 3}, 10, 1, nil)
|
|
|
switch tt.state {
|
|
|
case StateFollower:
|
|
|
sm.becomeFollower(1, None)
|
|
|
@@ -825,8 +866,8 @@ func TestAllServerStepdown(t *testing.T) {
|
|
|
if sm.Term != tt.wterm {
|
|
|
t.Errorf("#%d.%d term = %v , want %v", i, j, sm.Term, tt.wterm)
|
|
|
}
|
|
|
- if uint64(len(sm.raftLog.ents)) != tt.windex {
|
|
|
- t.Errorf("#%d.%d index = %v , want %v", i, j, len(sm.raftLog.ents), tt.windex)
|
|
|
+ if uint64(len(sm.raftLog.allEntries())) != tt.windex {
|
|
|
+ t.Errorf("#%d.%d index = %v , want %v", i, j, len(sm.raftLog.allEntries()), tt.windex)
|
|
|
}
|
|
|
wlead := uint64(2)
|
|
|
if msgType == pb.MsgVote {
|
|
|
@@ -861,8 +902,11 @@ func TestLeaderAppResp(t *testing.T) {
|
|
|
for i, tt := range tests {
|
|
|
// sm term is 1 after it becomes the leader.
|
|
|
// thus the last log term must be 1 to be committed.
|
|
|
- sm := newRaft(1, []uint64{1, 2, 3}, 10, 1)
|
|
|
- sm.raftLog = &raftLog{ents: []pb.Entry{{}, {Term: 0}, {Term: 1}}}
|
|
|
+ sm := newRaft(1, []uint64{1, 2, 3}, 10, 1, nil)
|
|
|
+ sm.raftLog = &raftLog{
|
|
|
+ storage: &MemoryStorage{ents: []pb.Entry{{}, {Term: 0}, {Term: 1}}},
|
|
|
+ unstable: 3,
|
|
|
+ }
|
|
|
sm.becomeCandidate()
|
|
|
sm.becomeLeader()
|
|
|
sm.readMessages()
|
|
|
@@ -902,7 +946,7 @@ func TestBcastBeat(t *testing.T) {
|
|
|
Term: 1,
|
|
|
Nodes: []uint64{1, 2, 3},
|
|
|
}
|
|
|
- sm := newRaft(1, []uint64{1, 2, 3}, 10, 1)
|
|
|
+ sm := newRaft(1, []uint64{1, 2, 3}, 10, 1, nil)
|
|
|
sm.Term = 1
|
|
|
sm.restore(s)
|
|
|
|
|
|
@@ -952,8 +996,8 @@ func TestRecvMsgBeat(t *testing.T) {
|
|
|
}
|
|
|
|
|
|
for i, tt := range tests {
|
|
|
- sm := newRaft(1, []uint64{1, 2, 3}, 10, 1)
|
|
|
- sm.raftLog = &raftLog{ents: []pb.Entry{{}, {Term: 0}, {Term: 1}}}
|
|
|
+ sm := newRaft(1, []uint64{1, 2, 3}, 10, 1, nil)
|
|
|
+ sm.raftLog = &raftLog{storage: &MemoryStorage{ents: []pb.Entry{{}, {Term: 0}, {Term: 1}}}}
|
|
|
sm.Term = 1
|
|
|
sm.state = tt.state
|
|
|
switch tt.state {
|
|
|
@@ -985,7 +1029,7 @@ func TestRestore(t *testing.T) {
|
|
|
Nodes: []uint64{1, 2, 3},
|
|
|
}
|
|
|
|
|
|
- sm := newRaft(1, []uint64{1, 2}, 10, 1)
|
|
|
+ sm := newRaft(1, []uint64{1, 2}, 10, 1, nil)
|
|
|
if ok := sm.restore(s); !ok {
|
|
|
t.Fatal("restore fail, want succeed")
|
|
|
}
|
|
|
@@ -1016,7 +1060,7 @@ func TestProvideSnap(t *testing.T) {
|
|
|
Term: 11, // magic number
|
|
|
Nodes: []uint64{1, 2},
|
|
|
}
|
|
|
- sm := newRaft(1, []uint64{1}, 10, 1)
|
|
|
+ sm := newRaft(1, []uint64{1}, 10, 1, nil)
|
|
|
// restore the statemachin from a snapshot
|
|
|
// so it has a compacted log and a snapshot
|
|
|
sm.restore(s)
|
|
|
@@ -1026,7 +1070,7 @@ func TestProvideSnap(t *testing.T) {
|
|
|
|
|
|
// force set the next of node 1, so that
|
|
|
// node 1 needs a snapshot
|
|
|
- sm.prs[2].next = sm.raftLog.offset
|
|
|
+ sm.prs[2].next = sm.raftLog.firstIndex()
|
|
|
|
|
|
sm.Step(pb.Message{From: 2, To: 1, Type: pb.MsgAppResp, Index: sm.prs[2].next - 1, Reject: true})
|
|
|
msgs := sm.readMessages()
|
|
|
@@ -1047,7 +1091,7 @@ func TestRestoreFromSnapMsg(t *testing.T) {
|
|
|
}
|
|
|
m := pb.Message{Type: pb.MsgSnap, From: 1, Term: 2, Snapshot: s}
|
|
|
|
|
|
- sm := newRaft(2, []uint64{1, 2}, 10, 1)
|
|
|
+ sm := newRaft(2, []uint64{1, 2}, 10, 1, nil)
|
|
|
sm.Step(m)
|
|
|
|
|
|
if !reflect.DeepEqual(sm.raftLog.snapshot, s) {
|
|
|
@@ -1086,7 +1130,7 @@ func TestSlowNodeRestore(t *testing.T) {
|
|
|
// it appends the entry to log and sets pendingConf to be true.
|
|
|
func TestStepConfig(t *testing.T) {
|
|
|
// a raft that cannot make progress
|
|
|
- r := newRaft(1, []uint64{1, 2}, 10, 1)
|
|
|
+ r := newRaft(1, []uint64{1, 2}, 10, 1, nil)
|
|
|
r.becomeCandidate()
|
|
|
r.becomeLeader()
|
|
|
index := r.raftLog.lastIndex()
|
|
|
@@ -1104,7 +1148,7 @@ func TestStepConfig(t *testing.T) {
|
|
|
// the proposal and keep its original state.
|
|
|
func TestStepIgnoreConfig(t *testing.T) {
|
|
|
// a raft that cannot make progress
|
|
|
- r := newRaft(1, []uint64{1, 2}, 10, 1)
|
|
|
+ r := newRaft(1, []uint64{1, 2}, 10, 1, nil)
|
|
|
r.becomeCandidate()
|
|
|
r.becomeLeader()
|
|
|
r.Step(pb.Message{From: 1, To: 1, Type: pb.MsgProp, Entries: []pb.Entry{{Type: pb.EntryConfChange}}})
|
|
|
@@ -1130,7 +1174,7 @@ func TestRecoverPendingConfig(t *testing.T) {
|
|
|
{pb.EntryConfChange, true},
|
|
|
}
|
|
|
for i, tt := range tests {
|
|
|
- r := newRaft(1, []uint64{1, 2}, 10, 1)
|
|
|
+ r := newRaft(1, []uint64{1, 2}, 10, 1, nil)
|
|
|
r.appendEntry(pb.Entry{Type: tt.entType})
|
|
|
r.becomeCandidate()
|
|
|
r.becomeLeader()
|
|
|
@@ -1149,7 +1193,7 @@ func TestRecoverDoublePendingConfig(t *testing.T) {
|
|
|
t.Errorf("expect panic, but nothing happens")
|
|
|
}
|
|
|
}()
|
|
|
- r := newRaft(1, []uint64{1, 2}, 10, 1)
|
|
|
+ r := newRaft(1, []uint64{1, 2}, 10, 1, nil)
|
|
|
r.appendEntry(pb.Entry{Type: pb.EntryConfChange})
|
|
|
r.appendEntry(pb.Entry{Type: pb.EntryConfChange})
|
|
|
r.becomeCandidate()
|
|
|
@@ -1159,7 +1203,7 @@ func TestRecoverDoublePendingConfig(t *testing.T) {
|
|
|
|
|
|
// TestAddNode tests that addNode could update pendingConf and nodes correctly.
|
|
|
func TestAddNode(t *testing.T) {
|
|
|
- r := newRaft(1, []uint64{1}, 10, 1)
|
|
|
+ r := newRaft(1, []uint64{1}, 10, 1, nil)
|
|
|
r.pendingConf = true
|
|
|
r.addNode(2)
|
|
|
if r.pendingConf != false {
|
|
|
@@ -1176,7 +1220,7 @@ func TestAddNode(t *testing.T) {
|
|
|
// TestRemoveNode tests that removeNode could update pendingConf, nodes and
|
|
|
// and removed list correctly.
|
|
|
func TestRemoveNode(t *testing.T) {
|
|
|
- r := newRaft(1, []uint64{1, 2}, 10, 1)
|
|
|
+ r := newRaft(1, []uint64{1, 2}, 10, 1, nil)
|
|
|
r.pendingConf = true
|
|
|
r.removeNode(2)
|
|
|
if r.pendingConf != false {
|
|
|
@@ -1216,7 +1260,12 @@ func ents(terms ...uint64) *raft {
|
|
|
ents = append(ents, pb.Entry{Term: term})
|
|
|
}
|
|
|
|
|
|
- sm := &raft{raftLog: &raftLog{ents: ents}}
|
|
|
+ sm := &raft{
|
|
|
+ raftLog: &raftLog{
|
|
|
+ storage: &MemoryStorage{ents: ents},
|
|
|
+ unstable: uint64(len(ents)),
|
|
|
+ },
|
|
|
+ }
|
|
|
sm.reset(0)
|
|
|
return sm
|
|
|
}
|
|
|
@@ -1241,7 +1290,7 @@ func newNetwork(peers ...Interface) *network {
|
|
|
id := peerAddrs[i]
|
|
|
switch v := p.(type) {
|
|
|
case nil:
|
|
|
- sm := newRaft(id, peerAddrs, 10, 1)
|
|
|
+ sm := newRaft(id, peerAddrs, 10, 1, nil)
|
|
|
npeers[id] = sm
|
|
|
case *raft:
|
|
|
v.id = id
|