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 }