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  }