go.etcd.io/etcd@v3.3.27+incompatible/raft/log_test.go (about)

     1  // Copyright 2015 The etcd Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package raft
    16  
    17  import (
    18  	"reflect"
    19  	"testing"
    20  
    21  	pb "github.com/coreos/etcd/raft/raftpb"
    22  )
    23  
    24  func TestFindConflict(t *testing.T) {
    25  	previousEnts := []pb.Entry{{Index: 1, Term: 1}, {Index: 2, Term: 2}, {Index: 3, Term: 3}}
    26  	tests := []struct {
    27  		ents      []pb.Entry
    28  		wconflict uint64
    29  	}{
    30  		// no conflict, empty ent
    31  		{[]pb.Entry{}, 0},
    32  		// no conflict
    33  		{[]pb.Entry{{Index: 1, Term: 1}, {Index: 2, Term: 2}, {Index: 3, Term: 3}}, 0},
    34  		{[]pb.Entry{{Index: 2, Term: 2}, {Index: 3, Term: 3}}, 0},
    35  		{[]pb.Entry{{Index: 3, Term: 3}}, 0},
    36  		// no conflict, but has new entries
    37  		{[]pb.Entry{{Index: 1, Term: 1}, {Index: 2, Term: 2}, {Index: 3, Term: 3}, {Index: 4, Term: 4}, {Index: 5, Term: 4}}, 4},
    38  		{[]pb.Entry{{Index: 2, Term: 2}, {Index: 3, Term: 3}, {Index: 4, Term: 4}, {Index: 5, Term: 4}}, 4},
    39  		{[]pb.Entry{{Index: 3, Term: 3}, {Index: 4, Term: 4}, {Index: 5, Term: 4}}, 4},
    40  		{[]pb.Entry{{Index: 4, Term: 4}, {Index: 5, Term: 4}}, 4},
    41  		// conflicts with existing entries
    42  		{[]pb.Entry{{Index: 1, Term: 4}, {Index: 2, Term: 4}}, 1},
    43  		{[]pb.Entry{{Index: 2, Term: 1}, {Index: 3, Term: 4}, {Index: 4, Term: 4}}, 2},
    44  		{[]pb.Entry{{Index: 3, Term: 1}, {Index: 4, Term: 2}, {Index: 5, Term: 4}, {Index: 6, Term: 4}}, 3},
    45  	}
    46  
    47  	for i, tt := range tests {
    48  		raftLog := newLog(NewMemoryStorage(), raftLogger)
    49  		raftLog.append(previousEnts...)
    50  
    51  		gconflict := raftLog.findConflict(tt.ents)
    52  		if gconflict != tt.wconflict {
    53  			t.Errorf("#%d: conflict = %d, want %d", i, gconflict, tt.wconflict)
    54  		}
    55  	}
    56  }
    57  
    58  func TestIsUpToDate(t *testing.T) {
    59  	previousEnts := []pb.Entry{{Index: 1, Term: 1}, {Index: 2, Term: 2}, {Index: 3, Term: 3}}
    60  	raftLog := newLog(NewMemoryStorage(), raftLogger)
    61  	raftLog.append(previousEnts...)
    62  	tests := []struct {
    63  		lastIndex uint64
    64  		term      uint64
    65  		wUpToDate bool
    66  	}{
    67  		// greater term, ignore lastIndex
    68  		{raftLog.lastIndex() - 1, 4, true},
    69  		{raftLog.lastIndex(), 4, true},
    70  		{raftLog.lastIndex() + 1, 4, true},
    71  		// smaller term, ignore lastIndex
    72  		{raftLog.lastIndex() - 1, 2, false},
    73  		{raftLog.lastIndex(), 2, false},
    74  		{raftLog.lastIndex() + 1, 2, false},
    75  		// equal term, equal or lager lastIndex wins
    76  		{raftLog.lastIndex() - 1, 3, false},
    77  		{raftLog.lastIndex(), 3, true},
    78  		{raftLog.lastIndex() + 1, 3, true},
    79  	}
    80  
    81  	for i, tt := range tests {
    82  		gUpToDate := raftLog.isUpToDate(tt.lastIndex, tt.term)
    83  		if gUpToDate != tt.wUpToDate {
    84  			t.Errorf("#%d: uptodate = %v, want %v", i, gUpToDate, tt.wUpToDate)
    85  		}
    86  	}
    87  }
    88  
    89  func TestAppend(t *testing.T) {
    90  	previousEnts := []pb.Entry{{Index: 1, Term: 1}, {Index: 2, Term: 2}}
    91  	tests := []struct {
    92  		ents      []pb.Entry
    93  		windex    uint64
    94  		wents     []pb.Entry
    95  		wunstable uint64
    96  	}{
    97  		{
    98  			[]pb.Entry{},
    99  			2,
   100  			[]pb.Entry{{Index: 1, Term: 1}, {Index: 2, Term: 2}},
   101  			3,
   102  		},
   103  		{
   104  			[]pb.Entry{{Index: 3, Term: 2}},
   105  			3,
   106  			[]pb.Entry{{Index: 1, Term: 1}, {Index: 2, Term: 2}, {Index: 3, Term: 2}},
   107  			3,
   108  		},
   109  		// conflicts with index 1
   110  		{
   111  			[]pb.Entry{{Index: 1, Term: 2}},
   112  			1,
   113  			[]pb.Entry{{Index: 1, Term: 2}},
   114  			1,
   115  		},
   116  		// conflicts with index 2
   117  		{
   118  			[]pb.Entry{{Index: 2, Term: 3}, {Index: 3, Term: 3}},
   119  			3,
   120  			[]pb.Entry{{Index: 1, Term: 1}, {Index: 2, Term: 3}, {Index: 3, Term: 3}},
   121  			2,
   122  		},
   123  	}
   124  
   125  	for i, tt := range tests {
   126  		storage := NewMemoryStorage()
   127  		storage.Append(previousEnts)
   128  		raftLog := newLog(storage, raftLogger)
   129  
   130  		index := raftLog.append(tt.ents...)
   131  		if index != tt.windex {
   132  			t.Errorf("#%d: lastIndex = %d, want %d", i, index, tt.windex)
   133  		}
   134  		g, err := raftLog.entries(1, noLimit)
   135  		if err != nil {
   136  			t.Fatalf("#%d: unexpected error %v", i, err)
   137  		}
   138  		if !reflect.DeepEqual(g, tt.wents) {
   139  			t.Errorf("#%d: logEnts = %+v, want %+v", i, g, tt.wents)
   140  		}
   141  		if goff := raftLog.unstable.offset; goff != tt.wunstable {
   142  			t.Errorf("#%d: unstable = %d, want %d", i, goff, tt.wunstable)
   143  		}
   144  	}
   145  }
   146  
   147  // TestLogMaybeAppend ensures:
   148  // If the given (index, term) matches with the existing log:
   149  // 	1. If an existing entry conflicts with a new one (same index
   150  // 	but different terms), delete the existing entry and all that
   151  // 	follow it
   152  // 	2.Append any new entries not already in the log
   153  // If the given (index, term) does not match with the existing log:
   154  // 	return false
   155  func TestLogMaybeAppend(t *testing.T) {
   156  	previousEnts := []pb.Entry{{Index: 1, Term: 1}, {Index: 2, Term: 2}, {Index: 3, Term: 3}}
   157  	lastindex := uint64(3)
   158  	lastterm := uint64(3)
   159  	commit := uint64(1)
   160  
   161  	tests := []struct {
   162  		logTerm   uint64
   163  		index     uint64
   164  		committed uint64
   165  		ents      []pb.Entry
   166  
   167  		wlasti  uint64
   168  		wappend bool
   169  		wcommit uint64
   170  		wpanic  bool
   171  	}{
   172  		// not match: term is different
   173  		{
   174  			lastterm - 1, lastindex, lastindex, []pb.Entry{{Index: lastindex + 1, Term: 4}},
   175  			0, false, commit, false,
   176  		},
   177  		// not match: index out of bound
   178  		{
   179  			lastterm, lastindex + 1, lastindex, []pb.Entry{{Index: lastindex + 2, Term: 4}},
   180  			0, false, commit, false,
   181  		},
   182  		// match with the last existing entry
   183  		{
   184  			lastterm, lastindex, lastindex, nil,
   185  			lastindex, true, lastindex, false,
   186  		},
   187  		{
   188  			lastterm, lastindex, lastindex + 1, nil,
   189  			lastindex, true, lastindex, false, // do not increase commit higher than lastnewi
   190  		},
   191  		{
   192  			lastterm, lastindex, lastindex - 1, nil,
   193  			lastindex, true, lastindex - 1, false, // commit up to the commit in the message
   194  		},
   195  		{
   196  			lastterm, lastindex, 0, nil,
   197  			lastindex, true, commit, false, // commit do not decrease
   198  		},
   199  		{
   200  			0, 0, lastindex, nil,
   201  			0, true, commit, false, // commit do not decrease
   202  		},
   203  		{
   204  			lastterm, lastindex, lastindex, []pb.Entry{{Index: lastindex + 1, Term: 4}},
   205  			lastindex + 1, true, lastindex, false,
   206  		},
   207  		{
   208  			lastterm, lastindex, lastindex + 1, []pb.Entry{{Index: lastindex + 1, Term: 4}},
   209  			lastindex + 1, true, lastindex + 1, false,
   210  		},
   211  		{
   212  			lastterm, lastindex, lastindex + 2, []pb.Entry{{Index: lastindex + 1, Term: 4}},
   213  			lastindex + 1, true, lastindex + 1, false, // do not increase commit higher than lastnewi
   214  		},
   215  		{
   216  			lastterm, lastindex, lastindex + 2, []pb.Entry{{Index: lastindex + 1, Term: 4}, {Index: lastindex + 2, Term: 4}},
   217  			lastindex + 2, true, lastindex + 2, false,
   218  		},
   219  		// match with the the entry in the middle
   220  		{
   221  			lastterm - 1, lastindex - 1, lastindex, []pb.Entry{{Index: lastindex, Term: 4}},
   222  			lastindex, true, lastindex, false,
   223  		},
   224  		{
   225  			lastterm - 2, lastindex - 2, lastindex, []pb.Entry{{Index: lastindex - 1, Term: 4}},
   226  			lastindex - 1, true, lastindex - 1, false,
   227  		},
   228  		{
   229  			lastterm - 3, lastindex - 3, lastindex, []pb.Entry{{Index: lastindex - 2, Term: 4}},
   230  			lastindex - 2, true, lastindex - 2, true, // conflict with existing committed entry
   231  		},
   232  		{
   233  			lastterm - 2, lastindex - 2, lastindex, []pb.Entry{{Index: lastindex - 1, Term: 4}, {Index: lastindex, Term: 4}},
   234  			lastindex, true, lastindex, false,
   235  		},
   236  	}
   237  
   238  	for i, tt := range tests {
   239  		raftLog := newLog(NewMemoryStorage(), raftLogger)
   240  		raftLog.append(previousEnts...)
   241  		raftLog.committed = commit
   242  		func() {
   243  			defer func() {
   244  				if r := recover(); r != nil {
   245  					if !tt.wpanic {
   246  						t.Errorf("%d: panic = %v, want %v", i, true, tt.wpanic)
   247  					}
   248  				}
   249  			}()
   250  			glasti, gappend := raftLog.maybeAppend(tt.index, tt.logTerm, tt.committed, tt.ents...)
   251  			gcommit := raftLog.committed
   252  
   253  			if glasti != tt.wlasti {
   254  				t.Errorf("#%d: lastindex = %d, want %d", i, glasti, tt.wlasti)
   255  			}
   256  			if gappend != tt.wappend {
   257  				t.Errorf("#%d: append = %v, want %v", i, gappend, tt.wappend)
   258  			}
   259  			if gcommit != tt.wcommit {
   260  				t.Errorf("#%d: committed = %d, want %d", i, gcommit, tt.wcommit)
   261  			}
   262  			if gappend && len(tt.ents) != 0 {
   263  				gents, err := raftLog.slice(raftLog.lastIndex()-uint64(len(tt.ents))+1, raftLog.lastIndex()+1, noLimit)
   264  				if err != nil {
   265  					t.Fatalf("unexpected error %v", err)
   266  				}
   267  				if !reflect.DeepEqual(tt.ents, gents) {
   268  					t.Errorf("%d: appended entries = %v, want %v", i, gents, tt.ents)
   269  				}
   270  			}
   271  		}()
   272  	}
   273  }
   274  
   275  // TestCompactionSideEffects ensures that all the log related functionality works correctly after
   276  // a compaction.
   277  func TestCompactionSideEffects(t *testing.T) {
   278  	var i uint64
   279  	// Populate the log with 1000 entries; 750 in stable storage and 250 in unstable.
   280  	lastIndex := uint64(1000)
   281  	unstableIndex := uint64(750)
   282  	lastTerm := lastIndex
   283  	storage := NewMemoryStorage()
   284  	for i = 1; i <= unstableIndex; i++ {
   285  		storage.Append([]pb.Entry{{Term: uint64(i), Index: uint64(i)}})
   286  	}
   287  	raftLog := newLog(storage, raftLogger)
   288  	for i = unstableIndex; i < lastIndex; i++ {
   289  		raftLog.append(pb.Entry{Term: uint64(i + 1), Index: uint64(i + 1)})
   290  	}
   291  
   292  	ok := raftLog.maybeCommit(lastIndex, lastTerm)
   293  	if !ok {
   294  		t.Fatalf("maybeCommit returned false")
   295  	}
   296  	raftLog.appliedTo(raftLog.committed)
   297  
   298  	offset := uint64(500)
   299  	storage.Compact(offset)
   300  
   301  	if raftLog.lastIndex() != lastIndex {
   302  		t.Errorf("lastIndex = %d, want %d", raftLog.lastIndex(), lastIndex)
   303  	}
   304  
   305  	for j := offset; j <= raftLog.lastIndex(); j++ {
   306  		if mustTerm(raftLog.term(j)) != j {
   307  			t.Errorf("term(%d) = %d, want %d", j, mustTerm(raftLog.term(j)), j)
   308  		}
   309  	}
   310  
   311  	for j := offset; j <= raftLog.lastIndex(); j++ {
   312  		if !raftLog.matchTerm(j, j) {
   313  			t.Errorf("matchTerm(%d) = false, want true", j)
   314  		}
   315  	}
   316  
   317  	unstableEnts := raftLog.unstableEntries()
   318  	if g := len(unstableEnts); g != 250 {
   319  		t.Errorf("len(unstableEntries) = %d, want = %d", g, 250)
   320  	}
   321  	if unstableEnts[0].Index != 751 {
   322  		t.Errorf("Index = %d, want = %d", unstableEnts[0].Index, 751)
   323  	}
   324  
   325  	prev := raftLog.lastIndex()
   326  	raftLog.append(pb.Entry{Index: raftLog.lastIndex() + 1, Term: raftLog.lastIndex() + 1})
   327  	if raftLog.lastIndex() != prev+1 {
   328  		t.Errorf("lastIndex = %d, want = %d", raftLog.lastIndex(), prev+1)
   329  	}
   330  
   331  	ents, err := raftLog.entries(raftLog.lastIndex(), noLimit)
   332  	if err != nil {
   333  		t.Fatalf("unexpected error %v", err)
   334  	}
   335  	if len(ents) != 1 {
   336  		t.Errorf("len(entries) = %d, want = %d", len(ents), 1)
   337  	}
   338  }
   339  
   340  func TestHasNextEnts(t *testing.T) {
   341  	snap := pb.Snapshot{
   342  		Metadata: pb.SnapshotMetadata{Term: 1, Index: 3},
   343  	}
   344  	ents := []pb.Entry{
   345  		{Term: 1, Index: 4},
   346  		{Term: 1, Index: 5},
   347  		{Term: 1, Index: 6},
   348  	}
   349  	tests := []struct {
   350  		applied uint64
   351  		hasNext bool
   352  	}{
   353  		{0, true},
   354  		{3, true},
   355  		{4, true},
   356  		{5, false},
   357  	}
   358  	for i, tt := range tests {
   359  		storage := NewMemoryStorage()
   360  		storage.ApplySnapshot(snap)
   361  		raftLog := newLog(storage, raftLogger)
   362  		raftLog.append(ents...)
   363  		raftLog.maybeCommit(5, 1)
   364  		raftLog.appliedTo(tt.applied)
   365  
   366  		hasNext := raftLog.hasNextEnts()
   367  		if hasNext != tt.hasNext {
   368  			t.Errorf("#%d: hasNext = %v, want %v", i, hasNext, tt.hasNext)
   369  		}
   370  	}
   371  }
   372  
   373  func TestNextEnts(t *testing.T) {
   374  	snap := pb.Snapshot{
   375  		Metadata: pb.SnapshotMetadata{Term: 1, Index: 3},
   376  	}
   377  	ents := []pb.Entry{
   378  		{Term: 1, Index: 4},
   379  		{Term: 1, Index: 5},
   380  		{Term: 1, Index: 6},
   381  	}
   382  	tests := []struct {
   383  		applied uint64
   384  		wents   []pb.Entry
   385  	}{
   386  		{0, ents[:2]},
   387  		{3, ents[:2]},
   388  		{4, ents[1:2]},
   389  		{5, nil},
   390  	}
   391  	for i, tt := range tests {
   392  		storage := NewMemoryStorage()
   393  		storage.ApplySnapshot(snap)
   394  		raftLog := newLog(storage, raftLogger)
   395  		raftLog.append(ents...)
   396  		raftLog.maybeCommit(5, 1)
   397  		raftLog.appliedTo(tt.applied)
   398  
   399  		nents := raftLog.nextEnts()
   400  		if !reflect.DeepEqual(nents, tt.wents) {
   401  			t.Errorf("#%d: nents = %+v, want %+v", i, nents, tt.wents)
   402  		}
   403  	}
   404  }
   405  
   406  // TestUnstableEnts ensures unstableEntries returns the unstable part of the
   407  // entries correctly.
   408  func TestUnstableEnts(t *testing.T) {
   409  	previousEnts := []pb.Entry{{Term: 1, Index: 1}, {Term: 2, Index: 2}}
   410  	tests := []struct {
   411  		unstable uint64
   412  		wents    []pb.Entry
   413  	}{
   414  		{3, nil},
   415  		{1, previousEnts},
   416  	}
   417  
   418  	for i, tt := range tests {
   419  		// append stable entries to storage
   420  		storage := NewMemoryStorage()
   421  		storage.Append(previousEnts[:tt.unstable-1])
   422  
   423  		// append unstable entries to raftlog
   424  		raftLog := newLog(storage, raftLogger)
   425  		raftLog.append(previousEnts[tt.unstable-1:]...)
   426  
   427  		ents := raftLog.unstableEntries()
   428  		if l := len(ents); l > 0 {
   429  			raftLog.stableTo(ents[l-1].Index, ents[l-i].Term)
   430  		}
   431  		if !reflect.DeepEqual(ents, tt.wents) {
   432  			t.Errorf("#%d: unstableEnts = %+v, want %+v", i, ents, tt.wents)
   433  		}
   434  		w := previousEnts[len(previousEnts)-1].Index + 1
   435  		if g := raftLog.unstable.offset; g != w {
   436  			t.Errorf("#%d: unstable = %d, want %d", i, g, w)
   437  		}
   438  	}
   439  }
   440  
   441  func TestCommitTo(t *testing.T) {
   442  	previousEnts := []pb.Entry{{Term: 1, Index: 1}, {Term: 2, Index: 2}, {Term: 3, Index: 3}}
   443  	commit := uint64(2)
   444  	tests := []struct {
   445  		commit  uint64
   446  		wcommit uint64
   447  		wpanic  bool
   448  	}{
   449  		{3, 3, false},
   450  		{1, 2, false}, // never decrease
   451  		{4, 0, true},  // commit out of range -> panic
   452  	}
   453  	for i, tt := range tests {
   454  		func() {
   455  			defer func() {
   456  				if r := recover(); r != nil {
   457  					if !tt.wpanic {
   458  						t.Errorf("%d: panic = %v, want %v", i, true, tt.wpanic)
   459  					}
   460  				}
   461  			}()
   462  			raftLog := newLog(NewMemoryStorage(), raftLogger)
   463  			raftLog.append(previousEnts...)
   464  			raftLog.committed = commit
   465  			raftLog.commitTo(tt.commit)
   466  			if raftLog.committed != tt.wcommit {
   467  				t.Errorf("#%d: committed = %d, want %d", i, raftLog.committed, tt.wcommit)
   468  			}
   469  		}()
   470  	}
   471  }
   472  
   473  func TestStableTo(t *testing.T) {
   474  	tests := []struct {
   475  		stablei   uint64
   476  		stablet   uint64
   477  		wunstable uint64
   478  	}{
   479  		{1, 1, 2},
   480  		{2, 2, 3},
   481  		{2, 1, 1}, // bad term
   482  		{3, 1, 1}, // bad index
   483  	}
   484  	for i, tt := range tests {
   485  		raftLog := newLog(NewMemoryStorage(), raftLogger)
   486  		raftLog.append([]pb.Entry{{Index: 1, Term: 1}, {Index: 2, Term: 2}}...)
   487  		raftLog.stableTo(tt.stablei, tt.stablet)
   488  		if raftLog.unstable.offset != tt.wunstable {
   489  			t.Errorf("#%d: unstable = %d, want %d", i, raftLog.unstable.offset, tt.wunstable)
   490  		}
   491  	}
   492  }
   493  
   494  func TestStableToWithSnap(t *testing.T) {
   495  	snapi, snapt := uint64(5), uint64(2)
   496  	tests := []struct {
   497  		stablei uint64
   498  		stablet uint64
   499  		newEnts []pb.Entry
   500  
   501  		wunstable uint64
   502  	}{
   503  		{snapi + 1, snapt, nil, snapi + 1},
   504  		{snapi, snapt, nil, snapi + 1},
   505  		{snapi - 1, snapt, nil, snapi + 1},
   506  
   507  		{snapi + 1, snapt + 1, nil, snapi + 1},
   508  		{snapi, snapt + 1, nil, snapi + 1},
   509  		{snapi - 1, snapt + 1, nil, snapi + 1},
   510  
   511  		{snapi + 1, snapt, []pb.Entry{{Index: snapi + 1, Term: snapt}}, snapi + 2},
   512  		{snapi, snapt, []pb.Entry{{Index: snapi + 1, Term: snapt}}, snapi + 1},
   513  		{snapi - 1, snapt, []pb.Entry{{Index: snapi + 1, Term: snapt}}, snapi + 1},
   514  
   515  		{snapi + 1, snapt + 1, []pb.Entry{{Index: snapi + 1, Term: snapt}}, snapi + 1},
   516  		{snapi, snapt + 1, []pb.Entry{{Index: snapi + 1, Term: snapt}}, snapi + 1},
   517  		{snapi - 1, snapt + 1, []pb.Entry{{Index: snapi + 1, Term: snapt}}, snapi + 1},
   518  	}
   519  	for i, tt := range tests {
   520  		s := NewMemoryStorage()
   521  		s.ApplySnapshot(pb.Snapshot{Metadata: pb.SnapshotMetadata{Index: snapi, Term: snapt}})
   522  		raftLog := newLog(s, raftLogger)
   523  		raftLog.append(tt.newEnts...)
   524  		raftLog.stableTo(tt.stablei, tt.stablet)
   525  		if raftLog.unstable.offset != tt.wunstable {
   526  			t.Errorf("#%d: unstable = %d, want %d", i, raftLog.unstable.offset, tt.wunstable)
   527  		}
   528  	}
   529  }
   530  
   531  //TestCompaction ensures that the number of log entries is correct after compactions.
   532  func TestCompaction(t *testing.T) {
   533  	tests := []struct {
   534  		lastIndex uint64
   535  		compact   []uint64
   536  		wleft     []int
   537  		wallow    bool
   538  	}{
   539  		// out of upper bound
   540  		{1000, []uint64{1001}, []int{-1}, false},
   541  		{1000, []uint64{300, 500, 800, 900}, []int{700, 500, 200, 100}, true},
   542  		// out of lower bound
   543  		{1000, []uint64{300, 299}, []int{700, -1}, false},
   544  	}
   545  
   546  	for i, tt := range tests {
   547  		func() {
   548  			defer func() {
   549  				if r := recover(); r != nil {
   550  					if tt.wallow {
   551  						t.Errorf("%d: allow = %v, want %v: %v", i, false, true, r)
   552  					}
   553  				}
   554  			}()
   555  
   556  			storage := NewMemoryStorage()
   557  			for i := uint64(1); i <= tt.lastIndex; i++ {
   558  				storage.Append([]pb.Entry{{Index: i}})
   559  			}
   560  			raftLog := newLog(storage, raftLogger)
   561  			raftLog.maybeCommit(tt.lastIndex, 0)
   562  			raftLog.appliedTo(raftLog.committed)
   563  
   564  			for j := 0; j < len(tt.compact); j++ {
   565  				err := storage.Compact(tt.compact[j])
   566  				if err != nil {
   567  					if tt.wallow {
   568  						t.Errorf("#%d.%d allow = %t, want %t", i, j, false, tt.wallow)
   569  					}
   570  					continue
   571  				}
   572  				if len(raftLog.allEntries()) != tt.wleft[j] {
   573  					t.Errorf("#%d.%d len = %d, want %d", i, j, len(raftLog.allEntries()), tt.wleft[j])
   574  				}
   575  			}
   576  		}()
   577  	}
   578  }
   579  
   580  func TestLogRestore(t *testing.T) {
   581  	index := uint64(1000)
   582  	term := uint64(1000)
   583  	snap := pb.SnapshotMetadata{Index: index, Term: term}
   584  	storage := NewMemoryStorage()
   585  	storage.ApplySnapshot(pb.Snapshot{Metadata: snap})
   586  	raftLog := newLog(storage, raftLogger)
   587  
   588  	if len(raftLog.allEntries()) != 0 {
   589  		t.Errorf("len = %d, want 0", len(raftLog.allEntries()))
   590  	}
   591  	if raftLog.firstIndex() != index+1 {
   592  		t.Errorf("firstIndex = %d, want %d", raftLog.firstIndex(), index+1)
   593  	}
   594  	if raftLog.committed != index {
   595  		t.Errorf("committed = %d, want %d", raftLog.committed, index)
   596  	}
   597  	if raftLog.unstable.offset != index+1 {
   598  		t.Errorf("unstable = %d, want %d", raftLog.unstable.offset, index+1)
   599  	}
   600  	if mustTerm(raftLog.term(index)) != term {
   601  		t.Errorf("term = %d, want %d", mustTerm(raftLog.term(index)), term)
   602  	}
   603  }
   604  
   605  func TestIsOutOfBounds(t *testing.T) {
   606  	offset := uint64(100)
   607  	num := uint64(100)
   608  	storage := NewMemoryStorage()
   609  	storage.ApplySnapshot(pb.Snapshot{Metadata: pb.SnapshotMetadata{Index: offset}})
   610  	l := newLog(storage, raftLogger)
   611  	for i := uint64(1); i <= num; i++ {
   612  		l.append(pb.Entry{Index: i + offset})
   613  	}
   614  
   615  	first := offset + 1
   616  	tests := []struct {
   617  		lo, hi        uint64
   618  		wpanic        bool
   619  		wErrCompacted bool
   620  	}{
   621  		{
   622  			first - 2, first + 1,
   623  			false,
   624  			true,
   625  		},
   626  		{
   627  			first - 1, first + 1,
   628  			false,
   629  			true,
   630  		},
   631  		{
   632  			first, first,
   633  			false,
   634  			false,
   635  		},
   636  		{
   637  			first + num/2, first + num/2,
   638  			false,
   639  			false,
   640  		},
   641  		{
   642  			first + num - 1, first + num - 1,
   643  			false,
   644  			false,
   645  		},
   646  		{
   647  			first + num, first + num,
   648  			false,
   649  			false,
   650  		},
   651  		{
   652  			first + num, first + num + 1,
   653  			true,
   654  			false,
   655  		},
   656  		{
   657  			first + num + 1, first + num + 1,
   658  			true,
   659  			false,
   660  		},
   661  	}
   662  
   663  	for i, tt := range tests {
   664  		func() {
   665  			defer func() {
   666  				if r := recover(); r != nil {
   667  					if !tt.wpanic {
   668  						t.Errorf("%d: panic = %v, want %v: %v", i, true, false, r)
   669  					}
   670  				}
   671  			}()
   672  			err := l.mustCheckOutOfBounds(tt.lo, tt.hi)
   673  			if tt.wpanic {
   674  				t.Errorf("%d: panic = %v, want %v", i, false, true)
   675  			}
   676  			if tt.wErrCompacted && err != ErrCompacted {
   677  				t.Errorf("%d: err = %v, want %v", i, err, ErrCompacted)
   678  			}
   679  			if !tt.wErrCompacted && err != nil {
   680  				t.Errorf("%d: unexpected err %v", i, err)
   681  			}
   682  		}()
   683  	}
   684  }
   685  
   686  func TestTerm(t *testing.T) {
   687  	var i uint64
   688  	offset := uint64(100)
   689  	num := uint64(100)
   690  
   691  	storage := NewMemoryStorage()
   692  	storage.ApplySnapshot(pb.Snapshot{Metadata: pb.SnapshotMetadata{Index: offset, Term: 1}})
   693  	l := newLog(storage, raftLogger)
   694  	for i = 1; i < num; i++ {
   695  		l.append(pb.Entry{Index: offset + i, Term: i})
   696  	}
   697  
   698  	tests := []struct {
   699  		index uint64
   700  		w     uint64
   701  	}{
   702  		{offset - 1, 0},
   703  		{offset, 1},
   704  		{offset + num/2, num / 2},
   705  		{offset + num - 1, num - 1},
   706  		{offset + num, 0},
   707  	}
   708  
   709  	for j, tt := range tests {
   710  		term := mustTerm(l.term(tt.index))
   711  		if term != tt.w {
   712  			t.Errorf("#%d: at = %d, want %d", j, term, tt.w)
   713  		}
   714  	}
   715  }
   716  
   717  func TestTermWithUnstableSnapshot(t *testing.T) {
   718  	storagesnapi := uint64(100)
   719  	unstablesnapi := storagesnapi + 5
   720  
   721  	storage := NewMemoryStorage()
   722  	storage.ApplySnapshot(pb.Snapshot{Metadata: pb.SnapshotMetadata{Index: storagesnapi, Term: 1}})
   723  	l := newLog(storage, raftLogger)
   724  	l.restore(pb.Snapshot{Metadata: pb.SnapshotMetadata{Index: unstablesnapi, Term: 1}})
   725  
   726  	tests := []struct {
   727  		index uint64
   728  		w     uint64
   729  	}{
   730  		// cannot get term from storage
   731  		{storagesnapi, 0},
   732  		// cannot get term from the gap between storage ents and unstable snapshot
   733  		{storagesnapi + 1, 0},
   734  		{unstablesnapi - 1, 0},
   735  		// get term from unstable snapshot index
   736  		{unstablesnapi, 1},
   737  	}
   738  
   739  	for i, tt := range tests {
   740  		term := mustTerm(l.term(tt.index))
   741  		if term != tt.w {
   742  			t.Errorf("#%d: at = %d, want %d", i, term, tt.w)
   743  		}
   744  	}
   745  }
   746  
   747  func TestSlice(t *testing.T) {
   748  	var i uint64
   749  	offset := uint64(100)
   750  	num := uint64(100)
   751  	last := offset + num
   752  	half := offset + num/2
   753  	halfe := pb.Entry{Index: half, Term: half}
   754  
   755  	storage := NewMemoryStorage()
   756  	storage.ApplySnapshot(pb.Snapshot{Metadata: pb.SnapshotMetadata{Index: offset}})
   757  	for i = 1; i < num/2; i++ {
   758  		storage.Append([]pb.Entry{{Index: offset + i, Term: offset + i}})
   759  	}
   760  	l := newLog(storage, raftLogger)
   761  	for i = num / 2; i < num; i++ {
   762  		l.append(pb.Entry{Index: offset + i, Term: offset + i})
   763  	}
   764  
   765  	tests := []struct {
   766  		from  uint64
   767  		to    uint64
   768  		limit uint64
   769  
   770  		w      []pb.Entry
   771  		wpanic bool
   772  	}{
   773  		// test no limit
   774  		{offset - 1, offset + 1, noLimit, nil, false},
   775  		{offset, offset + 1, noLimit, nil, false},
   776  		{half - 1, half + 1, noLimit, []pb.Entry{{Index: half - 1, Term: half - 1}, {Index: half, Term: half}}, false},
   777  		{half, half + 1, noLimit, []pb.Entry{{Index: half, Term: half}}, false},
   778  		{last - 1, last, noLimit, []pb.Entry{{Index: last - 1, Term: last - 1}}, false},
   779  		{last, last + 1, noLimit, nil, true},
   780  
   781  		// test limit
   782  		{half - 1, half + 1, 0, []pb.Entry{{Index: half - 1, Term: half - 1}}, false},
   783  		{half - 1, half + 1, uint64(halfe.Size() + 1), []pb.Entry{{Index: half - 1, Term: half - 1}}, false},
   784  		{half - 2, half + 1, uint64(halfe.Size() + 1), []pb.Entry{{Index: half - 2, Term: half - 2}}, false},
   785  		{half - 1, half + 1, uint64(halfe.Size() * 2), []pb.Entry{{Index: half - 1, Term: half - 1}, {Index: half, Term: half}}, false},
   786  		{half - 1, half + 2, uint64(halfe.Size() * 3), []pb.Entry{{Index: half - 1, Term: half - 1}, {Index: half, Term: half}, {Index: half + 1, Term: half + 1}}, false},
   787  		{half, half + 2, uint64(halfe.Size()), []pb.Entry{{Index: half, Term: half}}, false},
   788  		{half, half + 2, uint64(halfe.Size() * 2), []pb.Entry{{Index: half, Term: half}, {Index: half + 1, Term: half + 1}}, false},
   789  	}
   790  
   791  	for j, tt := range tests {
   792  		func() {
   793  			defer func() {
   794  				if r := recover(); r != nil {
   795  					if !tt.wpanic {
   796  						t.Errorf("%d: panic = %v, want %v: %v", j, true, false, r)
   797  					}
   798  				}
   799  			}()
   800  			g, err := l.slice(tt.from, tt.to, tt.limit)
   801  			if tt.from <= offset && err != ErrCompacted {
   802  				t.Fatalf("#%d: err = %v, want %v", j, err, ErrCompacted)
   803  			}
   804  			if tt.from > offset && err != nil {
   805  				t.Fatalf("#%d: unexpected error %v", j, err)
   806  			}
   807  			if !reflect.DeepEqual(g, tt.w) {
   808  				t.Errorf("#%d: from %d to %d = %v, want %v", j, tt.from, tt.to, g, tt.w)
   809  			}
   810  		}()
   811  	}
   812  }
   813  
   814  func mustTerm(term uint64, err error) uint64 {
   815  	if err != nil {
   816  		panic(err)
   817  	}
   818  	return term
   819  }