corruption_test.cc 19.3 KB
Newer Older
1
//  Copyright (c) 2011-present, Facebook, Inc.  All rights reserved.
S
Siying Dong 已提交
2 3 4
//  This source code is licensed under both the GPLv2 (found in the
//  COPYING file in the root directory) and Apache 2.0 License
//  (found in the LICENSE.Apache file in the root directory).
5
//
J
jorlow@chromium.org 已提交
6 7 8 9
// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. See the AUTHORS file for names of contributors.

10 11
#ifndef ROCKSDB_LITE

J
jorlow@chromium.org 已提交
12 13 14 15
#include <errno.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
M
mrambacher 已提交
16

S
sdong 已提交
17
#include <cinttypes>
M
mrambacher 已提交
18

19
#include "db/db_impl/db_impl.h"
S
sdong 已提交
20
#include "db/db_test_util.h"
21 22
#include "db/log_format.h"
#include "db/version_set.h"
23
#include "env/composite_env_wrapper.h"
24
#include "file/filename.h"
25
#include "rocksdb/cache.h"
A
Aaron G 已提交
26
#include "rocksdb/convenience.h"
M
mrambacher 已提交
27
#include "rocksdb/db.h"
28
#include "rocksdb/env.h"
S
Siying Dong 已提交
29
#include "rocksdb/table.h"
30
#include "rocksdb/write_batch.h"
31
#include "table/block_based/block_based_table_builder.h"
32
#include "table/meta_blocks.h"
33
#include "table/mock_table.h"
34 35
#include "test_util/testharness.h"
#include "test_util/testutil.h"
M
mrambacher 已提交
36
#include "util/random.h"
37
#include "util/string_util.h"
J
jorlow@chromium.org 已提交
38

39
namespace ROCKSDB_NAMESPACE {
J
jorlow@chromium.org 已提交
40 41 42

static const int kValueSize = 1000;

I
Igor Sugak 已提交
43
class CorruptionTest : public testing::Test {
J
jorlow@chromium.org 已提交
44 45 46
 public:
  test::ErrorEnv env_;
  std::string dbname_;
47
  std::shared_ptr<Cache> tiny_cache_;
J
jorlow@chromium.org 已提交
48 49 50
  Options options_;
  DB* db_;

51
  CorruptionTest() {
52 53 54
    // If LRU cache shard bit is smaller than 2 (or -1 which will automatically
    // set it to 0), test SequenceNumberRecovery will fail, likely because of a
    // bug in recovery code. Keep it 4 for now to make the test passes.
55
    tiny_cache_ = NewLRUCache(100, 4);
S
sdong 已提交
56
    options_.wal_recovery_mode = WALRecoveryMode::kTolerateCorruptedTailRecords;
J
jorlow@chromium.org 已提交
57
    options_.env = &env_;
58
    dbname_ = test::PerThreadDBPath("corruption_test");
J
jorlow@chromium.org 已提交
59 60
    DestroyDB(dbname_, options_);

A
Abhishek Kona 已提交
61
    db_ = nullptr;
J
jorlow@chromium.org 已提交
62
    options_.create_if_missing = true;
63 64 65
    BlockBasedTableOptions table_options;
    table_options.block_size_deviation = 0;  // make unit test pass for now
    options_.table_factory.reset(NewBlockBasedTableFactory(table_options));
J
jorlow@chromium.org 已提交
66 67 68 69
    Reopen();
    options_.create_if_missing = false;
  }

70 71 72
  ~CorruptionTest() override {
    delete db_;
    DestroyDB(dbname_, Options());
J
jorlow@chromium.org 已提交
73 74
  }

D
Dmitri Smirnov 已提交
75 76 77 78 79
  void CloseDb() {
    delete db_;
    db_ = nullptr;
  }

A
Abhishek Kona 已提交
80
  Status TryReopen(Options* options = nullptr) {
J
jorlow@chromium.org 已提交
81
    delete db_;
A
Abhishek Kona 已提交
82
    db_ = nullptr;
J
jorlow@chromium.org 已提交
83
    Options opt = (options ? *options : options_);
S
sdong 已提交
84 85 86 87 88
    if (opt.env == Options().env) {
      // If env is not overridden, replace it with ErrorEnv.
      // Otherwise, the test already uses a non-default Env.
      opt.env = &env_;
    }
X
Xing Jin 已提交
89
    opt.arena_block_size = 4096;
90 91 92 93
    BlockBasedTableOptions table_options;
    table_options.block_cache = tiny_cache_;
    table_options.block_size_deviation = 0;
    opt.table_factory.reset(NewBlockBasedTableFactory(table_options));
J
jorlow@chromium.org 已提交
94 95 96
    return DB::Open(opt, dbname_, &db_);
  }

A
Abhishek Kona 已提交
97
  void Reopen(Options* options = nullptr) {
J
jorlow@chromium.org 已提交
98 99 100 101 102
    ASSERT_OK(TryReopen(options));
  }

