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 }