|
@@ -636,3 +636,89 @@ func TestRestartCreateWal(t *testing.T) {
|
|
|
t.Fatalf("got error %v and meta %q, expected nil and %q", rerr, meta, "abc")
|
|
t.Fatalf("got error %v and meta %q, expected nil and %q", rerr, meta, "abc")
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+// TestOpenOnTornWrite ensures that entries past the torn write are truncated.
|
|
|
|
|
+func TestOpenOnTornWrite(t *testing.T) {
|
|
|
|
|
+ maxEntries := 40
|
|
|
|
|
+ clobberIdx := 20
|
|
|
|
|
+ overwriteEntries := 5
|
|
|
|
|
+
|
|
|
|
|
+ p, err := ioutil.TempDir(os.TempDir(), "waltest")
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ t.Fatal(err)
|
|
|
|
|
+ }
|
|
|
|
|
+ defer os.RemoveAll(p)
|
|
|
|
|
+ w, err := Create(p, nil)
|
|
|
|
|
+ defer w.Close()
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ t.Fatal(err)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // get offset of end of each saved entry
|
|
|
|
|
+ offsets := make([]int64, maxEntries)
|
|
|
|
|
+ for i := range offsets {
|
|
|
|
|
+ es := []raftpb.Entry{{Index: uint64(i)}}
|
|
|
|
|
+ if err = w.Save(raftpb.HardState{}, es); err != nil {
|
|
|
|
|
+ t.Fatal(err)
|
|
|
|
|
+ }
|
|
|
|
|
+ if offsets[i], err = w.tail().Seek(0, os.SEEK_CUR); err != nil {
|
|
|
|
|
+ t.Fatal(err)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ fn := w.tail().Name()
|
|
|
|
|
+ w.Close()
|
|
|
|
|
+
|
|
|
|
|
+ // clobber some entry with 0's to simulate a torn write
|
|
|
|
|
+ f, ferr := os.OpenFile(fn, os.O_WRONLY, fileutil.PrivateFileMode)
|
|
|
|
|
+ if ferr != nil {
|
|
|
|
|
+ t.Fatal(ferr)
|
|
|
|
|
+ }
|
|
|
|
|
+ defer f.Close()
|
|
|
|
|
+ _, err = f.Seek(offsets[clobberIdx], os.SEEK_SET)
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ t.Fatal(err)
|
|
|
|
|
+ }
|
|
|
|
|
+ zeros := make([]byte, offsets[clobberIdx+1]-offsets[clobberIdx])
|
|
|
|
|
+ _, err = f.Write(zeros)
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ t.Fatal(err)
|
|
|
|
|
+ }
|
|
|
|
|
+ f.Close()
|
|
|
|
|
+
|
|
|
|
|
+ w, err = Open(p, walpb.Snapshot{})
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ t.Fatal(err)
|
|
|
|
|
+ }
|
|
|
|
|
+ // seek up to clobbered entry
|
|
|
|
|
+ _, _, _, err = w.ReadAll()
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ t.Fatal(err)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // write a few entries past the clobbered entry
|
|
|
|
|
+ for i := 0; i < overwriteEntries; i++ {
|
|
|
|
|
+ // Index is different from old, truncated entries
|
|
|
|
|
+ es := []raftpb.Entry{{Index: uint64(i + clobberIdx), Data: []byte("new")}}
|
|
|
|
|
+ if err = w.Save(raftpb.HardState{}, es); err != nil {
|
|
|
|
|
+ t.Fatal(err)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ w.Close()
|
|
|
|
|
+
|
|
|
|
|
+ // read back the entries, confirm number of entries matches expectation
|
|
|
|
|
+ w, err = OpenForRead(p, walpb.Snapshot{})
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ t.Fatal(err)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ _, _, ents, rerr := w.ReadAll()
|
|
|
|
|
+ if rerr != nil {
|
|
|
|
|
+ // CRC error? the old entries were likely never truncated away
|
|
|
|
|
+ t.Fatal(rerr)
|
|
|
|
|
+ }
|
|
|
|
|
+ wEntries := (clobberIdx - 1) + overwriteEntries
|
|
|
|
|
+ if len(ents) != wEntries {
|
|
|
|
|
+ t.Fatalf("expected len(ents) = %d, got %d", wEntries, len(ents))
|
|
|
|
|
+ }
|
|
|
|
|
+}
|