  void RepairDB() {
    delete db_;
A
Abhishek Kona 已提交
103
    db_ = nullptr;
104
    ASSERT_OK(::ROCKSDB_NAMESPACE::RepairDB(dbname_, options_));
J
jorlow@chromium.org 已提交
105 106
  }

I
Igor Canadi 已提交
107
  void Build(int n, int flush_every = 0) {
J
jorlow@chromium.org 已提交
108 109 110
    std::string key_space, value_space;
    WriteBatch batch;
    for (int i = 0; i < n; i++) {
I
Igor Canadi 已提交
111
      if (flush_every != 0 && i != 0 && i % flush_every == 0) {
112
        DBImpl* dbi = static_cast_with_check<DBImpl>(db_);
I
Igor Canadi 已提交
113 114
        dbi->TEST_FlushMemTable();
      }
J
jorlow@chromium.org 已提交
115 116 117 118 119 120 121 122 123
      //if ((i % 100) == 0) fprintf(stderr, "@ %d of %d\n", i, n);
      Slice key = Key(i, &key_space);
      batch.Clear();
      batch.Put(key, Value(i, &value_space));
      ASSERT_OK(db_->Write(WriteOptions(), &batch));
    }
  }

  void Check(int min_expected, int max_expected) {
D
Dmitri Smirnov 已提交
124 125
    uint64_t next_expected = 0;
    uint64_t missed = 0;
J
jorlow@chromium.org 已提交
126 127 128 129
    int bad_keys = 0;
    int bad_values = 0;
    int correct = 0;
    std::string value_space;
130 131 132 133
    // Do not verify checksums. If we verify checksums then the
    // db itself will raise errors because data is corrupted.
    // Instead, we want the reads to be successful and this test
    // will detect whether the appropriate corruptions have
C
clark.kang 已提交
134
    // occurred.
135
    Iterator* iter = db_->NewIterator(ReadOptions(false, true));
J
jorlow@chromium.org 已提交
136 137 138 139 140 141 142 143 144 145
    for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
      uint64_t key;
      Slice in(iter->key());
      if (!ConsumeDecimalNumber(&in, &key) ||
          !in.empty() ||
          key < next_expected) {
        bad_keys++;
        continue;
      }
      missed += (key - next_expected);
D
Dmitri Smirnov 已提交
146
      next_expected = key + 1;
147
      if (iter->value() != Value(static_cast<int>(key), &value_space)) {
J
jorlow@chromium.org 已提交
148 149 150 151 152 153 154 155
        bad_values++;
      } else {
        correct++;
      }
    }
    delete iter;

