Feathercoin  0.5.0
P2P Digital Currency
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros
corruption_test.cc
Go to the documentation of this file.
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.
4 
5 #include "leveldb/db.h"
6 
7 #include <errno.h>
8 #include <fcntl.h>
9 #include <sys/stat.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"
22 
23 namespace leveldb {
24 
25 static const int kValueSize = 1000;
26 
28  public:
30  std::string dbname_;
33  DB* db_;
34 
36  tiny_cache_ = NewLRUCache(100);
37  options_.env = &env_;
38  options_.block_cache = tiny_cache_;
39  dbname_ = test::TmpDir() + "/db_test";
40  DestroyDB(dbname_, options_);
41 
42  db_ = NULL;
43  options_.create_if_missing = true;
44  Reopen();
45  options_.create_if_missing = false;
46  }
47 
49  delete db_;
50  DestroyDB(dbname_, Options());
51  delete tiny_cache_;
52  }
53 
55  delete db_;
56  db_ = NULL;
57  return DB::Open(options_, dbname_, &db_);
58  }
59 
60  void Reopen() {
62  }
63 
64  void RepairDB() {
65  delete db_;
66  db_ = NULL;
67  ASSERT_OK(::leveldb::RepairDB(dbname_, options_));
68  }
69 
70  void Build(int n) {
71  std::string key_space, value_space;
72  WriteBatch batch;
73  for (int i = 0; i < n; i++) {
74  //if ((i % 100) == 0) fprintf(stderr, "@ %d of %d\n", i, n);
75  Slice key = Key(i, &key_space);
76  batch.Clear();
77  batch.Put(key, Value(i, &value_space));
78  ASSERT_OK(db_->Write(WriteOptions(), &batch));
79  }
80  }
81 
82  void Check(int min_expected, int max_expected) {
83  int next_expected = 0;
84  int missed = 0;
85  int bad_keys = 0;
86  int bad_values = 0;
87  int correct = 0;
88  std::string value_space;
89  Iterator* iter = db_->NewIterator(ReadOptions());
90  for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
91  uint64_t key;
92  Slice in(iter->key());
93  if (in == "" || in == "~") {
94  // Ignore boundary keys.
95  continue;
96  }
97  if (!ConsumeDecimalNumber(&in, &key) ||
98  !in.empty() ||
99  key < next_expected) {
100  bad_keys++;
101  continue;
102  }
103  missed += (key - next_expected);
104  next_expected = key + 1;
105  if (iter->value() != Value(key, &value_space)) {
106  bad_values++;
107  } else {
108  correct++;
109  }
110  }
111  delete iter;
112 
113  fprintf(stderr,
114  "expected=%d..%d; got=%d; bad_keys=%d; bad_values=%d; missed=%d\n",
115  min_expected, max_expected, correct, bad_keys, bad_values, missed);
116  ASSERT_LE(min_expected, correct);
117  ASSERT_GE(max_expected, correct);
118  }
119 
120  void Corrupt(FileType filetype, int offset, int bytes_to_corrupt) {
121  // Pick file to corrupt
122  std::vector<std::string> filenames;
123  ASSERT_OK(env_.GetChildren(dbname_, &filenames));
124  uint64_t number;
125  FileType type;
126  std::string fname;
127  int picked_number = -1;
128  for (int i = 0; i < filenames.size(); i++) {
129  if (ParseFileName(filenames[i], &number, &type) &&
130  type == filetype &&
131  int(number) > picked_number) { // Pick latest file
132  fname = dbname_ + "/" + filenames[i];
133  picked_number = number;
134  }
135  }
136  ASSERT_TRUE(!fname.empty()) << filetype;
137 
138  struct stat sbuf;
139  if (stat(fname.c_str(), &sbuf) != 0) {
140  const char* msg = strerror(errno);
141  ASSERT_TRUE(false) << fname << ": " << msg;
142  }
143 
144  if (offset < 0) {
145  // Relative to end of file; make it absolute
146  if (-offset > sbuf.st_size) {
147  offset = 0;
148  } else {
149  offset = sbuf.st_size + offset;
150  }
151  }
152  if (offset > sbuf.st_size) {
153  offset = sbuf.st_size;
154  }
155  if (offset + bytes_to_corrupt > sbuf.st_size) {
156  bytes_to_corrupt = sbuf.st_size - offset;
157  }
158 
159  // Do it
160  std::string contents;
161  Status s = ReadFileToString(Env::Default(), fname, &contents);
162  ASSERT_TRUE(s.ok()) << s.ToString();
163  for (int i = 0; i < bytes_to_corrupt; i++) {
164  contents[i + offset] ^= 0x80;
165  }
166  s = WriteStringToFile(Env::Default(), contents, fname);
167  ASSERT_TRUE(s.ok()) << s.ToString();
168  }
169 
170  int Property(const std::string& name) {
171  std::string property;
172  int result;
173  if (db_->GetProperty(name, &property) &&
174  sscanf(property.c_str(), "%d", &result) == 1) {
175  return result;
176  } else {
177  return -1;
178  }
179  }
180 
181  // Return the ith key
182  Slice Key(int i, std::string* storage) {
183  char buf[100];
184  snprintf(buf, sizeof(buf), "%016d", i);
185  storage->assign(buf, strlen(buf));
186  return Slice(*storage);
187  }
188 
189  // Return the value to associate with the specified key
190  Slice Value(int k, std::string* storage) {
191  Random r(k);
192  return test::RandomString(&r, kValueSize, storage);
193  }
194 };
195 
196 TEST(CorruptionTest, Recovery) {
197  Build(100);
198  Check(100, 100);
199  Corrupt(kLogFile, 19, 1); // WriteBatch tag for first record
200  Corrupt(kLogFile, log::kBlockSize + 1000, 1); // Somewhere in second block
201  Reopen();
202 
203  // The 64 records in the first two log blocks are completely lost.
204  Check(36, 36);
205 }
206 
207 TEST(CorruptionTest, RecoverWriteError) {
208  env_.writable_file_error_ = true;
209  Status s = TryReopen();
210  ASSERT_TRUE(!s.ok());
211 }
212 
213 TEST(CorruptionTest, NewFileErrorDuringWrite) {
214  // Do enough writing to force minor compaction
215  env_.writable_file_error_ = true;
216  const int num = 3 + (Options().write_buffer_size / kValueSize);
217  std::string value_storage;
218  Status s;
219  for (int i = 0; s.ok() && i < num; i++) {
220  WriteBatch batch;
221  batch.Put("a", Value(100, &value_storage));
222  s = db_->Write(WriteOptions(), &batch);
223  }
224  ASSERT_TRUE(!s.ok());
225  ASSERT_GE(env_.num_writable_file_errors_, 1);
226  env_.writable_file_error_ = false;
227  Reopen();
228 }
229 
230 TEST(CorruptionTest, TableFile) {
231  Build(100);
232  DBImpl* dbi = reinterpret_cast<DBImpl*>(db_);
233  dbi->TEST_CompactMemTable();
234  dbi->TEST_CompactRange(0, NULL, NULL);
235  dbi->TEST_CompactRange(1, NULL, NULL);
236 
237  Corrupt(kTableFile, 100, 1);
238  Check(90, 99);
239 }
240 
241 TEST(CorruptionTest, TableFileIndexData) {
242  Build(10000); // Enough to build multiple Tables
243  DBImpl* dbi = reinterpret_cast<DBImpl*>(db_);
244  dbi->TEST_CompactMemTable();
245 
246  Corrupt(kTableFile, -2000, 500);
247  Reopen();
248  Check(5000, 9999);
249 }
250 
251 TEST(CorruptionTest, MissingDescriptor) {
252  Build(1000);
253  RepairDB();
254  Reopen();
255  Check(1000, 1000);
256 }
257 
258 TEST(CorruptionTest, SequenceNumberRecovery) {
259  ASSERT_OK(db_->Put(WriteOptions(), "foo", "v1"));
260  ASSERT_OK(db_->Put(WriteOptions(), "foo", "v2"));
261  ASSERT_OK(db_->Put(WriteOptions(), "foo", "v3"));
262  ASSERT_OK(db_->Put(WriteOptions(), "foo", "v4"));
263  ASSERT_OK(db_->Put(WriteOptions(), "foo", "v5"));
264  RepairDB();
265  Reopen();
266  std::string v;
267  ASSERT_OK(db_->Get(ReadOptions(), "foo", &v));
268  ASSERT_EQ("v5", v);
269  // Write something. If sequence number was not recovered properly,
270  // it will be hidden by an earlier write.
271  ASSERT_OK(db_->Put(WriteOptions(), "foo", "v6"));
272  ASSERT_OK(db_->Get(ReadOptions(), "foo", &v));
273  ASSERT_EQ("v6", v);
274  Reopen();
275  ASSERT_OK(db_->Get(ReadOptions(), "foo", &v));
276  ASSERT_EQ("v6", v);
277 }
278 
279 TEST(CorruptionTest, CorruptedDescriptor) {
280  ASSERT_OK(db_->Put(WriteOptions(), "foo", "hello"));
281  DBImpl* dbi = reinterpret_cast<DBImpl*>(db_);
282  dbi->TEST_CompactMemTable();
283  dbi->TEST_CompactRange(0, NULL, NULL);
284 
285  Corrupt(kDescriptorFile, 0, 1000);
286  Status s = TryReopen();
287  ASSERT_TRUE(!s.ok());
288 
289  RepairDB();
290  Reopen();
291  std::string v;
292  ASSERT_OK(db_->Get(ReadOptions(), "foo", &v));
293  ASSERT_EQ("hello", v);
294 }
295 
296 TEST(CorruptionTest, CompactionInputError) {
297  Build(10);
298  DBImpl* dbi = reinterpret_cast<DBImpl*>(db_);
299  dbi->TEST_CompactMemTable();
300  const int last = config::kMaxMemCompactLevel;
301  ASSERT_EQ(1, Property("leveldb.num-files-at-level" + NumberToString(last)));
302 
303  Corrupt(kTableFile, 100, 1);
304  Check(5, 9);
305 
306  // Force compactions by writing lots of values
307  Build(10000);
308  Check(10000, 10000);
309 }
310 
311 TEST(CorruptionTest, CompactionInputErrorParanoid) {
312  options_.paranoid_checks = true;
313  options_.write_buffer_size = 512 << 10;
314  Reopen();
315  DBImpl* dbi = reinterpret_cast<DBImpl*>(db_);
316 
317  // Make multiple inputs so we need to compact.
318  for (int i = 0; i < 2; i++) {
319  Build(10);
320  dbi->TEST_CompactMemTable();
321  Corrupt(kTableFile, 100, 1);
322  env_.SleepForMicroseconds(100000);
323  }
324  dbi->CompactRange(NULL, NULL);
325 
326  // Write must fail because of corrupted table
327  std::string tmp1, tmp2;
328  Status s = db_->Put(WriteOptions(), Key(5, &tmp1), Value(5, &tmp2));
329  ASSERT_TRUE(!s.ok()) << "write did not fail in corrupted paranoid db";
330 }
331 
332 TEST(CorruptionTest, UnrelatedKeys) {
333  Build(10);
334  DBImpl* dbi = reinterpret_cast<DBImpl*>(db_);
335  dbi->TEST_CompactMemTable();
336  Corrupt(kTableFile, 100, 1);
337 
338  std::string tmp1, tmp2;
339  ASSERT_OK(db_->Put(WriteOptions(), Key(1000, &tmp1), Value(1000, &tmp2)));
340  std::string v;
341  ASSERT_OK(db_->Get(ReadOptions(), Key(1000, &tmp1), &v));
342  ASSERT_EQ(Value(1000, &tmp2).ToString(), v);
343  dbi->TEST_CompactMemTable();
344  ASSERT_OK(db_->Get(ReadOptions(), Key(1000, &tmp1), &v));
345  ASSERT_EQ(Value(1000, &tmp2).ToString(), v);
346 }
347 
348 } // namespace leveldb
349 
350 int main(int argc, char** argv) {
352 }
uint64_t Key
virtual Status Write(const WriteOptions &options, WriteBatch *updates)
Definition: db_impl.cc:1157
Slice Value(int k, std::string *storage)
virtual void CompactRange(const Slice *begin, const Slice *end)
Definition: db_impl.cc:531
Cache * block_cache
Definition: options.h:98
virtual Status Write(const WriteOptions &options, WriteBatch *updates)=0
Status GetChildren(const std::string &dir, std::vector< std::string > *r)
Definition: env.h:293
bool create_if_missing
Definition: options.h:45
Status WriteStringToFile(Env *env, const Slice &data, const std::string &fname)
Definition: env.cc:58
Slice Key(int i, std::string *storage)
bool ParseFileName(const std::string &fname, uint64_t *number, FileType *type)
Definition: filename.cc:75
std::string TmpDir()
Definition: testharness.cc:60
int main(int argc, char **argv)
#define ASSERT_LE(a, b)
Definition: testharness.h:111
Status TEST_CompactMemTable()
Definition: db_impl.cc:583
void TEST_CompactRange(int level, const Slice *begin, const Slice *end)
Definition: db_impl.cc:548
Options const options_
Definition: repair.cc:104
#define ASSERT_OK(s)
Definition: testharness.h:106
int RunAllTests()
Definition: testharness.cc:36
virtual void SleepForMicroseconds(int micros)=0
DBImpl * db_
Definition: db_iter.cc:111
virtual Iterator * NewIterator(const ReadOptions &options)=0
virtual Slice value() const =0
Status RepairDB(const std::string &dbname, const Options &options)
Definition: repair.cc:384
#define ASSERT_EQ(a, b)
Definition: testharness.h:107
virtual void Next()=0
Status DestroyDB(const std::string &dbname, const Options &options)
Definition: db_impl.cc:1467
Definition: db.h:44
bool ConsumeDecimalNumber(Slice *in, uint64_t *val)
Definition: logging.cc:57
Config::Value_type Value
unsigned long long uint64_t
Definition: stdint.h:22
static Status Open(const Options &options, const std::string &name, DB **dbptr)
Definition: db_impl.cc:1430
TEST(CorruptionTest, Recovery)
#define ASSERT_TRUE(c)
Definition: testharness.h:105
virtual void SeekToFirst()=0
bool paranoid_checks
Definition: options.h:57
Env *const env_
Definition: repair.cc:101
#define ASSERT_GE(a, b)
Definition: testharness.h:109
virtual Status Put(const WriteOptions &, const Slice &key, const Slice &value)
Definition: db_impl.cc:1149
virtual Status Get(const ReadOptions &options, const Slice &key, std::string *value)
Definition: db_impl.cc:1072
void Corrupt(FileType filetype, int offset, int bytes_to_corrupt)
Cache * NewLRUCache(size_t capacity)
Definition: cache.cc:321
virtual bool Valid() const =0
int Property(const std::string &name)
virtual Slice key() const =0
bool ok() const
Definition: status.h:52
size_t write_buffer_size
Definition: options.h:83
FileType
Definition: filename.h:20
Slice RandomString(Random *rnd, int len, std::string *dst)
Definition: testutil.cc:12
virtual bool GetProperty(const Slice &property, std::string *value)=0
void Check(int min_expected, int max_expected)
void Put(const Slice &key, const Slice &value)
Definition: write_batch.cc:98
std::string NumberToString(uint64_t num)
Definition: logging.cc:36
static Env * Default()
Definition: env_posix.cc:800
const char * name
Definition: testharness.cc:18
Status ReadFileToString(Env *env, const std::string &fname, std::string *data)
Definition: env.cc:68
std::string ToString() const
Definition: status.cc:36