From af0f34c595e5978f97e010e446c7975fc58a8e58 Mon Sep 17 00:00:00 2001 From: Yicheng Qin Date: Tue, 2 Dec 2014 23:35:44 -0800 Subject: [PATCH] wal: save latest state into new WAL So we could always read out state when open at valid index. --- wal/wal.go | 11 ++++++++--- wal/wal_test.go | 24 +++++++++++++++++++++++- 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/wal/wal.go b/wal/wal.go index ddbc10dce..841e6ca26 100644 --- a/wal/wal.go +++ b/wal/wal.go @@ -56,8 +56,9 @@ var ( // 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. 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 decoder *decoder // decoder to decode records @@ -236,7 +237,10 @@ func (w *WAL) Cut() error { if err := w.saveCrc(prevCrc); err != nil { 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 { @@ -274,6 +278,7 @@ func (w *WAL) SaveState(s *raftpb.HardState) error { if raft.IsEmptyHardState(*s) { return nil } + w.state = *s b := pbutil.MustMarshal(s) rec := &walpb.Record{Type: stateType, Data: b} return w.encoder.encode(rec) diff --git a/wal/wal_test.go b/wal/wal_test.go index fcc520901..4372424a1 100644 --- a/wal/wal_test.go +++ b/wal/wal_test.go @@ -142,12 +142,15 @@ func TestCut(t *testing.T) { if err != nil { t.Fatal(err) } - defer w.Close() // TODO(unihorn): remove this when cut can operate on an empty file if err := w.SaveEntry(&raftpb.Entry{}); err != nil { t.Fatal(err) } + state := raftpb.HardState{Term: 1} + if err := w.SaveState(&state); err != nil { + t.Fatal(err) + } if err := w.Cut(); err != nil { t.Fatal(err) } @@ -167,6 +170,25 @@ func TestCut(t *testing.T) { if g := path.Base(w.f.Name()); 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) {