    fprintf(stderr,
D
Dmitri Smirnov 已提交
156 157 158
      "expected=%d..%d; got=%d; bad_keys=%d; bad_values=%d; missed=%llu\n",
            min_expected, max_expected, correct, bad_keys, bad_values,
            static_cast<unsigned long long>(missed));
J
jorlow@chromium.org 已提交
159 160 161 162
    ASSERT_LE(min_expected, correct);
    ASSERT_GE(max_expected, correct);
  }

163 164 165 166 167 168 169 170
  void Corrupt(FileType filetype, int offset, int bytes_to_corrupt) {
    // Pick file to corrupt
    std::vector<std::string> filenames;
    ASSERT_OK(env_.GetChildren(dbname_, &filenames));
    uint64_t number;
    FileType type;
    std::string fname;
    int picked_number = -1;
171
    for (size_t i = 0; i < filenames.size(); i++) {
172 173 174 175
      if (ParseFileName(filenames[i], &number, &type) &&
          type == filetype &&
          static_cast<int>(number) > picked_number) {  // Pick latest file
        fname = dbname_ + "/" + filenames[i];
176
        picked_number = static_cast<int>(number);
177 178 179 180
      }
    }
    ASSERT_TRUE(!fname.empty()) << filetype;

181
    test::CorruptFile(fname, offset, bytes_to_corrupt);
182 183 184 185 186 187 188 189 190
  }

  // corrupts exactly one file at level `level`. if no file found at level,
  // asserts
  void CorruptTableFileAtLevel(int level, int offset, int bytes_to_corrupt) {
    std::vector<LiveFileMetaData> metadata;
    db_->GetLiveFilesMetaData(&metadata);
    for (const auto& m : metadata) {
      if (m.level == level) {
191
        test::CorruptFile(dbname_ + "/" + m.name, offset, bytes_to_corrupt);
192 193 194
        return;
      }
    }
195
    FAIL() << "no file found at level";
196 197 198
  }


199 200 201 202 203 204 205 206
  int Property(const std::string& name) {
    std::string property;
    int result;
    if (db_->GetProperty(name, &property) &&
        sscanf(property.c_str(), "%d", &result) == 1) {
      return result;
    } else {
      return -1;
J
jorlow@chromium.org 已提交
207 208 209 210 211 212 213 214 215 216 217 218 219
    }
  }

  // Return the ith key
  Slice Key(int i, std::string* storage) {
    char buf[100];
    snprintf(buf, sizeof(buf), "%016d", i);
    storage->assign(buf, strlen(buf));
    return Slice(*storage);
  }

  // Return the value to associate with the specified key
  Slice Value(int k, std::string* storage) {
220 221 222 223 224 225 226
    if (k == 0) {
      // Ugh.  Random seed of 0 used to produce no entropy.  This code
      // preserves the implementation that was in place when all of the
      // magic values in this file were picked.
      *storage = std::string(kValueSize, ' ');
    } else {
      Random r(k);
M
mrambacher 已提交
227
      *storage = r.RandomString(kValueSize);
228
    }
M
mrambacher 已提交
229
    return Slice(*storage);
J
jorlow@chromium.org 已提交
230 231 232
  }
};

I
Igor Sugak 已提交
233
TEST_F(CorruptionTest, Recovery) {
J
jorlow@chromium.org 已提交
234 235
  Build(100);
  Check(100, 100);
D
Dmitri Smirnov 已提交
236 237 238 239 240 241 242 243 244 245
#ifdef OS_WIN
  // On Wndows OS Disk cache does not behave properly
  // We do not call FlushBuffers on every Flush. If we do not close
  // the log file prior to the corruption we end up with the first
  // block not corrupted but only the second. However, under the debugger
  // things work just fine but never pass when running normally
  // For that reason people may want to run with unbuffered I/O. That option
  // is not available for WAL though.
  CloseDb();
#endif
J
jorlow@chromium.org 已提交
246
  Corrupt(kLogFile, 19, 1);      // WriteBatch tag for first record
J
jorlow@chromium.org 已提交
247
  Corrupt(kLogFile, log::kBlockSize + 1000, 1);  // Somewhere in second block
I
Igor Canadi 已提交
248 249 250
  ASSERT_TRUE(!TryReopen().ok());
  options_.paranoid_checks = false;
  Reopen(&options_);
J
jorlow@chromium.org 已提交
251 252 253

  // The 64 records in the first two log blocks are completely lost.
  Check(36, 36);
J
jorlow@chromium.org 已提交
254 255
}

