github.com/df-mc/goleveldb@v1.1.9/leveldb/corrupt_test.go (about)

     1  // Copyright (c) 2013, Suryandaru Triandana <syndtr@gmail.com>
     2  // All rights reserved.
     3  //
     4  // Use of this source code is governed by a BSD-style license that can be
     5  // found in the LICENSE file.
     6  
     7  package leveldb
     8  
     9  import (
    10  	"bytes"
    11  	"fmt"
    12  	"io"
    13  	"math/rand"
    14  	"testing"
    15  	"time"
    16  
    17  	"github.com/df-mc/goleveldb/leveldb/filter"
    18  	"github.com/df-mc/goleveldb/leveldb/opt"
    19  	"github.com/df-mc/goleveldb/leveldb/storage"
    20  )
    21  
    22  const ctValSize = 1000
    23  
    24  type dbCorruptHarness struct {
    25  	dbHarness
    26  }
    27  
    28  func newDbCorruptHarnessWopt(t *testing.T, o *opt.Options) *dbCorruptHarness {
    29  	h := new(dbCorruptHarness)
    30  	h.init(t, o)
    31  	return h
    32  }
    33  
    34  func newDbCorruptHarness(t *testing.T) *dbCorruptHarness {
    35  	return newDbCorruptHarnessWopt(t, &opt.Options{
    36  		BlockCacheCapacity: 100,
    37  		Strict:             opt.StrictJournalChecksum,
    38  	})
    39  }
    40  
    41  func (h *dbCorruptHarness) recover() {
    42  	p := &h.dbHarness
    43  	t := p.t
    44  
    45  	var err error
    46  	p.db, err = Recover(h.stor, h.o)
    47  	if err != nil {
    48  		t.Fatal("Repair: got error: ", err)
    49  	}
    50  }
    51  
    52  func (h *dbCorruptHarness) build(n int) {
    53  	p := &h.dbHarness
    54  	t := p.t
    55  	db := p.db
    56  
    57  	batch := new(Batch)
    58  	for i := 0; i < n; i++ {
    59  		batch.Reset()
    60  		batch.Put(tkey(i), tval(i, ctValSize))
    61  		err := db.Write(batch, p.wo)
    62  		if err != nil {
    63  			t.Fatal("write error: ", err)
    64  		}
    65  	}
    66  }
    67  
    68  func (h *dbCorruptHarness) buildShuffled(n int, rnd *rand.Rand) {
    69  	p := &h.dbHarness
    70  	t := p.t
    71  	db := p.db
    72  
    73  	batch := new(Batch)
    74  	for i := range rnd.Perm(n) {
    75  		batch.Reset()
    76  		batch.Put(tkey(i), tval(i, ctValSize))
    77  		err := db.Write(batch, p.wo)
    78  		if err != nil {
    79  			t.Fatal("write error: ", err)
    80  		}
    81  	}
    82  }
    83  
    84  func (h *dbCorruptHarness) deleteRand(n, max int, rnd *rand.Rand) {
    85  	p := &h.dbHarness
    86  	t := p.t
    87  	db := p.db
    88  
    89  	batch := new(Batch)
    90  	for i := 0; i < n; i++ {
    91  		batch.Reset()
    92  		batch.Delete(tkey(rnd.Intn(max)))
    93  		err := db.Write(batch, p.wo)
    94  		if err != nil {
    95  			t.Fatal("write error: ", err)
    96  		}
    97  	}
    98  }
    99  
   100  func (h *dbCorruptHarness) corrupt(ft storage.FileType, fi, offset, n int) {
   101  	p := &h.dbHarness
   102  	t := p.t
   103  
   104  	fds, _ := p.stor.List(ft)
   105  	sortFds(fds)
   106  	if fi < 0 {
   107  		fi = len(fds) - 1
   108  	}
   109  	if fi >= len(fds) {
   110  		t.Fatalf("no such file with type %q with index %d", ft, fi)
   111  	}
   112  
   113  	fd := fds[fi]
   114  	r, err := h.stor.Open(fd)
   115  	if err != nil {
   116  		t.Fatal("cannot open file: ", err)
   117  	}
   118  	x, err := r.Seek(0, 2)
   119  	if err != nil {
   120  		t.Fatal("cannot query file size: ", err)
   121  	}
   122  	m := int(x)
   123  	if _, err := r.Seek(0, 0); err != nil {
   124  		t.Fatal(err)
   125  	}
   126  
   127  	if offset < 0 {
   128  		if -offset > m {
   129  			offset = 0
   130  		} else {
   131  			offset = m + offset
   132  		}
   133  	}
   134  	if offset > m {
   135  		offset = m
   136  	}
   137  	if offset+n > m {
   138  		n = m - offset
   139  	}
   140  
   141  	buf := make([]byte, m)
   142  	_, err = io.ReadFull(r, buf)
   143  	if err != nil {
   144  		t.Fatal("cannot read file: ", err)
   145  	}
   146  	r.Close()
   147  
   148  	for i := 0; i < n; i++ {
   149  		buf[offset+i] ^= 0x80
   150  	}
   151  
   152  	err = h.stor.Remove(fd)
   153  	if err != nil {
   154  		t.Fatal("cannot remove old file: ", err)
   155  	}
   156  	w, err := h.stor.Create(fd)
   157  	if err != nil {
   158  		t.Fatal("cannot create new file: ", err)
   159  	}
   160  	_, err = w.Write(buf)
   161  	if err != nil {
   162  		t.Fatal("cannot write new file: ", err)
   163  	}
   164  	w.Close()
   165  }
   166  
   167  func (h *dbCorruptHarness) removeAll(ft storage.FileType) {
   168  	fds, err := h.stor.List(ft)
   169  	if err != nil {
   170  		h.t.Fatal("get files: ", err)
   171  	}
   172  	for _, fd := range fds {
   173  		if err := h.stor.Remove(fd); err != nil {
   174  			h.t.Error("remove file: ", err)
   175  		}
   176  	}
   177  }
   178  
   179  func (h *dbCorruptHarness) forceRemoveAll(ft storage.FileType) {
   180  	fds, err := h.stor.List(ft)
   181  	if err != nil {
   182  		h.t.Fatal("get files: ", err)
   183  	}
   184  	for _, fd := range fds {
   185  		if err := h.stor.ForceRemove(fd); err != nil {
   186  			h.t.Error("remove file: ", err)
   187  		}
   188  	}
   189  }
   190  
   191  func (h *dbCorruptHarness) removeOne(ft storage.FileType) {
   192  	fds, err := h.stor.List(ft)
   193  	if err != nil {
   194  		h.t.Fatal("get files: ", err)
   195  	}
   196  	fd := fds[rand.Intn(len(fds))]
   197  	h.t.Logf("removing file @%d", fd.Num)
   198  	if err := h.stor.Remove(fd); err != nil {
   199  		h.t.Error("remove file: ", err)
   200  	}
   201  }
   202  
   203  func (h *dbCorruptHarness) check(min, max int) {
   204  	p := &h.dbHarness
   205  	t := p.t
   206  	db := p.db
   207  
   208  	var n, badk, badv, missed, good int
   209  	iter := db.NewIterator(nil, p.ro)
   210  	for iter.Next() {
   211  		k := 0
   212  		fmt.Sscanf(string(iter.Key()), "%d", &k)
   213  		if k < n {
   214  			badk++
   215  			continue
   216  		}
   217  		missed += k - n
   218  		n = k + 1
   219  		if !bytes.Equal(iter.Value(), tval(k, ctValSize)) {
   220  			badv++
   221  		} else {
   222  			good++
   223  		}
   224  	}
   225  	err := iter.Error()
   226  	iter.Release()
   227  	t.Logf("want=%d..%d got=%d badkeys=%d badvalues=%d missed=%d, err=%v",
   228  		min, max, good, badk, badv, missed, err)
   229  	if good < min || good > max {
   230  		t.Errorf("good entries number not in range")
   231  	}
   232  }
   233  
   234  func TestCorruptDB_Journal(t *testing.T) {
   235  	h := newDbCorruptHarness(t)
   236  	defer h.close()
   237  
   238  	h.build(100)
   239  	h.check(100, 100)
   240  	h.closeDB()
   241  	h.corrupt(storage.TypeJournal, -1, 19, 1)
   242  	h.corrupt(storage.TypeJournal, -1, 32*1024+1000, 1)
   243  
   244  	h.openDB()
   245  	h.check(36, 36)
   246  }
   247  
   248  func TestCorruptDB_Table(t *testing.T) {
   249  	h := newDbCorruptHarness(t)
   250  	defer h.close()
   251  
   252  	h.build(100)
   253  	h.compactMem()
   254  	h.compactRangeAt(0, "", "")
   255  	h.compactRangeAt(1, "", "")
   256  	h.closeDB()
   257  	h.corrupt(storage.TypeTable, -1, 100, 1)
   258  
   259  	h.openDB()
   260  	h.check(99, 99)
   261  }
   262  
   263  func TestCorruptDB_TableIndex(t *testing.T) {
   264  	h := newDbCorruptHarness(t)
   265  	defer h.close()
   266  
   267  	h.build(10000)
   268  	h.compactMem()
   269  	h.closeDB()
   270  	h.corrupt(storage.TypeTable, -1, -2000, 500)
   271  
   272  	h.openDB()
   273  	h.check(5000, 9999)
   274  }
   275  
   276  func TestCorruptDB_MissingManifest(t *testing.T) {
   277  	rnd := rand.New(rand.NewSource(0x0badda7a))
   278  	h := newDbCorruptHarnessWopt(t, &opt.Options{
   279  		BlockCacheCapacity: 100,
   280  		Strict:             opt.StrictJournalChecksum,
   281  		WriteBuffer:        1000 * 60,
   282  	})
   283  	defer h.close()
   284  
   285  	h.build(1000)
   286  	h.compactMem()
   287  	h.buildShuffled(1000, rnd)
   288  	h.compactMem()
   289  	h.deleteRand(500, 1000, rnd)
   290  	h.compactMem()
   291  	h.buildShuffled(1000, rnd)
   292  	h.compactMem()
   293  	h.deleteRand(500, 1000, rnd)
   294  	h.compactMem()
   295  	h.buildShuffled(1000, rnd)
   296  	h.compactMem()
   297  	h.closeDB()
   298  
   299  	h.forceRemoveAll(storage.TypeManifest)
   300  	h.openAssert(false)
   301  
   302  	h.recover()
   303  	h.check(1000, 1000)
   304  	h.build(1000)
   305  	h.compactMem()
   306  	h.compactRange("", "")
   307  	h.closeDB()
   308  
   309  	h.recover()
   310  	h.check(1000, 1000)
   311  }
   312  
   313  func TestCorruptDB_SequenceNumberRecovery(t *testing.T) {
   314  	h := newDbCorruptHarness(t)
   315  	defer h.close()
   316  
   317  	h.put("foo", "v1")
   318  	h.put("foo", "v2")
   319  	h.put("foo", "v3")
   320  	h.put("foo", "v4")
   321  	h.put("foo", "v5")
   322  	h.closeDB()
   323  
   324  	h.recover()
   325  	h.getVal("foo", "v5")
   326  	h.put("foo", "v6")
   327  	h.getVal("foo", "v6")
   328  
   329  	h.reopenDB()
   330  	h.getVal("foo", "v6")
   331  }
   332  
   333  func TestCorruptDB_SequenceNumberRecoveryTable(t *testing.T) {
   334  	h := newDbCorruptHarness(t)
   335  	defer h.close()
   336  
   337  	h.put("foo", "v1")
   338  	h.put("foo", "v2")
   339  	h.put("foo", "v3")
   340  	h.compactMem()
   341  	h.put("foo", "v4")
   342  	h.put("foo", "v5")
   343  	h.compactMem()
   344  	h.closeDB()
   345  
   346  	h.recover()
   347  	h.getVal("foo", "v5")
   348  	h.put("foo", "v6")
   349  	h.getVal("foo", "v6")
   350  
   351  	h.reopenDB()
   352  	h.getVal("foo", "v6")
   353  }
   354  
   355  func TestCorruptDB_CorruptedManifest(t *testing.T) {
   356  	h := newDbCorruptHarness(t)
   357  	defer h.close()
   358  
   359  	h.put("foo", "hello")
   360  	h.compactMem()
   361  	h.compactRange("", "")
   362  	h.closeDB()
   363  	h.corrupt(storage.TypeManifest, -1, 0, 1000)
   364  	h.openAssert(false)
   365  
   366  	h.recover()
   367  	h.getVal("foo", "hello")
   368  }
   369  
   370  func TestCorruptDB_CompactionInputError(t *testing.T) {
   371  	h := newDbCorruptHarness(t)
   372  	defer h.close()
   373  
   374  	h.build(10)
   375  	h.compactMem()
   376  	h.closeDB()
   377  	h.corrupt(storage.TypeTable, -1, 100, 1)
   378  
   379  	h.openDB()
   380  	h.check(9, 9)
   381  
   382  	h.build(10000)
   383  	h.check(10000, 10000)
   384  }
   385  
   386  func TestCorruptDB_UnrelatedKeys(t *testing.T) {
   387  	h := newDbCorruptHarness(t)
   388  	defer h.close()
   389  
   390  	h.build(10)
   391  	h.compactMem()
   392  	h.closeDB()
   393  	h.corrupt(storage.TypeTable, -1, 100, 1)
   394  
   395  	h.openDB()
   396  	h.put(string(tkey(1000)), string(tval(1000, ctValSize)))
   397  	h.getVal(string(tkey(1000)), string(tval(1000, ctValSize)))
   398  	h.compactMem()
   399  	h.getVal(string(tkey(1000)), string(tval(1000, ctValSize)))
   400  }
   401  
   402  func TestCorruptDB_Level0NewerFileHasOlderSeqnum(t *testing.T) {
   403  	h := newDbCorruptHarness(t)
   404  	defer h.close()
   405  
   406  	h.put("a", "v1")
   407  	h.put("b", "v1")
   408  	h.compactMem()
   409  	h.put("a", "v2")
   410  	h.put("b", "v2")
   411  	h.compactMem()
   412  	h.put("a", "v3")
   413  	h.put("b", "v3")
   414  	h.compactMem()
   415  	h.put("c", "v0")
   416  	h.put("d", "v0")
   417  	h.compactMem()
   418  	h.compactRangeAt(1, "", "")
   419  	h.closeDB()
   420  
   421  	h.recover()
   422  	h.getVal("a", "v3")
   423  	h.getVal("b", "v3")
   424  	h.getVal("c", "v0")
   425  	h.getVal("d", "v0")
   426  }
   427  
   428  func TestCorruptDB_RecoverInvalidSeq_Issue53(t *testing.T) {
   429  	h := newDbCorruptHarness(t)
   430  	defer h.close()
   431  
   432  	h.put("a", "v1")
   433  	h.put("b", "v1")
   434  	h.compactMem()
   435  	h.put("a", "v2")
   436  	h.put("b", "v2")
   437  	h.compactMem()
   438  	h.put("a", "v3")
   439  	h.put("b", "v3")
   440  	h.compactMem()
   441  	h.put("c", "v0")
   442  	h.put("d", "v0")
   443  	h.compactMem()
   444  	h.compactRangeAt(0, "", "")
   445  	h.closeDB()
   446  
   447  	h.recover()
   448  	h.getVal("a", "v3")
   449  	h.getVal("b", "v3")
   450  	h.getVal("c", "v0")
   451  	h.getVal("d", "v0")
   452  }
   453  
   454  func TestCorruptDB_MissingTableFiles(t *testing.T) {
   455  	h := newDbCorruptHarness(t)
   456  	defer h.close()
   457  
   458  	h.put("a", "v1")
   459  	h.put("b", "v1")
   460  	h.compactMem()
   461  	h.put("c", "v2")
   462  	h.put("d", "v2")
   463  	h.compactMem()
   464  	h.put("e", "v3")
   465  	h.put("f", "v3")
   466  	h.closeDB()
   467  
   468  	h.removeOne(storage.TypeTable)
   469  	h.openAssert(false)
   470  }
   471  
   472  func TestCorruptDB_RecoverTable(t *testing.T) {
   473  	h := newDbCorruptHarnessWopt(t, &opt.Options{
   474  		WriteBuffer:         112 * opt.KiB,
   475  		CompactionTableSize: 90 * opt.KiB,
   476  		Filter:              filter.NewBloomFilter(10),
   477  	})
   478  	defer h.close()
   479  
   480  	h.build(1000)
   481  	h.compactMem()
   482  	h.compactRangeAt(0, "", "")
   483  	h.compactRangeAt(1, "", "")
   484  	seq := h.db.seq
   485  	time.Sleep(100 * time.Millisecond) // Wait lazy reference finish tasks
   486  	h.closeDB()
   487  	h.corrupt(storage.TypeTable, 0, 1000, 1)
   488  	h.corrupt(storage.TypeTable, 3, 10000, 1)
   489  	// Corrupted filter shouldn't affect recovery.
   490  	h.corrupt(storage.TypeTable, 3, 113888, 10)
   491  	h.corrupt(storage.TypeTable, -1, 20000, 1)
   492  
   493  	h.recover()
   494  	if h.db.seq != seq {
   495  		t.Errorf("invalid seq, want=%d got=%d", seq, h.db.seq)
   496  	}
   497  	h.check(985, 985)
   498  }