فهرست منبع

wal: save latest state into new WAL

So we could always read out state when open at valid index.
Yicheng Qin 11 سال پیش
والد
کامیت
af0f34c595
2فایلهای تغییر یافته به همراه31 افزوده شده و 4 حذف شده
  1. 8 3
      wal/wal.go
  2. 23 1
      wal/wal_test.go

+ 8 - 3
wal/wal.go

@@ -56,8 +56,9 @@ var (
 // A just opened WAL is in read mode, and ready for reading records.
 // A just opened WAL is in read mode, and ready for reading records.
 // The WAL will be ready for appending after reading out all the previous records.
 // The WAL will be ready for appending after reading out all the previous records.
 type WAL struct {
 type WAL struct {
-	dir      string // the living directory of the underlay files
-	metadata []byte // metadata recorded at the head of each WAL
+	dir      string           // the living directory of the underlay files
+	metadata []byte           // metadata recorded at the head of each WAL
+	state    raftpb.HardState // hardstate recorded at the head of WAL
 
 
 	ri      uint64   // index of entry to start reading
 	ri      uint64   // index of entry to start reading
 	decoder *decoder // decoder to decode records
 	decoder *decoder // decoder to decode records
@@ -236,7 +237,10 @@ func (w *WAL) Cut() error {
 	if err := w.saveCrc(prevCrc); err != nil {
 	if err := w.saveCrc(prevCrc); err != nil {
 		return err
 		return err
 	}
 	}
-	return w.encoder.encode(&walpb.Record{Type: metadataType, Data: w.metadata})
+	if err := w.encoder.encode(&walpb.Record{Type: metadataType, Data: w.metadata}); err != nil {
+		return err
+	}
+	return w.SaveState(&w.state)
 }
 }
 
 
 func (w *WAL) sync() error {
 func (w *WAL) sync() error {
@@ -274,6 +278,7 @@ func (w *WAL) SaveState(s *raftpb.HardState) error {
 	if raft.IsEmptyHardState(*s) {
 	if raft.IsEmptyHardState(*s) {
 		return nil
 		return nil
 	}
 	}
+	w.state = *s
 	b := pbutil.MustMarshal(s)
 	b := pbutil.MustMarshal(s)
 	rec := &walpb.Record{Type: stateType, Data: b}
 	rec := &walpb.Record{Type: stateType, Data: b}
 	return w.encoder.encode(rec)
 	return w.encoder.encode(rec)

+ 23 - 1
wal/wal_test.go

@@ -142,12 +142,15 @@ func TestCut(t *testing.T) {
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
-	defer w.Close()
 
 
 	// TODO(unihorn): remove this when cut can operate on an empty file
 	// TODO(unihorn): remove this when cut can operate on an empty file
 	if err := w.SaveEntry(&raftpb.Entry{}); err != nil {
 	if err := w.SaveEntry(&raftpb.Entry{}); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
+	state := raftpb.HardState{Term: 1}
+	if err := w.SaveState(&state); err != nil {
+		t.Fatal(err)
+	}
 	if err := w.Cut(); err != nil {
 	if err := w.Cut(); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -167,6 +170,25 @@ func TestCut(t *testing.T) {
 	if g := path.Base(w.f.Name()); g != wname {
 	if g := path.Base(w.f.Name()); g != wname {
 		t.Errorf("name = %s, want %s", g, wname)
 		t.Errorf("name = %s, want %s", g, wname)
 	}
 	}
+	w.Close()
+
+	// check the state in the last WAL
+	f, err := os.Open(path.Join(p, wname))
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer f.Close()
+	w = &WAL{
+		decoder: newDecoder(f),
+		ri:      2,
+	}
+	_, gst, _, err := w.ReadAll()
+	if err != nil {
+		t.Fatal(err)
+	}
+	if !reflect.DeepEqual(gst, state) {
+		t.Errorf("state = %+v, want %+v", gst, state)
+	}
 }
 }
 
 
 func TestRecover(t *testing.T) {
 func TestRecover(t *testing.T) {