I
Igor Sugak 已提交
256
TEST_F(CorruptionTest, RecoverWriteError) {
J
jorlow@chromium.org 已提交
257 258 259 260 261
  env_.writable_file_error_ = true;
  Status s = TryReopen();
  ASSERT_TRUE(!s.ok());
}

I
Igor Sugak 已提交
262
TEST_F(CorruptionTest, NewFileErrorDuringWrite) {
J
jorlow@chromium.org 已提交
263 264
  // Do enough writing to force minor compaction
  env_.writable_file_error_ = true;
265 266
  const int num =
      static_cast<int>(3 + (Options().write_buffer_size / kValueSize));
J
jorlow@chromium.org 已提交
267 268
  std::string value_storage;
  Status s;
I
Igor Canadi 已提交
269 270
  bool failed = false;
  for (int i = 0; i < num; i++) {
J
jorlow@chromium.org 已提交
271 272 273
    WriteBatch batch;
    batch.Put("a", Value(100, &value_storage));
    s = db_->Write(WriteOptions(), &batch);
I
Igor Canadi 已提交
274 275 276 277
    if (!s.ok()) {
      failed = true;
    }
    ASSERT_TRUE(!failed || !s.ok());
J
jorlow@chromium.org 已提交
278 279 280 281 282 283 284
  }
  ASSERT_TRUE(!s.ok());
  ASSERT_GE(env_.num_writable_file_errors_, 1);
  env_.writable_file_error_ = false;
  Reopen();
}

I
Igor Sugak 已提交
285
TEST_F(CorruptionTest, TableFile) {
J
jorlow@chromium.org 已提交
286
  Build(100);
287
  DBImpl* dbi = static_cast_with_check<DBImpl>(db_);
288
  dbi->TEST_FlushMemTable();
A
Abhishek Kona 已提交
289 290
  dbi->TEST_CompactRange(0, nullptr, nullptr);
  dbi->TEST_CompactRange(1, nullptr, nullptr);
J
jorlow@chromium.org 已提交
291 292 293

  Corrupt(kTableFile, 100, 1);
  Check(99, 99);
A
Aaron G 已提交
294
  ASSERT_NOK(dbi->VerifyChecksum());
J
jorlow@chromium.org 已提交
295 296
}

S
sdong 已提交
297 298 299 300 301 302 303 304 305 306 307 308 309
TEST_F(CorruptionTest, VerifyChecksumReadahead) {
  Options options;
  SpecialEnv senv(Env::Default());
  options.env = &senv;
  // Disable block cache as we are going to check checksum for
  // the same file twice and measure number of reads.
  BlockBasedTableOptions table_options_no_bc;
  table_options_no_bc.no_block_cache = true;
  options.table_factory.reset(NewBlockBasedTableFactory(table_options_no_bc));

  Reopen(&options);

  Build(10000);
310
  DBImpl* dbi = static_cast_with_check<DBImpl>(db_);
S
sdong 已提交
311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339
  dbi->TEST_FlushMemTable();
  dbi->TEST_CompactRange(0, nullptr, nullptr);
  dbi->TEST_CompactRange(1, nullptr, nullptr);

  senv.count_random_reads_ = true;
  senv.random_read_counter_.Reset();
  ASSERT_OK(dbi->VerifyChecksum());

  // Make sure the counter is enabled.
  ASSERT_GT(senv.random_read_counter_.Read(), 0);

  // The SST file is about 10MB. Default readahead size is 256KB.
  // Give a conservative 20 reads for metadata blocks, The number
  // of random reads should be within 10 MB / 256KB + 20 = 60.
  ASSERT_LT(senv.random_read_counter_.Read(), 60);

  senv.random_read_bytes_counter_ = 0;
  ReadOptions ro;
  ro.readahead_size = size_t{32 * 1024};
  ASSERT_OK(dbi->VerifyChecksum(ro));
  // The SST file is about 10MB. We set readahead size to 32KB.
  // Give 0 to 20 reads for metadata blocks, and allow real read
  // to range from 24KB to 48KB. The lower bound would be:
  //   10MB / 48KB + 0 = 213
  // The higher bound is
  //   10MB / 24KB + 20 = 447.
  ASSERT_GE(senv.random_read_counter_.Read(), 213);
  ASSERT_LE(senv.random_read_counter_.Read(), 447);

340 341 342 343 344 345 346
  // Test readahead shouldn't break mmap mode (where it should be
  // disabled).
  options.allow_mmap_reads = true;
  Reopen(&options);
  dbi = static_cast<DBImpl*>(db_);
  ASSERT_OK(dbi->VerifyChecksum(ro));

S
sdong 已提交
347 348 349
  CloseDb();
}

