1 // Copyright (c) 2011 The LevelDB Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. See the AUTHORS file for names of contributors.
5 #include "leveldb/db.h"
10 #include <sys/types.h>
11 #include "leveldb/cache.h"
12 #include "leveldb/env.h"
13 #include "leveldb/table.h"
14 #include "leveldb/write_batch.h"
15 #include "db/db_impl.h"
16 #include "db/filename.h"
17 #include "db/log_format.h"
18 #include "db/version_set.h"
19 #include "util/logging.h"
20 #include "util/testharness.h"
21 #include "util/testutil.h"
25 static const int kValueSize = 1000;
27 class CorruptionTest {
36 tiny_cache_ = NewLRUCache(100);
38 dbname_ = test::TmpDir() + "/db_test";
39 DestroyDB(dbname_, options_);
42 options_.create_if_missing = true;
44 options_.create_if_missing = false;
49 DestroyDB(dbname_, Options());
53 Status TryReopen(Options* options = NULL) {
56 Options opt = (options ? *options : options_);
58 opt.block_cache = tiny_cache_;
59 return DB::Open(opt, dbname_, &db_);
62 void Reopen(Options* options = NULL) {
63 ASSERT_OK(TryReopen(options));
69 ASSERT_OK(::leveldb::RepairDB(dbname_, options_));
73 std::string key_space, value_space;
75 for (int i = 0; i < n; i++) {
76 //if ((i % 100) == 0) fprintf(stderr, "@ %d of %d\n", i, n);
77 Slice key = Key(i, &key_space);
79 batch.Put(key, Value(i, &value_space));
80 ASSERT_OK(db_->Write(WriteOptions(), &batch));
84 void Check(int min_expected, int max_expected) {
85 int next_expected = 0;
90 std::string value_space;
91 Iterator* iter = db_->NewIterator(ReadOptions());
92 for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
94 Slice in(iter->key());
95 if (!ConsumeDecimalNumber(&in, &key) ||
97 key < next_expected) {
101 missed += (key - next_expected);
102 next_expected = key + 1;
103 if (iter->value() != Value(key, &value_space)) {
112 "expected=%d..%d; got=%d; bad_keys=%d; bad_values=%d; missed=%d\n",
113 min_expected, max_expected, correct, bad_keys, bad_values, missed);
114 ASSERT_LE(min_expected, correct);
115 ASSERT_GE(max_expected, correct);
118 void Corrupt(FileType filetype, int offset, int bytes_to_corrupt) {
119 // Pick file to corrupt
120 std::vector<std::string> filenames;
121 ASSERT_OK(env_.GetChildren(dbname_, &filenames));
125 int picked_number = -1;
126 for (int i = 0; i < filenames.size(); i++) {
127 if (ParseFileName(filenames[i], &number, &type) &&
129 int(number) > picked_number) { // Pick latest file
130 fname = dbname_ + "/" + filenames[i];
131 picked_number = number;
134 ASSERT_TRUE(!fname.empty()) << filetype;
137 if (stat(fname.c_str(), &sbuf) != 0) {
138 const char* msg = strerror(errno);
139 ASSERT_TRUE(false) << fname << ": " << msg;
143 // Relative to end of file; make it absolute
144 if (-offset > sbuf.st_size) {
147 offset = sbuf.st_size + offset;
150 if (offset > sbuf.st_size) {
151 offset = sbuf.st_size;
153 if (offset + bytes_to_corrupt > sbuf.st_size) {
154 bytes_to_corrupt = sbuf.st_size - offset;
158 std::string contents;
159 Status s = ReadFileToString(Env::Default(), fname, &contents);
160 ASSERT_TRUE(s.ok()) << s.ToString();
161 for (int i = 0; i < bytes_to_corrupt; i++) {
162 contents[i + offset] ^= 0x80;
164 s = WriteStringToFile(Env::Default(), contents, fname);
165 ASSERT_TRUE(s.ok()) << s.ToString();
168 int Property(const std::string& name) {
169 std::string property;
171 if (db_->GetProperty(name, &property) &&
172 sscanf(property.c_str(), "%d", &result) == 1) {
179 // Return the ith key
180 Slice Key(int i, std::string* storage) {
182 snprintf(buf, sizeof(buf), "%016d", i);
183 storage->assign(buf, strlen(buf));
184 return Slice(*storage);
187 // Return the value to associate with the specified key
188 Slice Value(int k, std::string* storage) {
190 return test::RandomString(&r, kValueSize, storage);
194 TEST(CorruptionTest, Recovery) {
197 Corrupt(kLogFile, 19, 1); // WriteBatch tag for first record
198 Corrupt(kLogFile, log::kBlockSize + 1000, 1); // Somewhere in second block
201 // The 64 records in the first two log blocks are completely lost.
205 TEST(CorruptionTest, RecoverWriteError) {
206 env_.writable_file_error_ = true;
207 Status s = TryReopen();
208 ASSERT_TRUE(!s.ok());
211 TEST(CorruptionTest, NewFileErrorDuringWrite) {
212 // Do enough writing to force minor compaction
213 env_.writable_file_error_ = true;
214 const int num = 3 + (Options().write_buffer_size / kValueSize);
215 std::string value_storage;
217 for (int i = 0; s.ok() && i < num; i++) {
219 batch.Put("a", Value(100, &value_storage));
220 s = db_->Write(WriteOptions(), &batch);
222 ASSERT_TRUE(!s.ok());
223 ASSERT_GE(env_.num_writable_file_errors_, 1);
224 env_.writable_file_error_ = false;
228 TEST(CorruptionTest, TableFile) {
230 DBImpl* dbi = reinterpret_cast<DBImpl*>(db_);
231 dbi->TEST_CompactMemTable();
232 dbi->TEST_CompactRange(0, NULL, NULL);
233 dbi->TEST_CompactRange(1, NULL, NULL);
235 Corrupt(kTableFile, 100, 1);
239 TEST(CorruptionTest, TableFileIndexData) {
240 Build(10000); // Enough to build multiple Tables
241 DBImpl* dbi = reinterpret_cast<DBImpl*>(db_);
242 dbi->TEST_CompactMemTable();
244 Corrupt(kTableFile, -2000, 500);
249 TEST(CorruptionTest, MissingDescriptor) {
256 TEST(CorruptionTest, SequenceNumberRecovery) {
257 ASSERT_OK(db_->Put(WriteOptions(), "foo", "v1"));
258 ASSERT_OK(db_->Put(WriteOptions(), "foo", "v2"));
259 ASSERT_OK(db_->Put(WriteOptions(), "foo", "v3"));
260 ASSERT_OK(db_->Put(WriteOptions(), "foo", "v4"));
261 ASSERT_OK(db_->Put(WriteOptions(), "foo", "v5"));
265 ASSERT_OK(db_->Get(ReadOptions(), "foo", &v));
267 // Write something. If sequence number was not recovered properly,
268 // it will be hidden by an earlier write.
269 ASSERT_OK(db_->Put(WriteOptions(), "foo", "v6"));
270 ASSERT_OK(db_->Get(ReadOptions(), "foo", &v));
273 ASSERT_OK(db_->Get(ReadOptions(), "foo", &v));
277 TEST(CorruptionTest, CorruptedDescriptor) {
278 ASSERT_OK(db_->Put(WriteOptions(), "foo", "hello"));
279 DBImpl* dbi = reinterpret_cast<DBImpl*>(db_);
280 dbi->TEST_CompactMemTable();
281 dbi->TEST_CompactRange(0, NULL, NULL);
283 Corrupt(kDescriptorFile, 0, 1000);
284 Status s = TryReopen();
285 ASSERT_TRUE(!s.ok());
290 ASSERT_OK(db_->Get(ReadOptions(), "foo", &v));
291 ASSERT_EQ("hello", v);
294 TEST(CorruptionTest, CompactionInputError) {
296 DBImpl* dbi = reinterpret_cast<DBImpl*>(db_);
297 dbi->TEST_CompactMemTable();
298 const int last = config::kMaxMemCompactLevel;
299 ASSERT_EQ(1, Property("leveldb.num-files-at-level" + NumberToString(last)));
301 Corrupt(kTableFile, 100, 1);
304 // Force compactions by writing lots of values
309 TEST(CorruptionTest, CompactionInputErrorParanoid) {
311 options.paranoid_checks = true;
312 options.write_buffer_size = 1048576;
314 DBImpl* dbi = reinterpret_cast<DBImpl*>(db_);
316 // Fill levels >= 1 so memtable compaction outputs to level 1
317 for (int level = 1; level < config::kNumLevels; level++) {
318 dbi->Put(WriteOptions(), "", "begin");
319 dbi->Put(WriteOptions(), "~", "end");
320 dbi->TEST_CompactMemTable();
324 dbi->TEST_CompactMemTable();
325 ASSERT_EQ(1, Property("leveldb.num-files-at-level0"));
327 Corrupt(kTableFile, 100, 1);
330 // Write must eventually fail because of corrupted table
332 std::string tmp1, tmp2;
333 for (int i = 0; i < 10000 && s.ok(); i++) {
334 s = db_->Put(WriteOptions(), Key(i, &tmp1), Value(i, &tmp2));
336 ASSERT_TRUE(!s.ok()) << "write did not fail in corrupted paranoid db";
339 TEST(CorruptionTest, UnrelatedKeys) {
341 DBImpl* dbi = reinterpret_cast<DBImpl*>(db_);
342 dbi->TEST_CompactMemTable();
343 Corrupt(kTableFile, 100, 1);
345 std::string tmp1, tmp2;
346 ASSERT_OK(db_->Put(WriteOptions(), Key(1000, &tmp1), Value(1000, &tmp2)));
348 ASSERT_OK(db_->Get(ReadOptions(), Key(1000, &tmp1), &v));
349 ASSERT_EQ(Value(1000, &tmp2).ToString(), v);
350 dbi->TEST_CompactMemTable();
351 ASSERT_OK(db_->Get(ReadOptions(), Key(1000, &tmp1), &v));
352 ASSERT_EQ(Value(1000, &tmp2).ToString(), v);
355 } // namespace leveldb
357 int main(int argc, char** argv) {
358 return leveldb::test::RunAllTests();