go.etcd.io/etcd@v3.3.27+incompatible/wal/wal_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 wal 16 17 import ( 18 "bytes" 19 "io" 20 "io/ioutil" 21 "math" 22 "os" 23 "path" 24 "path/filepath" 25 "reflect" 26 "testing" 27 28 "github.com/coreos/etcd/pkg/fileutil" 29 "github.com/coreos/etcd/pkg/pbutil" 30 "github.com/coreos/etcd/raft/raftpb" 31 "github.com/coreos/etcd/wal/walpb" 32 ) 33 34 func TestNew(t *testing.T) { 35 p, err := ioutil.TempDir(os.TempDir(), "waltest") 36 if err != nil { 37 t.Fatal(err) 38 } 39 defer os.RemoveAll(p) 40 41 w, err := Create(p, []byte("somedata")) 42 if err != nil { 43 t.Fatalf("err = %v, want nil", err) 44 } 45 if g := filepath.Base(w.tail().Name()); g != walName(0, 0) { 46 t.Errorf("name = %+v, want %+v", g, walName(0, 0)) 47 } 48 defer w.Close() 49 50 // file is preallocated to segment size; only read data written by wal 51 off, err := w.tail().Seek(0, io.SeekCurrent) 52 if err != nil { 53 t.Fatal(err) 54 } 55 gd := make([]byte, off) 56 f, err := os.Open(filepath.Join(p, filepath.Base(w.tail().Name()))) 57 if err != nil { 58 t.Fatal(err) 59 } 60 defer f.Close() 61 if _, err = io.ReadFull(f, gd); err != nil { 62 t.Fatalf("err = %v, want nil", err) 63 } 64 65 var wb bytes.Buffer 66 e := newEncoder(&wb, 0, 0) 67 err = e.encode(&walpb.Record{Type: crcType, Crc: 0}) 68 if err != nil { 69 t.Fatalf("err = %v, want nil", err) 70 } 71 err = e.encode(&walpb.Record{Type: metadataType, Data: []byte("somedata")}) 72 if err != nil { 73 t.Fatalf("err = %v, want nil", err) 74 } 75 r := &walpb.Record{ 76 Type: snapshotType, 77 Data: pbutil.MustMarshal(&walpb.Snapshot{}), 78 } 79 if err = e.encode(r); err != nil { 80 t.Fatalf("err = %v, want nil", err) 81 } 82 e.flush() 83 if !bytes.Equal(gd, wb.Bytes()) { 84 t.Errorf("data = %v, want %v", gd, wb.Bytes()) 85 } 86 } 87 88 func TestNewForInitedDir(t *testing.T) { 89 p, err := ioutil.TempDir(os.TempDir(), "waltest") 90 if err != nil { 91 t.Fatal(err) 92 } 93 defer os.RemoveAll(p) 94 95 os.Create(filepath.Join(p, walName(0, 0))) 96 if _, err = Create(p, nil); err == nil || err != os.ErrExist { 97 t.Errorf("err = %v, want %v", err, os.ErrExist) 98 } 99 } 100 101 func TestOpenAtIndex(t *testing.T) { 102 dir, err := ioutil.TempDir(os.TempDir(), "waltest") 103 if err != nil { 104 t.Fatal(err) 105 } 106 defer os.RemoveAll(dir) 107 108 f, err := os.Create(filepath.Join(dir, walName(0, 0))) 109 if err != nil { 110 t.Fatal(err) 111 } 112 f.Close() 113 114 w, err := Open(dir, walpb.Snapshot{}) 115 if err != nil { 116 t.Fatalf("err = %v, want nil", err) 117 } 118 if g := filepath.Base(w.tail().Name()); g != walName(0, 0) { 119 t.Errorf("name = %+v, want %+v", g, walName(0, 0)) 120 } 121 if w.seq() != 0 { 122 t.Errorf("seq = %d, want %d", w.seq(), 0) 123 } 124 w.Close() 125 126 wname := walName(2, 10) 127 f, err = os.Create(filepath.Join(dir, wname)) 128 if err != nil { 129 t.Fatal(err) 130 } 131 f.Close() 132 133 w, err = Open(dir, walpb.Snapshot{Index: 5}) 134 if err != nil { 135 t.Fatalf("err = %v, want nil", err) 136 } 137 if g := filepath.Base(w.tail().Name()); g != wname { 138 t.Errorf("name = %+v, want %+v", g, wname) 139 } 140 if w.seq() != 2 { 141 t.Errorf("seq = %d, want %d", w.seq(), 2) 142 } 143 w.Close() 144 145 emptydir, err := ioutil.TempDir(os.TempDir(), "waltestempty") 146 if err != nil { 147 t.Fatal(err) 148 } 149 defer os.RemoveAll(emptydir) 150 if _, err = Open(emptydir, walpb.Snapshot{}); err != ErrFileNotFound { 151 t.Errorf("err = %v, want %v", err, ErrFileNotFound) 152 } 153 } 154 155 // TestVerify tests that Verify throws a non-nil error when the WAL is corrupted. 156 // The test creates a WAL directory and cuts out multiple WAL files. Then 157 // it corrupts one of the files by completely truncating it. 158 func TestVerify(t *testing.T) { 159 walDir, err := ioutil.TempDir(os.TempDir(), "waltest") 160 if err != nil { 161 t.Fatal(err) 162 } 163 defer os.RemoveAll(walDir) 164 165 // create WAL 166 w, err := Create(walDir, nil) 167 if err != nil { 168 t.Fatal(err) 169 } 170 defer w.Close() 171 172 // make 5 separate files 173 for i := 0; i < 5; i++ { 174 es := []raftpb.Entry{{Index: uint64(i), Data: []byte("waldata" + string(i+1))}} 175 if err = w.Save(raftpb.HardState{}, es); err != nil { 176 t.Fatal(err) 177 } 178 if err = w.cut(); err != nil { 179 t.Fatal(err) 180 } 181 } 182 183 // to verify the WAL is not corrupted at this point 184 err = Verify(walDir, walpb.Snapshot{}) 185 if err != nil { 186 t.Errorf("expected a nil error, got %v", err) 187 } 188 189 walFiles, err := ioutil.ReadDir(walDir) 190 if err != nil { 191 t.Fatal(err) 192 } 193 194 // corrupt the WAL by truncating one of the WAL files completely 195 err = os.Truncate(path.Join(walDir, walFiles[2].Name()), 0) 196 if err != nil { 197 t.Fatal(err) 198 } 199 200 err = Verify(walDir, walpb.Snapshot{}) 201 if err == nil { 202 t.Error("expected a non-nil error, got nil") 203 } 204 } 205 206 // TODO: split it into smaller tests for better readability 207 func TestCut(t *testing.T) { 208 p, err := ioutil.TempDir(os.TempDir(), "waltest") 209 if err != nil { 210 t.Fatal(err) 211 } 212 defer os.RemoveAll(p) 213 214 w, err := Create(p, nil) 215 if err != nil { 216 t.Fatal(err) 217 } 218 defer w.Close() 219 220 state := raftpb.HardState{Term: 1} 221 if err = w.Save(state, nil); err != nil { 222 t.Fatal(err) 223 } 224 if err = w.cut(); err != nil { 225 t.Fatal(err) 226 } 227 wname := walName(1, 1) 228 if g := filepath.Base(w.tail().Name()); g != wname { 229 t.Errorf("name = %s, want %s", g, wname) 230 } 231 232 es := []raftpb.Entry{{Index: 1, Term: 1, Data: []byte{1}}} 233 if err = w.Save(raftpb.HardState{}, es); err != nil { 234 t.Fatal(err) 235 } 236 if err = w.cut(); err != nil { 237 t.Fatal(err) 238 } 239 snap := walpb.Snapshot{Index: 2, Term: 1} 240 if err = w.SaveSnapshot(snap); err != nil { 241 t.Fatal(err) 242 } 243 wname = walName(2, 2) 244 if g := filepath.Base(w.tail().Name()); g != wname { 245 t.Errorf("name = %s, want %s", g, wname) 246 } 247 248 // check the state in the last WAL 249 // We do check before closing the WAL to ensure that Cut syncs the data 250 // into the disk. 251 f, err := os.Open(filepath.Join(p, wname)) 252 if err != nil { 253 t.Fatal(err) 254 } 255 defer f.Close() 256 nw := &WAL{ 257 decoder: newDecoder(f), 258 start: snap, 259 } 260 _, gst, _, err := nw.ReadAll() 261 if err != nil { 262 t.Fatal(err) 263 } 264 if !reflect.DeepEqual(gst, state) { 265 t.Errorf("state = %+v, want %+v", gst, state) 266 } 267 } 268 269 func TestSaveWithCut(t *testing.T) { 270 p, err := ioutil.TempDir(os.TempDir(), "waltest") 271 if err != nil { 272 t.Fatal(err) 273 } 274 defer os.RemoveAll(p) 275 276 w, err := Create(p, []byte("metadata")) 277 if err != nil { 278 t.Fatal(err) 279 } 280 281 state := raftpb.HardState{Term: 1} 282 if err = w.Save(state, nil); err != nil { 283 t.Fatal(err) 284 } 285 bigData := make([]byte, 500) 286 strdata := "Hello World!!" 287 copy(bigData, strdata) 288 // set a lower value for SegmentSizeBytes, else the test takes too long to complete 289 restoreLater := SegmentSizeBytes 290 const EntrySize int = 500 291 SegmentSizeBytes = 2 * 1024 292 defer func() { SegmentSizeBytes = restoreLater }() 293 var index uint64 = 0 294 for totalSize := 0; totalSize < int(SegmentSizeBytes); totalSize += EntrySize { 295 ents := []raftpb.Entry{{Index: index, Term: 1, Data: bigData}} 296 if err = w.Save(state, ents); err != nil { 297 t.Fatal(err) 298 } 299 index++ 300 } 301 302 w.Close() 303 304 neww, err := Open(p, walpb.Snapshot{}) 305 if err != nil { 306 t.Fatalf("err = %v, want nil", err) 307 } 308 defer neww.Close() 309 wname := walName(1, index) 310 if g := filepath.Base(neww.tail().Name()); g != wname { 311 t.Errorf("name = %s, want %s", g, wname) 312 } 313 314 _, newhardstate, entries, err := neww.ReadAll() 315 if err != nil { 316 t.Fatal(err) 317 } 318 319 if !reflect.DeepEqual(newhardstate, state) { 320 t.Errorf("Hard State = %+v, want %+v", newhardstate, state) 321 } 322 if len(entries) != int(SegmentSizeBytes/int64(EntrySize)) { 323 t.Errorf("Number of entries = %d, expected = %d", len(entries), int(SegmentSizeBytes/int64(EntrySize))) 324 } 325 for _, oneent := range entries { 326 if !bytes.Equal(oneent.Data, bigData) { 327 t.Errorf("the saved data does not match at Index %d : found: %s , want :%s", oneent.Index, oneent.Data, bigData) 328 } 329 } 330 } 331 332 func TestRecover(t *testing.T) { 333 p, err := ioutil.TempDir(os.TempDir(), "waltest") 334 if err != nil { 335 t.Fatal(err) 336 } 337 defer os.RemoveAll(p) 338 339 w, err := Create(p, []byte("metadata")) 340 if err != nil { 341 t.Fatal(err) 342 } 343 if err = w.SaveSnapshot(walpb.Snapshot{}); err != nil { 344 t.Fatal(err) 345 } 346 ents := []raftpb.Entry{{Index: 1, Term: 1, Data: []byte{1}}, {Index: 2, Term: 2, Data: []byte{2}}} 347 if err = w.Save(raftpb.HardState{}, ents); err != nil { 348 t.Fatal(err) 349 } 350 sts := []raftpb.HardState{{Term: 1, Vote: 1, Commit: 1}, {Term: 2, Vote: 2, Commit: 2}} 351 for _, s := range sts { 352 if err = w.Save(s, nil); err != nil { 353 t.Fatal(err) 354 } 355 } 356 w.Close() 357 358 if w, err = Open(p, walpb.Snapshot{}); err != nil { 359 t.Fatal(err) 360 } 361 metadata, state, entries, err := w.ReadAll() 362 if err != nil { 363 t.Fatal(err) 364 } 365 366 if !bytes.Equal(metadata, []byte("metadata")) { 367 t.Errorf("metadata = %s, want %s", metadata, "metadata") 368 } 369 if !reflect.DeepEqual(entries, ents) { 370 t.Errorf("ents = %+v, want %+v", entries, ents) 371 } 372 // only the latest state is recorded 373 s := sts[len(sts)-1] 374 if !reflect.DeepEqual(state, s) { 375 t.Errorf("state = %+v, want %+v", state, s) 376 } 377 w.Close() 378 } 379 380 func TestSearchIndex(t *testing.T) { 381 tests := []struct { 382 names []string 383 index uint64 384 widx int 385 wok bool 386 }{ 387 { 388 []string{ 389 "0000000000000000-0000000000000000.wal", 390 "0000000000000001-0000000000001000.wal", 391 "0000000000000002-0000000000002000.wal", 392 }, 393 0x1000, 1, true, 394 }, 395 { 396 []string{ 397 "0000000000000001-0000000000004000.wal", 398 "0000000000000002-0000000000003000.wal", 399 "0000000000000003-0000000000005000.wal", 400 }, 401 0x4000, 1, true, 402 }, 403 { 404 []string{ 405 "0000000000000001-0000000000002000.wal", 406 "0000000000000002-0000000000003000.wal", 407 "0000000000000003-0000000000005000.wal", 408 }, 409 0x1000, -1, false, 410 }, 411 } 412 for i, tt := range tests { 413 idx, ok := searchIndex(tt.names, tt.index) 414 if idx != tt.widx { 415 t.Errorf("#%d: idx = %d, want %d", i, idx, tt.widx) 416 } 417 if ok != tt.wok { 418 t.Errorf("#%d: ok = %v, want %v", i, ok, tt.wok) 419 } 420 } 421 } 422 423 func TestScanWalName(t *testing.T) { 424 tests := []struct { 425 str string 426 wseq, windex uint64 427 wok bool 428 }{ 429 {"0000000000000000-0000000000000000.wal", 0, 0, true}, 430 {"0000000000000000.wal", 0, 0, false}, 431 {"0000000000000000-0000000000000000.snap", 0, 0, false}, 432 } 433 for i, tt := range tests { 434 s, index, err := parseWalName(tt.str) 435 if g := err == nil; g != tt.wok { 436 t.Errorf("#%d: ok = %v, want %v", i, g, tt.wok) 437 } 438 if s != tt.wseq { 439 t.Errorf("#%d: seq = %d, want %d", i, s, tt.wseq) 440 } 441 if index != tt.windex { 442 t.Errorf("#%d: index = %d, want %d", i, index, tt.windex) 443 } 444 } 445 } 446 447 func TestRecoverAfterCut(t *testing.T) { 448 p, err := ioutil.TempDir(os.TempDir(), "waltest") 449 if err != nil { 450 t.Fatal(err) 451 } 452 defer os.RemoveAll(p) 453 454 md, err := Create(p, []byte("metadata")) 455 if err != nil { 456 t.Fatal(err) 457 } 458 for i := 0; i < 10; i++ { 459 if err = md.SaveSnapshot(walpb.Snapshot{Index: uint64(i)}); err != nil { 460 t.Fatal(err) 461 } 462 es := []raftpb.Entry{{Index: uint64(i)}} 463 if err = md.Save(raftpb.HardState{}, es); err != nil { 464 t.Fatal(err) 465 } 466 if err = md.cut(); err != nil { 467 t.Fatal(err) 468 } 469 } 470 md.Close() 471 472 if err := os.Remove(filepath.Join(p, walName(4, 4))); err != nil { 473 t.Fatal(err) 474 } 475 476 for i := 0; i < 10; i++ { 477 w, err := Open(p, walpb.Snapshot{Index: uint64(i)}) 478 if err != nil { 479 if i <= 4 { 480 if err != ErrFileNotFound { 481 t.Errorf("#%d: err = %v, want %v", i, err, ErrFileNotFound) 482 } 483 } else { 484 t.Errorf("#%d: err = %v, want nil", i, err) 485 } 486 continue 487 } 488 metadata, _, entries, err := w.ReadAll() 489 if err != nil { 490 t.Errorf("#%d: err = %v, want nil", i, err) 491 continue 492 } 493 if !bytes.Equal(metadata, []byte("metadata")) { 494 t.Errorf("#%d: metadata = %s, want %s", i, metadata, "metadata") 495 } 496 for j, e := range entries { 497 if e.Index != uint64(j+i+1) { 498 t.Errorf("#%d: ents[%d].Index = %+v, want %+v", i, j, e.Index, j+i+1) 499 } 500 } 501 w.Close() 502 } 503 } 504 505 func TestOpenAtUncommittedIndex(t *testing.T) { 506 p, err := ioutil.TempDir(os.TempDir(), "waltest") 507 if err != nil { 508 t.Fatal(err) 509 } 510 defer os.RemoveAll(p) 511 512 w, err := Create(p, nil) 513 if err != nil { 514 t.Fatal(err) 515 } 516 if err = w.SaveSnapshot(walpb.Snapshot{}); err != nil { 517 t.Fatal(err) 518 } 519 if err = w.Save(raftpb.HardState{}, []raftpb.Entry{{Index: 0}}); err != nil { 520 t.Fatal(err) 521 } 522 w.Close() 523 524 w, err = Open(p, walpb.Snapshot{}) 525 if err != nil { 526 t.Fatal(err) 527 } 528 // commit up to index 0, try to read index 1 529 if _, _, _, err = w.ReadAll(); err != nil { 530 t.Errorf("err = %v, want nil", err) 531 } 532 w.Close() 533 } 534 535 // TestOpenForRead tests that OpenForRead can load all files. 536 // The tests creates WAL directory, and cut out multiple WAL files. Then 537 // it releases the lock of part of data, and excepts that OpenForRead 538 // can read out all files even if some are locked for write. 539 func TestOpenForRead(t *testing.T) { 540 p, err := ioutil.TempDir(os.TempDir(), "waltest") 541 if err != nil { 542 t.Fatal(err) 543 } 544 defer os.RemoveAll(p) 545 // create WAL 546 w, err := Create(p, nil) 547 if err != nil { 548 t.Fatal(err) 549 } 550 defer w.Close() 551 // make 10 separate files 552 for i := 0; i < 10; i++ { 553 es := []raftpb.Entry{{Index: uint64(i)}} 554 if err = w.Save(raftpb.HardState{}, es); err != nil { 555 t.Fatal(err) 556 } 557 if err = w.cut(); err != nil { 558 t.Fatal(err) 559 } 560 } 561 // release the lock to 5 562 unlockIndex := uint64(5) 563 w.ReleaseLockTo(unlockIndex) 564 565 // All are available for read 566 w2, err := OpenForRead(p, walpb.Snapshot{}) 567 if err != nil { 568 t.Fatal(err) 569 } 570 defer w2.Close() 571 _, _, ents, err := w2.ReadAll() 572 if err != nil { 573 t.Fatalf("err = %v, want nil", err) 574 } 575 if g := ents[len(ents)-1].Index; g != 9 { 576 t.Errorf("last index read = %d, want %d", g, 9) 577 } 578 } 579 580 func TestOpenWithMaxIndex(t *testing.T) { 581 p, err := ioutil.TempDir(os.TempDir(), "waltest") 582 if err != nil { 583 t.Fatal(err) 584 } 585 defer os.RemoveAll(p) 586 // create WAL 587 w, err := Create(p, nil) 588 if err != nil { 589 t.Fatal(err) 590 } 591 defer w.Close() 592 593 es := []raftpb.Entry{{Index: uint64(math.MaxInt64)}} 594 if err = w.Save(raftpb.HardState{}, es); err != nil { 595 t.Fatal(err) 596 } 597 w.Close() 598 599 w, err = Open(p, walpb.Snapshot{}) 600 if err != nil { 601 t.Fatal(err) 602 } 603 _, _, _, err = w.ReadAll() 604 if err == nil || err != ErrSliceOutOfRange { 605 t.Fatalf("err = %v, want ErrSliceOutOfRange", err) 606 } 607 } 608 609 func TestSaveEmpty(t *testing.T) { 610 var buf bytes.Buffer 611 var est raftpb.HardState 612 w := WAL{ 613 encoder: newEncoder(&buf, 0, 0), 614 } 615 if err := w.saveState(&est); err != nil { 616 t.Errorf("err = %v, want nil", err) 617 } 618 if len(buf.Bytes()) != 0 { 619 t.Errorf("buf.Bytes = %d, want 0", len(buf.Bytes())) 620 } 621 } 622 623 func TestReleaseLockTo(t *testing.T) { 624 p, err := ioutil.TempDir(os.TempDir(), "waltest") 625 if err != nil { 626 t.Fatal(err) 627 } 628 defer os.RemoveAll(p) 629 // create WAL 630 w, err := Create(p, nil) 631 defer func() { 632 if err = w.Close(); err != nil { 633 t.Fatal(err) 634 } 635 }() 636 if err != nil { 637 t.Fatal(err) 638 } 639 640 // release nothing if no files 641 err = w.ReleaseLockTo(10) 642 if err != nil { 643 t.Errorf("err = %v, want nil", err) 644 } 645 646 // make 10 separate files 647 for i := 0; i < 10; i++ { 648 es := []raftpb.Entry{{Index: uint64(i)}} 649 if err = w.Save(raftpb.HardState{}, es); err != nil { 650 t.Fatal(err) 651 } 652 if err = w.cut(); err != nil { 653 t.Fatal(err) 654 } 655 } 656 // release the lock to 5 657 unlockIndex := uint64(5) 658 w.ReleaseLockTo(unlockIndex) 659 660 // expected remaining are 4,5,6,7,8,9,10 661 if len(w.locks) != 7 { 662 t.Errorf("len(w.locks) = %d, want %d", len(w.locks), 7) 663 } 664 for i, l := range w.locks { 665 var lockIndex uint64 666 _, lockIndex, err = parseWalName(filepath.Base(l.Name())) 667 if err != nil { 668 t.Fatal(err) 669 } 670 671 if lockIndex != uint64(i+4) { 672 t.Errorf("#%d: lockindex = %d, want %d", i, lockIndex, uint64(i+4)) 673 } 674 } 675 676 // release the lock to 15 677 unlockIndex = uint64(15) 678 w.ReleaseLockTo(unlockIndex) 679 680 // expected remaining is 10 681 if len(w.locks) != 1 { 682 t.Errorf("len(w.locks) = %d, want %d", len(w.locks), 1) 683 } 684 _, lockIndex, err := parseWalName(filepath.Base(w.locks[0].Name())) 685 if err != nil { 686 t.Fatal(err) 687 } 688 689 if lockIndex != uint64(10) { 690 t.Errorf("lockindex = %d, want %d", lockIndex, 10) 691 } 692 } 693 694 // TestTailWriteNoSlackSpace ensures that tail writes append if there's no preallocated space. 695 func TestTailWriteNoSlackSpace(t *testing.T) { 696 p, err := ioutil.TempDir(os.TempDir(), "waltest") 697 if err != nil { 698 t.Fatal(err) 699 } 700 defer os.RemoveAll(p) 701 702 // create initial WAL 703 w, err := Create(p, []byte("metadata")) 704 if err != nil { 705 t.Fatal(err) 706 } 707 // write some entries 708 for i := 1; i <= 5; i++ { 709 es := []raftpb.Entry{{Index: uint64(i), Term: 1, Data: []byte{byte(i)}}} 710 if err = w.Save(raftpb.HardState{Term: 1}, es); err != nil { 711 t.Fatal(err) 712 } 713 } 714 // get rid of slack space by truncating file 715 off, serr := w.tail().Seek(0, io.SeekCurrent) 716 if serr != nil { 717 t.Fatal(serr) 718 } 719 if terr := w.tail().Truncate(off); terr != nil { 720 t.Fatal(terr) 721 } 722 w.Close() 723 724 // open, write more 725 w, err = Open(p, walpb.Snapshot{}) 726 if err != nil { 727 t.Fatal(err) 728 } 729 _, _, ents, rerr := w.ReadAll() 730 if rerr != nil { 731 t.Fatal(rerr) 732 } 733 if len(ents) != 5 { 734 t.Fatalf("got entries %+v, expected 5 entries", ents) 735 } 736 // write more entries 737 for i := 6; i <= 10; i++ { 738 es := []raftpb.Entry{{Index: uint64(i), Term: 1, Data: []byte{byte(i)}}} 739 if err = w.Save(raftpb.HardState{Term: 1}, es); err != nil { 740 t.Fatal(err) 741 } 742 } 743 w.Close() 744 745 // confirm all writes 746 w, err = Open(p, walpb.Snapshot{}) 747 if err != nil { 748 t.Fatal(err) 749 } 750 _, _, ents, rerr = w.ReadAll() 751 if rerr != nil { 752 t.Fatal(rerr) 753 } 754 if len(ents) != 10 { 755 t.Fatalf("got entries %+v, expected 10 entries", ents) 756 } 757 w.Close() 758 } 759 760 // TestRestartCreateWal ensures that an interrupted WAL initialization is clobbered on restart 761 func TestRestartCreateWal(t *testing.T) { 762 p, err := ioutil.TempDir(os.TempDir(), "waltest") 763 if err != nil { 764 t.Fatal(err) 765 } 766 defer os.RemoveAll(p) 767 768 // make temporary directory so it looks like initialization is interrupted 769 tmpdir := filepath.Clean(p) + ".tmp" 770 if err = os.Mkdir(tmpdir, fileutil.PrivateDirMode); err != nil { 771 t.Fatal(err) 772 } 773 if _, err = os.OpenFile(filepath.Join(tmpdir, "test"), os.O_WRONLY|os.O_CREATE, fileutil.PrivateFileMode); err != nil { 774 t.Fatal(err) 775 } 776 777 w, werr := Create(p, []byte("abc")) 778 if werr != nil { 779 t.Fatal(werr) 780 } 781 w.Close() 782 if Exist(tmpdir) { 783 t.Fatalf("got %q exists, expected it to not exist", tmpdir) 784 } 785 786 if w, err = OpenForRead(p, walpb.Snapshot{}); err != nil { 787 t.Fatal(err) 788 } 789 defer w.Close() 790 791 if meta, _, _, rerr := w.ReadAll(); rerr != nil || string(meta) != "abc" { 792 t.Fatalf("got error %v and meta %q, expected nil and %q", rerr, meta, "abc") 793 } 794 } 795 796 // TestOpenOnTornWrite ensures that entries past the torn write are truncated. 797 func TestOpenOnTornWrite(t *testing.T) { 798 maxEntries := 40 799 clobberIdx := 20 800 overwriteEntries := 5 801 802 p, err := ioutil.TempDir(os.TempDir(), "waltest") 803 if err != nil { 804 t.Fatal(err) 805 } 806 defer os.RemoveAll(p) 807 w, err := Create(p, nil) 808 defer func() { 809 if err = w.Close(); err != nil && err != os.ErrInvalid { 810 t.Fatal(err) 811 } 812 }() 813 if err != nil { 814 t.Fatal(err) 815 } 816 817 // get offset of end of each saved entry 818 offsets := make([]int64, maxEntries) 819 for i := range offsets { 820 es := []raftpb.Entry{{Index: uint64(i)}} 821 if err = w.Save(raftpb.HardState{}, es); err != nil { 822 t.Fatal(err) 823 } 824 if offsets[i], err = w.tail().Seek(0, io.SeekCurrent); err != nil { 825 t.Fatal(err) 826 } 827 } 828 829 fn := filepath.Join(p, filepath.Base(w.tail().Name())) 830 w.Close() 831 832 // clobber some entry with 0's to simulate a torn write 833 f, ferr := os.OpenFile(fn, os.O_WRONLY, fileutil.PrivateFileMode) 834 if ferr != nil { 835 t.Fatal(ferr) 836 } 837 defer f.Close() 838 _, err = f.Seek(offsets[clobberIdx], io.SeekStart) 839 if err != nil { 840 t.Fatal(err) 841 } 842 zeros := make([]byte, offsets[clobberIdx+1]-offsets[clobberIdx]) 843 _, err = f.Write(zeros) 844 if err != nil { 845 t.Fatal(err) 846 } 847 f.Close() 848 849 w, err = Open(p, walpb.Snapshot{}) 850 if err != nil { 851 t.Fatal(err) 852 } 853 // seek up to clobbered entry 854 _, _, _, err = w.ReadAll() 855 if err != nil { 856 t.Fatal(err) 857 } 858 859 // write a few entries past the clobbered entry 860 for i := 0; i < overwriteEntries; i++ { 861 // Index is different from old, truncated entries 862 es := []raftpb.Entry{{Index: uint64(i + clobberIdx), Data: []byte("new")}} 863 if err = w.Save(raftpb.HardState{}, es); err != nil { 864 t.Fatal(err) 865 } 866 } 867 w.Close() 868 869 // read back the entries, confirm number of entries matches expectation 870 w, err = OpenForRead(p, walpb.Snapshot{}) 871 if err != nil { 872 t.Fatal(err) 873 } 874 875 _, _, ents, rerr := w.ReadAll() 876 if rerr != nil { 877 // CRC error? the old entries were likely never truncated away 878 t.Fatal(rerr) 879 } 880 wEntries := (clobberIdx - 1) + overwriteEntries 881 if len(ents) != wEntries { 882 t.Fatalf("expected len(ents) = %d, got %d", wEntries, len(ents)) 883 } 884 } 885 886 // TestValidSnapshotEntries ensures ValidSnapshotEntries returns all valid wal snapshot entries, accounting 887 // for hardstate 888 func TestValidSnapshotEntries(t *testing.T) { 889 p, err := ioutil.TempDir(os.TempDir(), "waltest") 890 if err != nil { 891 t.Fatal(err) 892 } 893 defer os.RemoveAll(p) 894 snap0 := walpb.Snapshot{Index: 0, Term: 0} 895 snap1 := walpb.Snapshot{Index: 1, Term: 1} 896 state1 := raftpb.HardState{Commit: 1, Term: 1} 897 snap2 := walpb.Snapshot{Index: 2, Term: 1} 898 snap3 := walpb.Snapshot{Index: 3, Term: 2} 899 state2 := raftpb.HardState{Commit: 3, Term: 2} 900 snap4 := walpb.Snapshot{Index: 4, Term: 2} // will be orphaned since the last committed entry will be snap3 901 func() { 902 var w *WAL 903 w, err = Create(p, nil) 904 if err != nil { 905 t.Fatal(err) 906 } 907 defer w.Close() 908 909 // snap0 is implicitly created at index 0, term 0 910 if err = w.SaveSnapshot(snap1); err != nil { 911 t.Fatal(err) 912 } 913 if err = w.Save(state1, nil); err != nil { 914 t.Fatal(err) 915 } 916 if err = w.SaveSnapshot(snap2); err != nil { 917 t.Fatal(err) 918 } 919 if err = w.SaveSnapshot(snap3); err != nil { 920 t.Fatal(err) 921 } 922 if err = w.Save(state2, nil); err != nil { 923 t.Fatal(err) 924 } 925 if err = w.SaveSnapshot(snap4); err != nil { 926 t.Fatal(err) 927 } 928 }() 929 walSnaps, serr := ValidSnapshotEntries(p) 930 if serr != nil { 931 t.Fatal(serr) 932 } 933 expected := []walpb.Snapshot{snap0, snap1, snap2, snap3} 934 if !reflect.DeepEqual(walSnaps, expected) { 935 t.Errorf("expected walSnaps %+v, got %+v", expected, walSnaps) 936 } 937 } 938 939 // TestValidSnapshotEntriesAfterPurgeWal ensure that there are many wal files, and after cleaning the first wal file, 940 // it can work well. 941 func TestValidSnapshotEntriesAfterPurgeWal(t *testing.T) { 942 oldSegmentSizeBytes := SegmentSizeBytes 943 SegmentSizeBytes = 64 944 defer func() { 945 SegmentSizeBytes = oldSegmentSizeBytes 946 }() 947 p, err := ioutil.TempDir(os.TempDir(), "waltest") 948 if err != nil { 949 t.Fatal(err) 950 } 951 defer os.RemoveAll(p) 952 snap0 := walpb.Snapshot{Index: 0, Term: 0} 953 snap1 := walpb.Snapshot{Index: 1, Term: 1} 954 state1 := raftpb.HardState{Commit: 1, Term: 1} 955 snap2 := walpb.Snapshot{Index: 2, Term: 1} 956 snap3 := walpb.Snapshot{Index: 3, Term: 2} 957 state2 := raftpb.HardState{Commit: 3, Term: 2} 958 func() { 959 w, werr := Create(p, nil) 960 if werr != nil { 961 t.Fatal(werr) 962 } 963 defer w.Close() 964 965 // snap0 is implicitly created at index 0, term 0 966 if err = w.SaveSnapshot(snap1); err != nil { 967 t.Fatal(err) 968 } 969 if err = w.Save(state1, nil); err != nil { 970 t.Fatal(err) 971 } 972 if err = w.SaveSnapshot(snap2); err != nil { 973 t.Fatal(err) 974 } 975 if err = w.SaveSnapshot(snap3); err != nil { 976 t.Fatal(err) 977 } 978 for i := 0; i < 128; i++ { 979 if err = w.Save(state2, nil); err != nil { 980 t.Fatal(err) 981 } 982 } 983 }() 984 files, _, ferr := selectWALFiles(p, snap0) 985 if ferr != nil { 986 t.Fatal(ferr) 987 } 988 os.Remove(p + "/" + files[0]) 989 _, err = ValidSnapshotEntries(p) 990 if err != nil { 991 t.Fatal(err) 992 } 993 } 994 995 // TestReadAllFail ensure ReadAll error if used without opening the WAL 996 func TestReadAllFail(t *testing.T) { 997 dir, err := ioutil.TempDir(os.TempDir(), "waltest") 998 if err != nil { 999 t.Fatal(err) 1000 } 1001 defer os.RemoveAll(dir) 1002 1003 // create initial WAL 1004 f, err := Create(dir, []byte("metadata")) 1005 if err != nil { 1006 t.Fatal(err) 1007 } 1008 f.Close() 1009 // try to read without opening the WAL 1010 _, _, _, err = f.ReadAll() 1011 if err == nil || err != ErrDecoderNotFound { 1012 t.Fatalf("err = %v, want ErrDecoderNotFound", err) 1013 } 1014 }