I
Igor Sugak 已提交
350
TEST_F(CorruptionTest, TableFileIndexData) {
I
Igor Canadi 已提交
351 352 353 354 355 356
  Options options;
  // very big, we'll trigger flushes manually
  options.write_buffer_size = 100 * 1024 * 1024;
  Reopen(&options);
  // build 2 tables, flush at 5000
  Build(10000, 5000);
357
  DBImpl* dbi = static_cast_with_check<DBImpl>(db_);
358
  dbi->TEST_FlushMemTable();
J
jorlow@chromium.org 已提交
359

I
Igor Canadi 已提交
360
  // corrupt an index block of an entire file
361
  Corrupt(kTableFile, -2000, 500);
362 363
  options.paranoid_checks = false;
  Reopen(&options);
364
  dbi = static_cast_with_check<DBImpl>(db_);
365
  // one full file may be readable, since only one was corrupted
I
Igor Canadi 已提交
366
  // the other file should be fully non-readable, since index was corrupted
367
  Check(0, 5000);
A
Aaron G 已提交
368
  ASSERT_NOK(dbi->VerifyChecksum());
369 370 371

  // In paranoid mode, the db cannot be opened due to the corrupted file.
  ASSERT_TRUE(TryReopen().IsCorruption());
J
jorlow@chromium.org 已提交
372 373
}

I
Igor Sugak 已提交
374
TEST_F(CorruptionTest, MissingDescriptor) {
J
jorlow@chromium.org 已提交
375 376 377 378 379 380
  Build(1000);
  RepairDB();
  Reopen();
  Check(1000, 1000);
}

I
Igor Sugak 已提交
381
TEST_F(CorruptionTest, SequenceNumberRecovery) {
J
jorlow@chromium.org 已提交
382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401
  ASSERT_OK(db_->Put(WriteOptions(), "foo", "v1"));
  ASSERT_OK(db_->Put(WriteOptions(), "foo", "v2"));
  ASSERT_OK(db_->Put(WriteOptions(), "foo", "v3"));
  ASSERT_OK(db_->Put(WriteOptions(), "foo", "v4"));
  ASSERT_OK(db_->Put(WriteOptions(), "foo", "v5"));
  RepairDB();
  Reopen();
  std::string v;
  ASSERT_OK(db_->Get(ReadOptions(), "foo", &v));
  ASSERT_EQ("v5", v);
  // Write something.  If sequence number was not recovered properly,
  // it will be hidden by an earlier write.
  ASSERT_OK(db_->Put(WriteOptions(), "foo", "v6"));
  ASSERT_OK(db_->Get(ReadOptions(), "foo", &v));
  ASSERT_EQ("v6", v);
  Reopen();
  ASSERT_OK(db_->Get(ReadOptions(), "foo", &v));
  ASSERT_EQ("v6", v);
}

