etcd/raft/log_test.go

565 lines
14 KiB
Go
Raw Normal View History

/*
Copyright 2014 CoreOS, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
2014-06-28 03:15:04 +04:00
package raft
import (
"reflect"
"testing"
2014-08-28 05:53:18 +04:00
pb "github.com/coreos/etcd/raft/raftpb"
2014-06-28 03:15:04 +04:00
)
2014-10-26 04:49:49 +03:00
func TestFindConflict(t *testing.T) {
previousEnts := []pb.Entry{{Term: 1}, {Term: 2}, {Term: 3}}
tests := []struct {
from uint64
ents []pb.Entry
wconflict uint64
}{
// no conflict, empty ent
{1, []pb.Entry{}, 0},
{3, []pb.Entry{}, 0},
// no conflict
{1, []pb.Entry{{Term: 1}, {Term: 2}, {Term: 3}}, 0},
{2, []pb.Entry{{Term: 2}, {Term: 3}}, 0},
{3, []pb.Entry{{Term: 3}}, 0},
// no conflict, but has new entries
{1, []pb.Entry{{Term: 1}, {Term: 2}, {Term: 3}, {Term: 4}, {Term: 4}}, 4},
{2, []pb.Entry{{Term: 2}, {Term: 3}, {Term: 4}, {Term: 4}}, 4},
{3, []pb.Entry{{Term: 3}, {Term: 4}, {Term: 4}}, 4},
{4, []pb.Entry{{Term: 4}, {Term: 4}}, 4},
// conflicts with existing entries
{1, []pb.Entry{{Term: 4}, {Term: 4}}, 1},
{2, []pb.Entry{{Term: 1}, {Term: 4}, {Term: 4}}, 2},
{3, []pb.Entry{{Term: 1}, {Term: 2}, {Term: 4}, {Term: 4}}, 3},
}
for i, tt := range tests {
raftLog := newLog()
2014-10-27 21:09:44 +03:00
raftLog.append(raftLog.lastIndex(), previousEnts...)
2014-10-26 04:49:49 +03:00
gconflict := raftLog.findConflict(tt.from, tt.ents)
if gconflict != tt.wconflict {
t.Errorf("#%d: conflict = %d, want %d", i, gconflict, tt.wconflict)
}
}
}
func TestIsUpToDate(t *testing.T) {
previousEnts := []pb.Entry{{Term: 1}, {Term: 2}, {Term: 3}}
raftLog := newLog()
2014-10-27 21:09:44 +03:00
raftLog.append(raftLog.lastIndex(), previousEnts...)
tests := []struct {
lastIndex uint64
term uint64
wUpToDate bool
}{
// greater term, ignore lastIndex
{raftLog.lastIndex() - 1, 4, true},
{raftLog.lastIndex(), 4, true},
{raftLog.lastIndex() + 1, 4, true},
// smaller term, ignore lastIndex
{raftLog.lastIndex() - 1, 2, false},
{raftLog.lastIndex(), 2, false},
{raftLog.lastIndex() + 1, 2, false},
// equal term, lager lastIndex wins
{raftLog.lastIndex() - 1, 3, false},
{raftLog.lastIndex(), 3, true},
{raftLog.lastIndex() + 1, 3, true},
}
for i, tt := range tests {
gUpToDate := raftLog.isUpToDate(tt.lastIndex, tt.term)
if gUpToDate != tt.wUpToDate {
t.Errorf("#%d: uptodate = %v, want %v", i, gUpToDate, tt.wUpToDate)
}
}
}
2014-07-14 21:58:41 +04:00
func TestAppend(t *testing.T) {
2014-08-28 05:53:18 +04:00
previousEnts := []pb.Entry{{Term: 1}, {Term: 2}}
2014-10-08 14:29:53 +04:00
previousUnstable := uint64(3)
2014-07-14 21:58:41 +04:00
tests := []struct {
2014-10-08 14:29:53 +04:00
after uint64
2014-08-28 05:53:18 +04:00
ents []pb.Entry
2014-10-08 14:29:53 +04:00
windex uint64
2014-08-28 05:53:18 +04:00
wents []pb.Entry
2014-10-08 14:29:53 +04:00
wunstable uint64
2014-07-14 21:58:41 +04:00
}{
{
2,
2014-08-28 05:53:18 +04:00
[]pb.Entry{},
2014-07-14 21:58:41 +04:00
2,
2014-08-28 05:53:18 +04:00
[]pb.Entry{{Term: 1}, {Term: 2}},
3,
2014-07-14 21:58:41 +04:00
},
{
2,
2014-08-28 05:53:18 +04:00
[]pb.Entry{{Term: 2}},
2014-07-14 21:58:41 +04:00
3,
2014-08-28 05:53:18 +04:00
[]pb.Entry{{Term: 1}, {Term: 2}, {Term: 2}},
3,
2014-07-14 21:58:41 +04:00
},
// conflicts with index 1
{
0,
2014-08-28 05:53:18 +04:00
[]pb.Entry{{Term: 2}},
2014-07-14 21:58:41 +04:00
1,
2014-08-28 05:53:18 +04:00
[]pb.Entry{{Term: 2}},
1,
2014-07-14 21:58:41 +04:00
},
// conflicts with index 2
{
1,
2014-08-28 05:53:18 +04:00
[]pb.Entry{{Term: 3}, {Term: 3}},
2014-07-14 21:58:41 +04:00
3,
2014-08-28 05:53:18 +04:00
[]pb.Entry{{Term: 1}, {Term: 3}, {Term: 3}},
2,
2014-07-14 21:58:41 +04:00
},
}
for i, tt := range tests {
2014-07-24 03:15:25 +04:00
raftLog := newLog()
2014-10-27 21:09:44 +03:00
raftLog.append(raftLog.lastIndex(), previousEnts...)
raftLog.unstable = previousUnstable
2014-07-24 03:15:25 +04:00
index := raftLog.append(tt.after, tt.ents...)
2014-07-14 21:58:41 +04:00
if index != tt.windex {
t.Errorf("#%d: lastIndex = %d, want %d", i, index, tt.windex)
}
2014-07-24 03:15:25 +04:00
if g := raftLog.entries(1); !reflect.DeepEqual(g, tt.wents) {
2014-07-14 21:58:41 +04:00
t.Errorf("#%d: logEnts = %+v, want %+v", i, g, tt.wents)
}
if g := raftLog.unstable; g != tt.wunstable {
t.Errorf("#%d: unstable = %d, want %d", i, g, tt.wunstable)
}
2014-07-14 21:58:41 +04:00
}
}
2014-10-29 01:06:21 +03:00
// TestLogMaybeAppend ensures:
// If the given (index, term) matches with the existing log:
// 1. If an existing entry conflicts with a new one (same index
// but different terms), delete the existing entry and all that
// follow it
// 2.Append any new entries not already in the log
// If the given (index, term) does not match with the existing log:
// return false
func TestLogMaybeAppend(t *testing.T) {
previousEnts := []pb.Entry{{Term: 1}, {Term: 2}, {Term: 3}}
lastindex := uint64(3)
lastterm := uint64(3)
commit := uint64(1)
2014-10-29 01:06:21 +03:00
tests := []struct {
logTerm uint64
index uint64
committed uint64
ents []pb.Entry
wlasti uint64
wappend bool
wcommit uint64
wpanic bool
2014-10-29 01:06:21 +03:00
}{
// not match: term is different
{
lastterm - 1, lastindex, lastindex, []pb.Entry{{Term: 4}},
0, false, commit, false,
2014-10-29 01:06:21 +03:00
},
// not match: index out of bound
{
lastterm, lastindex + 1, lastindex, []pb.Entry{{Term: 4}},
0, false, commit, false,
2014-10-29 01:06:21 +03:00
},
// match with the last existing entry
{
lastterm, lastindex, lastindex, nil,
lastindex, true, lastindex, false,
2014-10-29 01:06:21 +03:00
},
{
lastterm, lastindex, lastindex + 1, nil,
lastindex, true, lastindex, false, // do not increase commit higher than lastnewi
2014-10-29 01:06:21 +03:00
},
{
lastterm, lastindex, lastindex - 1, nil,
lastindex, true, lastindex - 1, false, // commit up to the commit in the message
},
{
lastterm, lastindex, 0, nil,
lastindex, true, commit, false, // commit do not decrease
},
{
0, 0, lastindex, nil,
0, true, commit, false, // commit do not decrease
},
2014-10-29 01:06:21 +03:00
{
lastterm, lastindex, lastindex, []pb.Entry{{Term: 4}},
lastindex + 1, true, lastindex, false,
2014-10-29 01:06:21 +03:00
},
{
lastterm, lastindex, lastindex + 1, []pb.Entry{{Term: 4}},
lastindex + 1, true, lastindex + 1, false,
2014-10-29 01:06:21 +03:00
},
{
lastterm, lastindex, lastindex + 2, []pb.Entry{{Term: 4}},
lastindex + 1, true, lastindex + 1, false, // do not increase commit higher than lastnewi
2014-10-29 01:06:21 +03:00
},
{
lastterm, lastindex, lastindex + 2, []pb.Entry{{Term: 4}, {Term: 4}},
lastindex + 2, true, lastindex + 2, false,
2014-10-29 01:06:21 +03:00
},
// match with the the entry in the middle
{
lastterm - 1, lastindex - 1, lastindex, []pb.Entry{{Term: 4}},
lastindex, true, lastindex, false,
2014-10-29 01:06:21 +03:00
},
{
lastterm - 2, lastindex - 2, lastindex, []pb.Entry{{Term: 4}},
lastindex - 1, true, lastindex - 1, false,
2014-10-29 01:06:21 +03:00
},
{
lastterm - 3, lastindex - 3, lastindex, []pb.Entry{{Term: 4}},
lastindex - 2, true, lastindex - 2, true, // conflict with existing committed entry
2014-10-29 01:06:21 +03:00
},
{
lastterm - 2, lastindex - 2, lastindex, []pb.Entry{{Term: 4}, {Term: 4}},
lastindex, true, lastindex, false,
2014-10-29 01:06:21 +03:00
},
}
for i, tt := range tests {
raftLog := newLog()
raftLog.append(raftLog.lastIndex(), previousEnts...)
raftLog.committed = commit
func() {
defer func() {
if r := recover(); r != nil {
if tt.wpanic != true {
t.Errorf("%d: panic = %v, want %v", i, true, tt.wpanic)
}
}
}()
glasti, gappend := raftLog.maybeAppend(tt.index, tt.logTerm, tt.committed, tt.ents...)
gcommit := raftLog.committed
2014-10-29 01:06:21 +03:00
if glasti != tt.wlasti {
t.Errorf("#%d: lastindex = %d, want %d", i, glasti, tt.wlasti)
2014-10-29 01:06:21 +03:00
}
if gappend != tt.wappend {
t.Errorf("#%d: append = %v, want %v", i, gappend, tt.wappend)
}
if gcommit != tt.wcommit {
t.Errorf("#%d: committed = %d, want %d", i, gcommit, tt.wcommit)
}
if gappend {
gents := raftLog.slice(raftLog.lastIndex()-uint64(len(tt.ents))+1, raftLog.lastIndex()+1)
if !reflect.DeepEqual(tt.ents, gents) {
t.Errorf("%d: appended entries = %v, want %v", i, gents, tt.ents)
}
}
}()
2014-10-29 01:06:21 +03:00
}
}
2014-07-01 02:15:51 +04:00
// TestCompactionSideEffects ensures that all the log related funcationality works correctly after
// a compaction.
func TestCompactionSideEffects(t *testing.T) {
2014-10-08 14:29:53 +04:00
var i uint64
lastIndex := uint64(1000)
lastTerm := lastIndex
2014-07-24 03:15:25 +04:00
raftLog := newLog()
2014-07-01 02:15:51 +04:00
2014-07-11 09:51:37 +04:00
for i = 0; i < lastIndex; i++ {
2014-10-08 14:29:53 +04:00
raftLog.append(uint64(i), pb.Entry{Term: uint64(i + 1), Index: uint64(i + 1)})
2014-07-01 02:15:51 +04:00
}
raftLog.maybeCommit(lastIndex, lastTerm)
raftLog.appliedTo(raftLog.committed)
2014-07-01 02:15:51 +04:00
2014-07-24 03:15:25 +04:00
raftLog.compact(500)
2014-07-01 02:15:51 +04:00
2014-07-24 03:15:25 +04:00
if raftLog.lastIndex() != lastIndex {
t.Errorf("lastIndex = %d, want %d", raftLog.lastIndex(), lastIndex)
2014-07-01 02:15:51 +04:00
}
2014-07-24 03:15:25 +04:00
for i := raftLog.offset; i <= raftLog.lastIndex(); i++ {
if raftLog.term(i) != i {
t.Errorf("term(%d) = %d, want %d", i, raftLog.term(i), i)
2014-07-01 02:15:51 +04:00
}
}
2014-07-24 03:15:25 +04:00
for i := raftLog.offset; i <= raftLog.lastIndex(); i++ {
if !raftLog.matchTerm(i, i) {
2014-07-01 02:15:51 +04:00
t.Errorf("matchTerm(%d) = false, want true", i)
}
}
2014-08-02 08:43:08 +04:00
unstableEnts := raftLog.unstableEnts()
if g := len(unstableEnts); g != 500 {
t.Errorf("len(unstableEntries) = %d, want = %d", g, 500)
}
2014-08-02 08:43:08 +04:00
if unstableEnts[0].Index != 501 {
t.Errorf("Index = %d, want = %d", unstableEnts[0].Index, 501)
}
2014-07-24 03:15:25 +04:00
prev := raftLog.lastIndex()
2014-08-28 05:53:18 +04:00
raftLog.append(raftLog.lastIndex(), pb.Entry{Term: raftLog.lastIndex() + 1})
2014-07-24 03:15:25 +04:00
if raftLog.lastIndex() != prev+1 {
t.Errorf("lastIndex = %d, want = %d", raftLog.lastIndex(), prev+1)
2014-07-01 02:15:51 +04:00
}
2014-07-24 03:15:25 +04:00
ents := raftLog.entries(raftLog.lastIndex())
2014-07-01 02:15:51 +04:00
if len(ents) != 1 {
t.Errorf("len(entries) = %d, want = %d", len(ents), 1)
}
}
func TestNextEnts(t *testing.T) {
snap := pb.Snapshot{Term: 1, Index: 3}
ents := []pb.Entry{
{Term: 1, Index: 3},
{Term: 1, Index: 4},
{Term: 1, Index: 5},
{Term: 1, Index: 6},
}
tests := []struct {
applied uint64
wents []pb.Entry
}{
{0, ents[1:3]},
{3, ents[1:3]},
{4, ents[2:3]},
{5, nil},
}
for i, tt := range tests {
raftLog := newLog()
raftLog.restore(snap)
raftLog.load(ents)
raftLog.maybeCommit(5, 1)
raftLog.appliedTo(tt.applied)
ents := raftLog.nextEnts()
if !reflect.DeepEqual(ents, tt.wents) {
t.Errorf("#%d: ents = %+v, want %+v", i, ents, tt.wents)
}
}
}
func TestUnstableEnts(t *testing.T) {
2014-08-28 05:53:18 +04:00
previousEnts := []pb.Entry{{Term: 1, Index: 1}, {Term: 2, Index: 2}}
tests := []struct {
2014-10-08 14:29:53 +04:00
unstable uint64
2014-08-28 05:53:18 +04:00
wents []pb.Entry
2014-10-08 14:29:53 +04:00
wunstable uint64
}{
2014-08-02 08:43:08 +04:00
{3, nil, 3},
{1, previousEnts, 3},
{0, append([]pb.Entry{{}}, previousEnts...), 3},
}
for i, tt := range tests {
raftLog := newLog()
2014-10-27 21:09:44 +03:00
raftLog.append(0, previousEnts...)
raftLog.unstable = tt.unstable
2014-08-02 08:43:08 +04:00
ents := raftLog.unstableEnts()
if l := len(ents); l > 0 {
raftLog.stableTo(ents[l-1].Index)
}
if !reflect.DeepEqual(ents, tt.wents) {
t.Errorf("#%d: unstableEnts = %+v, want %+v", i, ents, tt.wents)
}
if g := raftLog.unstable; g != tt.wunstable {
t.Errorf("#%d: unstable = %d, want %d", i, g, tt.wunstable)
}
}
}
func TestStableTo(t *testing.T) {
tests := []struct {
stable uint64
wunstable uint64
}{
{0, 1},
{1, 2},
{2, 3},
}
for i, tt := range tests {
raftLog := newLog()
raftLog.stableTo(tt.stable)
if raftLog.unstable != tt.wunstable {
t.Errorf("#%d: unstable = %d, want %d", i, raftLog.unstable, tt.wunstable)
}
}
}
2014-07-01 02:15:51 +04:00
//TestCompaction ensures that the number of log entreis is correct after compactions.
func TestCompaction(t *testing.T) {
tests := []struct {
2014-10-08 14:29:53 +04:00
applied uint64
lastIndex uint64
compact []uint64
wleft []int
wallow bool
2014-07-01 02:15:51 +04:00
}{
// out of upper bound
2014-10-08 14:29:53 +04:00
{1000, 1000, []uint64{1001}, []int{-1}, false},
{1000, 1000, []uint64{300, 500, 800, 900}, []int{701, 501, 201, 101}, true},
2014-07-01 02:15:51 +04:00
// out of lower bound
2014-10-08 14:29:53 +04:00
{1000, 1000, []uint64{300, 299}, []int{701, -1}, false},
{0, 1000, []uint64{1}, []int{-1}, false},
2014-07-01 02:15:51 +04:00
}
for i, tt := range tests {
func() {
defer func() {
if r := recover(); r != nil {
if tt.wallow == true {
t.Errorf("%d: allow = %v, want %v", i, false, true)
}
}
}()
2014-07-24 03:15:25 +04:00
raftLog := newLog()
2014-10-08 14:29:53 +04:00
for i := uint64(0); i < tt.lastIndex; i++ {
raftLog.append(uint64(i), pb.Entry{})
2014-07-01 02:15:51 +04:00
}
raftLog.maybeCommit(tt.applied, 0)
raftLog.appliedTo(raftLog.committed)
2014-07-01 02:15:51 +04:00
for j := 0; j < len(tt.compact); j++ {
2014-07-24 03:15:25 +04:00
raftLog.compact(tt.compact[j])
if len(raftLog.ents) != tt.wleft[j] {
t.Errorf("#%d.%d len = %d, want %d", i, j, len(raftLog.ents), tt.wleft[j])
2014-07-01 02:15:51 +04:00
}
}
}()
}
}
2014-07-01 23:10:43 +04:00
func TestLogRestore(t *testing.T) {
2014-10-08 14:29:53 +04:00
var i uint64
2014-07-24 03:15:25 +04:00
raftLog := newLog()
2014-07-11 09:51:37 +04:00
for i = 0; i < 100; i++ {
2014-08-28 05:53:18 +04:00
raftLog.append(i, pb.Entry{Term: i + 1})
2014-07-01 23:10:43 +04:00
}
2014-10-08 14:29:53 +04:00
index := uint64(1000)
term := uint64(1000)
2014-08-28 05:53:18 +04:00
raftLog.restore(pb.Snapshot{Index: index, Term: term})
2014-07-01 23:10:43 +04:00
// only has the guard entry
2014-07-24 03:15:25 +04:00
if len(raftLog.ents) != 1 {
t.Errorf("len = %d, want 0", len(raftLog.ents))
2014-07-01 23:10:43 +04:00
}
2014-07-24 03:15:25 +04:00
if raftLog.offset != index {
t.Errorf("offset = %d, want %d", raftLog.offset, index)
2014-07-01 23:10:43 +04:00
}
2014-07-24 03:15:25 +04:00
if raftLog.committed != index {
t.Errorf("comitted = %d, want %d", raftLog.committed, index)
2014-07-01 23:10:43 +04:00
}
if raftLog.unstable != index+1 {
t.Errorf("unstable = %d, want %d", raftLog.unstable, index+1)
}
2014-07-24 03:15:25 +04:00
if raftLog.term(index) != term {
t.Errorf("term = %d, want %d", raftLog.term(index), term)
2014-07-01 23:10:43 +04:00
}
}
2014-06-28 03:15:04 +04:00
func TestIsOutOfBounds(t *testing.T) {
2014-10-08 14:29:53 +04:00
offset := uint64(100)
num := uint64(100)
2014-08-28 05:53:18 +04:00
l := &raftLog{offset: offset, ents: make([]pb.Entry, num)}
2014-06-28 03:15:04 +04:00
tests := []struct {
2014-10-08 14:29:53 +04:00
index uint64
2014-06-28 03:15:04 +04:00
w bool
}{
{offset - 1, true},
{offset, false},
{offset + num/2, false},
{offset + num - 1, false},
{offset + num, true},
}
for i, tt := range tests {
g := l.isOutOfBounds(tt.index)
if g != tt.w {
t.Errorf("#%d: isOutOfBounds = %v, want %v", i, g, tt.w)
}
}
}
func TestAt(t *testing.T) {
2014-10-08 14:29:53 +04:00
var i uint64
offset := uint64(100)
num := uint64(100)
2014-06-28 03:15:04 +04:00
2014-07-24 03:15:25 +04:00
l := &raftLog{offset: offset}
2014-07-11 09:51:37 +04:00
for i = 0; i < num; i++ {
2014-08-28 05:53:18 +04:00
l.ents = append(l.ents, pb.Entry{Term: i})
2014-06-28 03:15:04 +04:00
}
tests := []struct {
2014-10-08 14:29:53 +04:00
index uint64
2014-08-28 05:53:18 +04:00
w *pb.Entry
2014-06-28 03:15:04 +04:00
}{
{offset - 1, nil},
2014-08-28 05:53:18 +04:00
{offset, &pb.Entry{Term: 0}},
{offset + num/2, &pb.Entry{Term: num / 2}},
{offset + num - 1, &pb.Entry{Term: num - 1}},
2014-06-28 03:15:04 +04:00
{offset + num, nil},
}
for i, tt := range tests {
g := l.at(tt.index)
if !reflect.DeepEqual(g, tt.w) {
t.Errorf("#%d: at = %v, want %v", i, g, tt.w)
}
}
}
func TestSlice(t *testing.T) {
2014-10-08 14:29:53 +04:00
var i uint64
offset := uint64(100)
num := uint64(100)
2014-06-28 03:15:04 +04:00
2014-07-24 03:15:25 +04:00
l := &raftLog{offset: offset}
2014-07-11 09:51:37 +04:00
for i = 0; i < num; i++ {
2014-08-28 05:53:18 +04:00
l.ents = append(l.ents, pb.Entry{Term: i})
2014-06-28 03:15:04 +04:00
}
tests := []struct {
2014-10-08 14:29:53 +04:00
from uint64
to uint64
2014-08-28 05:53:18 +04:00
w []pb.Entry
2014-06-28 03:15:04 +04:00
}{
{offset - 1, offset + 1, nil},
2014-08-28 05:53:18 +04:00
{offset, offset + 1, []pb.Entry{{Term: 0}}},
{offset + num/2, offset + num/2 + 1, []pb.Entry{{Term: num / 2}}},
{offset + num - 1, offset + num, []pb.Entry{{Term: num - 1}}},
2014-06-28 03:15:04 +04:00
{offset + num, offset + num + 1, nil},
{offset + num/2, offset + num/2, nil},
{offset + num/2, offset + num/2 - 1, nil},
}
for i, tt := range tests {
g := l.slice(tt.from, tt.to)
if !reflect.DeepEqual(g, tt.w) {
t.Errorf("#%d: from %d to %d = %v, want %v", i, tt.from, tt.to, g, tt.w)
}
}
}