Browse Source

raft: add offset for log

Xiang Li 11 years ago
parent
commit
6a232dfc13
2 changed files with 127 additions and 14 deletions
  1. 37 14
      raft/log.go
  2. 90 0
      raft/log_test.go

+ 37 - 14
raft/log.go

@@ -21,6 +21,7 @@ type log struct {
 	ents      []Entry
 	committed int
 	applied   int
+	offset    int
 }
 
 func newLog() *log {
@@ -41,38 +42,35 @@ func (l *log) maybeAppend(index, logTerm, committed int, ents ...Entry) bool {
 }
 
 func (l *log) append(after int, ents ...Entry) int {
-	l.ents = append(l.ents[:after+1], ents...)
+	l.ents = append(l.slice(0, after+1), ents...)
 	return l.lastIndex()
 }
 
 func (l *log) lastIndex() int {
-	return len(l.ents) - 1
+	return len(l.ents) - 1 + l.offset
 }
 
 func (l *log) term(i int) int {
-	if i > l.lastIndex() {
-		return -1
+	if e := l.at(i); e != nil {
+		return e.Term
 	}
-	return l.ents[i].Term
+	return -1
 }
 
 func (l *log) entries(i int) []Entry {
-	if i > l.lastIndex() {
-		return nil
-	}
-	return l.ents[i:]
+	return l.slice(i, l.lastIndex()+1)
 }
 
 func (l *log) isUpToDate(i, term int) bool {
-	e := l.ents[l.lastIndex()]
+	e := l.at(l.lastIndex())
 	return term > e.Term || (term == e.Term && i >= l.lastIndex())
 }
 
 func (l *log) matchTerm(i, term int) bool {
-	if i > l.lastIndex() {
-		return false
+	if e := l.at(i); e != nil {
+		return e.Term == term
 	}
-	return l.ents[i].Term == term
+	return false
 }
 
 func (l *log) maybeCommit(maxIndex, term int) bool {
@@ -87,8 +85,33 @@ func (l *log) maybeCommit(maxIndex, term int) bool {
 // all the returned entries will be marked as applied.
 func (l *log) nextEnts() (ents []Entry) {
 	if l.committed > l.applied {
-		ents = l.ents[l.applied+1 : l.committed+1]
+		ents = l.slice(l.applied+1, l.committed+1)
 		l.applied = l.committed
 	}
 	return ents
 }
+
+func (l *log) at(i int) *Entry {
+	if l.isOutOfBounds(i) {
+		return nil
+	}
+	return &l.ents[i-l.offset]
+}
+
+// slice get a slice of log entries from lo through hi-1, inclusive.
+func (l *log) slice(lo int, hi int) []Entry {
+	if lo >= hi {
+		return nil
+	}
+	if l.isOutOfBounds(lo) || l.isOutOfBounds(hi-1) {
+		return nil
+	}
+	return l.ents[lo-l.offset : hi-l.offset]
+}
+
+func (l *log) isOutOfBounds(i int) bool {
+	if i < l.offset || i > l.lastIndex() {
+		return true
+	}
+	return false
+}

+ 90 - 0
raft/log_test.go

@@ -0,0 +1,90 @@
+package raft
+
+import (
+	"reflect"
+	"testing"
+)
+
+func TestIsOutOfBounds(t *testing.T) {
+	offset := 100
+	num := 100
+	l := &log{offset: offset, ents: make([]Entry, num)}
+
+	tests := []struct {
+		index int
+		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) {
+	offset := 100
+	num := 100
+
+	l := &log{offset: offset}
+	for i := 0; i < num; i++ {
+		l.ents = append(l.ents, Entry{Term: i})
+	}
+
+	tests := []struct {
+		index int
+		w     *Entry
+	}{
+		{offset - 1, nil},
+		{offset, &Entry{Term: 0}},
+		{offset + num/2, &Entry{Term: num / 2}},
+		{offset + num - 1, &Entry{Term: num - 1}},
+		{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) {
+	offset := 100
+	num := 100
+
+	l := &log{offset: offset}
+	for i := 0; i < num; i++ {
+		l.ents = append(l.ents, Entry{Term: i})
+	}
+
+	tests := []struct {
+		from int
+		to   int
+		w    []Entry
+	}{
+		{offset - 1, offset + 1, nil},
+		{offset, offset + 1, []Entry{{Term: 0}}},
+		{offset + num/2, offset + num/2 + 1, []Entry{{Term: num / 2}}},
+		{offset + num - 1, offset + num, []Entry{{Term: num - 1}}},
+		{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)
+		}
+	}
+}