I
Igor Sugak 已提交
402
TEST_F(CorruptionTest, CorruptedDescriptor) {
J
jorlow@chromium.org 已提交
403
  ASSERT_OK(db_->Put(WriteOptions(), "foo", "hello"));
404
  DBImpl* dbi = static_cast_with_check<DBImpl>(db_);
405
  dbi->TEST_FlushMemTable();
A
Abhishek Kona 已提交
406
  dbi->TEST_CompactRange(0, nullptr, nullptr);
J
jorlow@chromium.org 已提交
407 408 409 410 411 412 413 414 415 416 417 418

  Corrupt(kDescriptorFile, 0, 1000);
  Status s = TryReopen();
  ASSERT_TRUE(!s.ok());

  RepairDB();
  Reopen();
  std::string v;
  ASSERT_OK(db_->Get(ReadOptions(), "foo", &v));
  ASSERT_EQ("hello", v);
}

I
Igor Sugak 已提交
419
TEST_F(CorruptionTest, CompactionInputError) {
I
Igor Canadi 已提交
420 421
  Options options;
  Reopen(&options);
J
jorlow@chromium.org 已提交
422
  Build(10);
423
  DBImpl* dbi = static_cast_with_check<DBImpl>(db_);
424
  dbi->TEST_FlushMemTable();
425 426 427
  dbi->TEST_CompactRange(0, nullptr, nullptr);
  dbi->TEST_CompactRange(1, nullptr, nullptr);
  ASSERT_EQ(1, Property("rocksdb.num-files-at-level2"));
J
jorlow@chromium.org 已提交
428 429 430

  Corrupt(kTableFile, 100, 1);
  Check(9, 9);
A
Aaron G 已提交
431
  ASSERT_NOK(dbi->VerifyChecksum());
J
jorlow@chromium.org 已提交
432 433 434 435

  // Force compactions by writing lots of values
  Build(10000);
  Check(10000, 10000);
A
Aaron G 已提交
436
  ASSERT_NOK(dbi->VerifyChecksum());
J
jorlow@chromium.org 已提交
437 438
}

I
Igor Sugak 已提交
439
TEST_F(CorruptionTest, CompactionInputErrorParanoid) {
J
jorlow@chromium.org 已提交
440 441
  Options options;
  options.paranoid_checks = true;
442 443
  options.write_buffer_size = 131072;
  options.max_write_buffer_number = 2;
J
jorlow@chromium.org 已提交
444
  Reopen(&options);
445
  DBImpl* dbi = static_cast_with_check<DBImpl>(db_);
446

447
  // Fill levels >= 1
448
  for (int level = 1; level < dbi->NumberLevels(); level++) {
449 450
    dbi->Put(WriteOptions(), "", "begin");
    dbi->Put(WriteOptions(), "~", "end");
451
    dbi->TEST_FlushMemTable();
452 453 454 455
    for (int comp_level = 0; comp_level < dbi->NumberLevels() - level;
         ++comp_level) {
      dbi->TEST_CompactRange(comp_level, nullptr, nullptr);
    }
456
  }
J
jorlow@chromium.org 已提交
457

458 459
  Reopen(&options);

460
  dbi = static_cast_with_check<DBImpl>(db_);
J
jorlow@chromium.org 已提交
461
  Build(10);
462
  dbi->TEST_FlushMemTable();
463
  dbi->TEST_WaitForCompact();
464
  ASSERT_EQ(1, Property("rocksdb.num-files-at-level0"));
J
jorlow@chromium.org 已提交
465

466
  CorruptTableFileAtLevel(0, 100, 1);
J
jorlow@chromium.org 已提交
467
  Check(9, 9);
A
Aaron G 已提交
468
  ASSERT_NOK(dbi->VerifyChecksum());
J
jorlow@chromium.org 已提交
469 470 471 472

  // Write must eventually fail because of corrupted table
  Status s;
  std::string tmp1, tmp2;
I
Igor Canadi 已提交
473
  bool failed = false;
474
  for (int i = 0; i < 10000; i++) {
J
jorlow@chromium.org 已提交
475
    s = db_->Put(WriteOptions(), Key(i, &tmp1), Value(i, &tmp2));
I
Igor Canadi 已提交
476 477 478 479 480
    if (!s.ok()) {
      failed = true;
    }
    // if one write failed, every subsequent write must fail, too
    ASSERT_TRUE(!failed || !s.ok()) << "write did not fail in a corrupted db";
J
jorlow@chromium.org 已提交
481 482 483 484
  }
  ASSERT_TRUE(!s.ok()) << "write did not fail in corrupted paranoid db";
}

