go.etcd.io/etcd@v3.3.27+incompatible/raft/log_unstable_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 TestUnstableMaybeFirstIndex(t *testing.T) {
    25  	tests := []struct {
    26  		entries []pb.Entry
    27  		offset  uint64
    28  		snap    *pb.Snapshot
    29  
    30  		wok    bool
    31  		windex uint64
    32  	}{
    33  		// no snapshot
    34  		{
    35  			[]pb.Entry{{Index: 5, Term: 1}}, 5, nil,
    36  			false, 0,
    37  		},
    38  		{
    39  			[]pb.Entry{}, 0, nil,
    40  			false, 0,
    41  		},
    42  		// has snapshot
    43  		{
    44  			[]pb.Entry{{Index: 5, Term: 1}}, 5, &pb.Snapshot{Metadata: pb.SnapshotMetadata{Index: 4, Term: 1}},
    45  			true, 5,
    46  		},
    47  		{
    48  			[]pb.Entry{}, 5, &pb.Snapshot{Metadata: pb.SnapshotMetadata{Index: 4, Term: 1}},
    49  			true, 5,
    50  		},
    51  	}
    52  
    53  	for i, tt := range tests {
    54  		u := unstable{
    55  			entries:  tt.entries,
    56  			offset:   tt.offset,
    57  			snapshot: tt.snap,
    58  			logger:   raftLogger,
    59  		}
    60  		index, ok := u.maybeFirstIndex()
    61  		if ok != tt.wok {
    62  			t.Errorf("#%d: ok = %t, want %t", i, ok, tt.wok)
    63  		}
    64  		if index != tt.windex {
    65  			t.Errorf("#%d: index = %d, want %d", i, index, tt.windex)
    66  		}
    67  	}
    68  }
    69  
    70  func TestMaybeLastIndex(t *testing.T) {
    71  	tests := []struct {
    72  		entries []pb.Entry
    73  		offset  uint64
    74  		snap    *pb.Snapshot
    75  
    76  		wok    bool
    77  		windex uint64
    78  	}{
    79  		// last in entries
    80  		{
    81  			[]pb.Entry{{Index: 5, Term: 1}}, 5, nil,
    82  			true, 5,
    83  		},
    84  		{
    85  			[]pb.Entry{{Index: 5, Term: 1}}, 5, &pb.Snapshot{Metadata: pb.SnapshotMetadata{Index: 4, Term: 1}},
    86  			true, 5,
    87  		},
    88  		// last in snapshot
    89  		{
    90  			[]pb.Entry{}, 5, &pb.Snapshot{Metadata: pb.SnapshotMetadata{Index: 4, Term: 1}},
    91  			true, 4,
    92  		},
    93  		// empty unstable
    94  		{
    95  			[]pb.Entry{}, 0, nil,
    96  			false, 0,
    97  		},
    98  	}
    99  
   100  	for i, tt := range tests {
   101  		u := unstable{
   102  			entries:  tt.entries,
   103  			offset:   tt.offset,
   104  			snapshot: tt.snap,
   105  			logger:   raftLogger,
   106  		}
   107  		index, ok := u.maybeLastIndex()
   108  		if ok != tt.wok {
   109  			t.Errorf("#%d: ok = %t, want %t", i, ok, tt.wok)
   110  		}
   111  		if index != tt.windex {
   112  			t.Errorf("#%d: index = %d, want %d", i, index, tt.windex)
   113  		}
   114  	}
   115  }
   116  
   117  func TestUnstableMaybeTerm(t *testing.T) {
   118  	tests := []struct {
   119  		entries []pb.Entry
   120  		offset  uint64
   121  		snap    *pb.Snapshot
   122  		index   uint64
   123  
   124  		wok   bool
   125  		wterm uint64
   126  	}{
   127  		// term from entries
   128  		{
   129  			[]pb.Entry{{Index: 5, Term: 1}}, 5, nil,
   130  			5,
   131  			true, 1,
   132  		},
   133  		{
   134  			[]pb.Entry{{Index: 5, Term: 1}}, 5, nil,
   135  			6,
   136  			false, 0,
   137  		},
   138  		{
   139  			[]pb.Entry{{Index: 5, Term: 1}}, 5, nil,
   140  			4,
   141  			false, 0,
   142  		},
   143  		{
   144  			[]pb.Entry{{Index: 5, Term: 1}}, 5, &pb.Snapshot{Metadata: pb.SnapshotMetadata{Index: 4, Term: 1}},
   145  			5,
   146  			true, 1,
   147  		},
   148  		{
   149  			[]pb.Entry{{Index: 5, Term: 1}}, 5, &pb.Snapshot{Metadata: pb.SnapshotMetadata{Index: 4, Term: 1}},
   150  			6,
   151  			false, 0,
   152  		},
   153  		// term from snapshot
   154  		{
   155  			[]pb.Entry{{Index: 5, Term: 1}}, 5, &pb.Snapshot{Metadata: pb.SnapshotMetadata{Index: 4, Term: 1}},
   156  			4,
   157  			true, 1,
   158  		},
   159  		{
   160  			[]pb.Entry{{Index: 5, Term: 1}}, 5, &pb.Snapshot{Metadata: pb.SnapshotMetadata{Index: 4, Term: 1}},
   161  			3,
   162  			false, 0,
   163  		},
   164  		{
   165  			[]pb.Entry{}, 5, &pb.Snapshot{Metadata: pb.SnapshotMetadata{Index: 4, Term: 1}},
   166  			5,
   167  			false, 0,
   168  		},
   169  		{
   170  			[]pb.Entry{}, 5, &pb.Snapshot{Metadata: pb.SnapshotMetadata{Index: 4, Term: 1}},
   171  			4,
   172  			true, 1,
   173  		},
   174  		{
   175  			[]pb.Entry{}, 0, nil,
   176  			5,
   177  			false, 0,
   178  		},
   179  	}
   180  
   181  	for i, tt := range tests {
   182  		u := unstable{
   183  			entries:  tt.entries,
   184  			offset:   tt.offset,
   185  			snapshot: tt.snap,
   186  			logger:   raftLogger,
   187  		}
   188  		term, ok := u.maybeTerm(tt.index)
   189  		if ok != tt.wok {
   190  			t.Errorf("#%d: ok = %t, want %t", i, ok, tt.wok)
   191  		}
   192  		if term != tt.wterm {
   193  			t.Errorf("#%d: term = %d, want %d", i, term, tt.wterm)
   194  		}
   195  	}
   196  }
   197  
   198  func TestUnstableRestore(t *testing.T) {
   199  	u := unstable{
   200  		entries:  []pb.Entry{{Index: 5, Term: 1}},
   201  		offset:   5,
   202  		snapshot: &pb.Snapshot{Metadata: pb.SnapshotMetadata{Index: 4, Term: 1}},
   203  		logger:   raftLogger,
   204  	}
   205  	s := pb.Snapshot{Metadata: pb.SnapshotMetadata{Index: 6, Term: 2}}
   206  	u.restore(s)
   207  
   208  	if u.offset != s.Metadata.Index+1 {
   209  		t.Errorf("offset = %d, want %d", u.offset, s.Metadata.Index+1)
   210  	}
   211  	if len(u.entries) != 0 {
   212  		t.Errorf("len = %d, want 0", len(u.entries))
   213  	}
   214  	if !reflect.DeepEqual(u.snapshot, &s) {
   215  		t.Errorf("snap = %v, want %v", u.snapshot, &s)
   216  	}
   217  }
   218  
   219  func TestUnstableStableTo(t *testing.T) {
   220  	tests := []struct {
   221  		entries     []pb.Entry
   222  		offset      uint64
   223  		snap        *pb.Snapshot
   224  		index, term uint64
   225  
   226  		woffset uint64
   227  		wlen    int
   228  	}{
   229  		{
   230  			[]pb.Entry{}, 0, nil,
   231  			5, 1,
   232  			0, 0,
   233  		},
   234  		{
   235  			[]pb.Entry{{Index: 5, Term: 1}}, 5, nil,
   236  			5, 1, // stable to the first entry
   237  			6, 0,
   238  		},
   239  		{
   240  			[]pb.Entry{{Index: 5, Term: 1}, {Index: 6, Term: 1}}, 5, nil,
   241  			5, 1, // stable to the first entry
   242  			6, 1,
   243  		},
   244  		{
   245  			[]pb.Entry{{Index: 6, Term: 2}}, 6, nil,
   246  			6, 1, // stable to the first entry and term mismatch
   247  			6, 1,
   248  		},
   249  		{
   250  			[]pb.Entry{{Index: 5, Term: 1}}, 5, nil,
   251  			4, 1, // stable to old entry
   252  			5, 1,
   253  		},
   254  		{
   255  			[]pb.Entry{{Index: 5, Term: 1}}, 5, nil,
   256  			4, 2, // stable to old entry
   257  			5, 1,
   258  		},
   259  		// with snapshot
   260  		{
   261  			[]pb.Entry{{Index: 5, Term: 1}}, 5, &pb.Snapshot{Metadata: pb.SnapshotMetadata{Index: 4, Term: 1}},
   262  			5, 1, // stable to the first entry
   263  			6, 0,
   264  		},
   265  		{
   266  			[]pb.Entry{{Index: 5, Term: 1}, {Index: 6, Term: 1}}, 5, &pb.Snapshot{Metadata: pb.SnapshotMetadata{Index: 4, Term: 1}},
   267  			5, 1, // stable to the first entry
   268  			6, 1,
   269  		},
   270  		{
   271  			[]pb.Entry{{Index: 6, Term: 2}}, 6, &pb.Snapshot{Metadata: pb.SnapshotMetadata{Index: 5, Term: 1}},
   272  			6, 1, // stable to the first entry and term mismatch
   273  			6, 1,
   274  		},
   275  		{
   276  			[]pb.Entry{{Index: 5, Term: 1}}, 5, &pb.Snapshot{Metadata: pb.SnapshotMetadata{Index: 4, Term: 1}},
   277  			4, 1, // stable to snapshot
   278  			5, 1,
   279  		},
   280  		{
   281  			[]pb.Entry{{Index: 5, Term: 2}}, 5, &pb.Snapshot{Metadata: pb.SnapshotMetadata{Index: 4, Term: 2}},
   282  			4, 1, // stable to old entry
   283  			5, 1,
   284  		},
   285  	}
   286  
   287  	for i, tt := range tests {
   288  		u := unstable{
   289  			entries:  tt.entries,
   290  			offset:   tt.offset,
   291  			snapshot: tt.snap,
   292  			logger:   raftLogger,
   293  		}
   294  		u.stableTo(tt.index, tt.term)
   295  		if u.offset != tt.woffset {
   296  			t.Errorf("#%d: offset = %d, want %d", i, u.offset, tt.woffset)
   297  		}
   298  		if len(u.entries) != tt.wlen {
   299  			t.Errorf("#%d: len = %d, want %d", i, len(u.entries), tt.wlen)
   300  		}
   301  	}
   302  }
   303  
   304  func TestUnstableTruncateAndAppend(t *testing.T) {
   305  	tests := []struct {
   306  		entries  []pb.Entry
   307  		offset   uint64
   308  		snap     *pb.Snapshot
   309  		toappend []pb.Entry
   310  
   311  		woffset  uint64
   312  		wentries []pb.Entry
   313  	}{
   314  		// append to the end
   315  		{
   316  			[]pb.Entry{{Index: 5, Term: 1}}, 5, nil,
   317  			[]pb.Entry{{Index: 6, Term: 1}, {Index: 7, Term: 1}},
   318  			5, []pb.Entry{{Index: 5, Term: 1}, {Index: 6, Term: 1}, {Index: 7, Term: 1}},
   319  		},
   320  		// replace the unstable entries
   321  		{
   322  			[]pb.Entry{{Index: 5, Term: 1}}, 5, nil,
   323  			[]pb.Entry{{Index: 5, Term: 2}, {Index: 6, Term: 2}},
   324  			5, []pb.Entry{{Index: 5, Term: 2}, {Index: 6, Term: 2}},
   325  		},
   326  		{
   327  			[]pb.Entry{{Index: 5, Term: 1}}, 5, nil,
   328  			[]pb.Entry{{Index: 4, Term: 2}, {Index: 5, Term: 2}, {Index: 6, Term: 2}},
   329  			4, []pb.Entry{{Index: 4, Term: 2}, {Index: 5, Term: 2}, {Index: 6, Term: 2}},
   330  		},
   331  		// truncate the existing entries and append
   332  		{
   333  			[]pb.Entry{{Index: 5, Term: 1}, {Index: 6, Term: 1}, {Index: 7, Term: 1}}, 5, nil,
   334  			[]pb.Entry{{Index: 6, Term: 2}},
   335  			5, []pb.Entry{{Index: 5, Term: 1}, {Index: 6, Term: 2}},
   336  		},
   337  		{
   338  			[]pb.Entry{{Index: 5, Term: 1}, {Index: 6, Term: 1}, {Index: 7, Term: 1}}, 5, nil,
   339  			[]pb.Entry{{Index: 7, Term: 2}, {Index: 8, Term: 2}},
   340  			5, []pb.Entry{{Index: 5, Term: 1}, {Index: 6, Term: 1}, {Index: 7, Term: 2}, {Index: 8, Term: 2}},
   341  		},
   342  	}
   343  
   344  	for i, tt := range tests {
   345  		u := unstable{
   346  			entries:  tt.entries,
   347  			offset:   tt.offset,
   348  			snapshot: tt.snap,
   349  			logger:   raftLogger,
   350  		}
   351  		u.truncateAndAppend(tt.toappend)
   352  		if u.offset != tt.woffset {
   353  			t.Errorf("#%d: offset = %d, want %d", i, u.offset, tt.woffset)
   354  		}
   355  		if !reflect.DeepEqual(u.entries, tt.wentries) {
   356  			t.Errorf("#%d: entries = %v, want %v", i, u.entries, tt.wentries)
   357  		}
   358  	}
   359  }