I
Igor Sugak 已提交
485
TEST_F(CorruptionTest, UnrelatedKeys) {
J
jorlow@chromium.org 已提交
486
  Build(10);
487
  DBImpl* dbi = static_cast_with_check<DBImpl>(db_);
488
  dbi->TEST_FlushMemTable();
J
jorlow@chromium.org 已提交
489
  Corrupt(kTableFile, 100, 1);
A
Aaron G 已提交
490
  ASSERT_NOK(dbi->VerifyChecksum());
J
jorlow@chromium.org 已提交
491 492 493 494 495 496

  std::string tmp1, tmp2;
  ASSERT_OK(db_->Put(WriteOptions(), Key(1000, &tmp1), Value(1000, &tmp2)));
  std::string v;
  ASSERT_OK(db_->Get(ReadOptions(), Key(1000, &tmp1), &v));
  ASSERT_EQ(Value(1000, &tmp2).ToString(), v);
497
  dbi->TEST_FlushMemTable();
J
jorlow@chromium.org 已提交
498 499 500 501
  ASSERT_OK(db_->Get(ReadOptions(), Key(1000, &tmp1), &v));
  ASSERT_EQ(Value(1000, &tmp2).ToString(), v);
}

502 503 504 505 506 507 508 509 510 511 512 513
TEST_F(CorruptionTest, RangeDeletionCorrupted) {
  ASSERT_OK(
      db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(), "a", "b"));
  ASSERT_OK(db_->Flush(FlushOptions()));
  std::vector<LiveFileMetaData> metadata;
  db_->GetLiveFilesMetaData(&metadata);
  ASSERT_EQ(static_cast<size_t>(1), metadata.size());
  std::string filename = dbname_ + metadata[0].name;

  std::unique_ptr<RandomAccessFile> file;
  ASSERT_OK(options_.env->NewRandomAccessFile(filename, &file, EnvOptions()));
  std::unique_ptr<RandomAccessFileReader> file_reader(
514 515
      new RandomAccessFileReader(NewLegacyRandomAccessFileWrapper(file),
                                 filename));
516 517 518 519 520 521 522 523 524 525

  uint64_t file_size;
  ASSERT_OK(options_.env->GetFileSize(filename, &file_size));

  BlockHandle range_del_handle;
  ASSERT_OK(FindMetaBlock(
      file_reader.get(), file_size, kBlockBasedTableMagicNumber,
      ImmutableCFOptions(options_), kRangeDelBlock, &range_del_handle));

  ASSERT_OK(TryReopen());
526
  test::CorruptFile(filename, static_cast<int>(range_del_handle.offset()), 1);
527
  ASSERT_TRUE(TryReopen().IsCorruption());
528 529
}

I
Igor Sugak 已提交
530
TEST_F(CorruptionTest, FileSystemStateCorrupted) {
I
Igor Canadi 已提交
531 532 533 534 535 536 537
  for (int iter = 0; iter < 2; ++iter) {
    Options options;
    options.paranoid_checks = true;
    options.create_if_missing = true;
    Reopen(&options);
    Build(10);
    ASSERT_OK(db_->Flush(FlushOptions()));
538
    DBImpl* dbi = static_cast_with_check<DBImpl>(db_);
I
Igor Canadi 已提交
539 540
    std::vector<LiveFileMetaData> metadata;
    dbi->GetLiveFilesMetaData(&metadata);
541
    ASSERT_GT(metadata.size(), size_t(0));
I
Igor Canadi 已提交
542 543 544
    std::string filename = dbname_ + metadata[0].name;

    delete db_;
I
Igor Canadi 已提交
545
    db_ = nullptr;
I
Igor Canadi 已提交
546 547

    if (iter == 0) {  // corrupt file size
548
      std::unique_ptr<WritableFile> file;
I
Igor Canadi 已提交
549 550 551
      env_.NewWritableFile(filename, &file, EnvOptions());
      file->Append(Slice("corrupted sst"));
      file.reset();
552 553
      Status x = TryReopen(&options);
      ASSERT_TRUE(x.IsCorruption());
I
Igor Canadi 已提交
554 555
    } else {  // delete the file
      env_.DeleteFile(filename);
556 557
      Status x = TryReopen(&options);
      ASSERT_TRUE(x.IsPathNotFound());
I
Igor Canadi 已提交
558 559 560 561 562 563
    }

    DestroyDB(dbname_, options_);
  }
}

564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616
static const auto& corruption_modes = {mock::MockTableFactory::kCorruptNone,
                                       mock::MockTableFactory::kCorruptKey,
                                       mock::MockTableFactory::kCorruptValue};

TEST_F(CorruptionTest, ParaniodFileChecksOnFlush) {
  Options options;
  options.paranoid_file_checks = true;
  options.create_if_missing = true;
  Status s;
  for (const auto& mode : corruption_modes) {
    delete db_;
    s = DestroyDB(dbname_, options);
    std::shared_ptr<mock::MockTableFactory> mock =
        std::make_shared<mock::MockTableFactory>();
    options.table_factory = mock;
    mock->SetCorruptionMode(mode);
    ASSERT_OK(DB::Open(options, dbname_, &db_));
    Build(10);
    s = db_->Flush(FlushOptions());
    if (mode == mock::MockTableFactory::kCorruptNone) {
      ASSERT_OK(s);
    } else {
      ASSERT_NOK(s);
    }
  }
}

TEST_F(CorruptionTest, ParaniodFileChecksOnCompact) {
  Options options;
  options.paranoid_file_checks = true;
  options.create_if_missing = true;
  Status s;
  for (const auto& mode : corruption_modes) {
    delete db_;
    s = DestroyDB(dbname_, options);
    std::shared_ptr<mock::MockTableFactory> mock =
        std::make_shared<mock::MockTableFactory>();
    options.table_factory = mock;
    ASSERT_OK(DB::Open(options, dbname_, &db_));
    Build(100, 2);
    // ASSERT_OK(db_->Flush(FlushOptions()));
    DBImpl* dbi = static_cast_with_check<DBImpl>(db_);
    ASSERT_OK(dbi->TEST_FlushMemTable());
    mock->SetCorruptionMode(mode);
    s = dbi->TEST_CompactRange(0, nullptr, nullptr, nullptr, true);
    if (mode == mock::MockTableFactory::kCorruptNone) {
      ASSERT_OK(s);
    } else {
      ASSERT_NOK(s);
    }
  }
}

617
}  // namespace ROCKSDB_NAMESPACE
J
jorlow@chromium.org 已提交
618 619

int main(int argc, char** argv) {
I
Igor Sugak 已提交
620 621
  ::testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
J
jorlow@chromium.org 已提交
622
}
623 624 625 626

#else
#include <stdio.h>

627
int main(int /*argc*/, char** /*argv*/) {
628 629 630 631 632
  fprintf(stderr, "SKIPPED as RepairDB() is not supported in ROCKSDB_LITE\n");
  return 0;
}

#endif  // !ROCKSDB_LITE