小组成员:谢瑞阳、徐翔宇
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

542 lines
15 KiB

  1. // Copyright 2014 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. // This test uses a custom Env to keep track of the state of a filesystem as of
  5. // the last "sync". It then checks for data loss errors by purposely dropping
  6. // file data (or entire files) not protected by a "sync".
  7. #include "leveldb/db.h"
  8. #include <map>
  9. #include <set>
  10. #include "db/db_impl.h"
  11. #include "db/filename.h"
  12. #include "db/log_format.h"
  13. #include "db/version_set.h"
  14. #include "leveldb/cache.h"
  15. #include "leveldb/env.h"
  16. #include "leveldb/table.h"
  17. #include "leveldb/write_batch.h"
  18. #include "util/logging.h"
  19. #include "util/mutexlock.h"
  20. #include "util/testharness.h"
  21. #include "util/testutil.h"
  22. namespace leveldb {
  23. static const int kValueSize = 1000;
  24. static const int kMaxNumValues = 2000;
  25. static const size_t kNumIterations = 3;
  26. class FaultInjectionTestEnv;
  27. namespace {
  28. // Assume a filename, and not a directory name like "/foo/bar/"
  29. static std::string GetDirName(const std::string filename) {
  30. size_t found = filename.find_last_of("/\\");
  31. if (found == std::string::npos) {
  32. return "";
  33. } else {
  34. return filename.substr(0, found);
  35. }
  36. }
  37. Status SyncDir(const std::string& dir) {
  38. // As this is a test it isn't required to *actually* sync this directory.
  39. return Status::OK();
  40. }
  41. // A basic file truncation function suitable for this test.
  42. Status Truncate(const std::string& filename, uint64_t length) {
  43. leveldb::Env* env = leveldb::Env::Default();
  44. SequentialFile* orig_file;
  45. Status s = env->NewSequentialFile(filename, &orig_file);
  46. if (!s.ok())
  47. return s;
  48. char* scratch = new char[length];
  49. leveldb::Slice result;
  50. s = orig_file->Read(length, &result, scratch);
  51. delete orig_file;
  52. if (s.ok()) {
  53. std::string tmp_name = GetDirName(filename) + "/truncate.tmp";
  54. WritableFile* tmp_file;
  55. s = env->NewWritableFile(tmp_name, &tmp_file);
  56. if (s.ok()) {
  57. s = tmp_file->Append(result);
  58. delete tmp_file;
  59. if (s.ok()) {
  60. s = env->RenameFile(tmp_name, filename);
  61. } else {
  62. env->DeleteFile(tmp_name);
  63. }
  64. }
  65. }
  66. delete[] scratch;
  67. return s;
  68. }
  69. struct FileState {
  70. std::string filename_;
  71. ssize_t pos_;
  72. ssize_t pos_at_last_sync_;
  73. ssize_t pos_at_last_flush_;
  74. FileState(const std::string& filename)
  75. : filename_(filename),
  76. pos_(-1),
  77. pos_at_last_sync_(-1),
  78. pos_at_last_flush_(-1) { }
  79. FileState() : pos_(-1), pos_at_last_sync_(-1), pos_at_last_flush_(-1) {}
  80. bool IsFullySynced() const { return pos_ <= 0 || pos_ == pos_at_last_sync_; }
  81. Status DropUnsyncedData() const;
  82. };
  83. } // anonymous namespace
  84. // A wrapper around WritableFile which informs another Env whenever this file
  85. // is written to or sync'ed.
  86. class TestWritableFile : public WritableFile {
  87. public:
  88. TestWritableFile(const std::string& fname,
  89. WritableFile* f,
  90. FaultInjectionTestEnv* env);
  91. virtual ~TestWritableFile();
  92. virtual Status Append(const Slice& data);
  93. virtual Status Close();
  94. virtual Status Flush();
  95. virtual Status Sync();
  96. private:
  97. FileState state_;
  98. WritableFile* target_;
  99. bool writable_file_opened_;
  100. FaultInjectionTestEnv* env_;
  101. Status SyncParent();
  102. };
  103. class FaultInjectionTestEnv : public EnvWrapper {
  104. public:
  105. FaultInjectionTestEnv() : EnvWrapper(Env::Default()), filesystem_active_(true) {}
  106. virtual ~FaultInjectionTestEnv() { }
  107. virtual Status NewWritableFile(const std::string& fname,
  108. WritableFile** result);
  109. virtual Status DeleteFile(const std::string& f);
  110. virtual Status RenameFile(const std::string& s, const std::string& t);
  111. void WritableFileClosed(const FileState& state);
  112. Status DropUnsyncedFileData();
  113. Status DeleteFilesCreatedAfterLastDirSync();
  114. void DirWasSynced();
  115. bool IsFileCreatedSinceLastDirSync(const std::string& filename);
  116. void ResetState();
  117. void UntrackFile(const std::string& f);
  118. // Setting the filesystem to inactive is the test equivalent to simulating a
  119. // system reset. Setting to inactive will freeze our saved filesystem state so
  120. // that it will stop being recorded. It can then be reset back to the state at
  121. // the time of the reset.
  122. bool IsFilesystemActive() const { return filesystem_active_; }
  123. void SetFilesystemActive(bool active) { filesystem_active_ = active; }
  124. private:
  125. port::Mutex mutex_;
  126. std::map<std::string, FileState> db_file_state_;
  127. std::set<std::string> new_files_since_last_dir_sync_;
  128. bool filesystem_active_; // Record flushes, syncs, writes
  129. };
  130. TestWritableFile::TestWritableFile(const std::string& fname,
  131. WritableFile* f,
  132. FaultInjectionTestEnv* env)
  133. : state_(fname),
  134. target_(f),
  135. writable_file_opened_(true),
  136. env_(env) {
  137. assert(f != NULL);
  138. state_.pos_ = 0;
  139. }
  140. TestWritableFile::~TestWritableFile() {
  141. if (writable_file_opened_) {
  142. Close();
  143. }
  144. delete target_;
  145. }
  146. Status TestWritableFile::Append(const Slice& data) {
  147. Status s = target_->Append(data);
  148. if (s.ok() && env_->IsFilesystemActive()) {
  149. state_.pos_ += data.size();
  150. }
  151. return s;
  152. }
  153. Status TestWritableFile::Close() {
  154. writable_file_opened_ = false;
  155. Status s = target_->Close();
  156. if (s.ok()) {
  157. env_->WritableFileClosed(state_);
  158. }
  159. return s;
  160. }
  161. Status TestWritableFile::Flush() {
  162. Status s = target_->Flush();
  163. if (s.ok() && env_->IsFilesystemActive()) {
  164. state_.pos_at_last_flush_ = state_.pos_;
  165. }
  166. return s;
  167. }
  168. Status TestWritableFile::SyncParent() {
  169. Status s = SyncDir(GetDirName(state_.filename_));
  170. if (s.ok()) {
  171. env_->DirWasSynced();
  172. }
  173. return s;
  174. }
  175. Status TestWritableFile::Sync() {
  176. if (!env_->IsFilesystemActive()) {
  177. return Status::OK();
  178. }
  179. // Ensure new files referred to by the manifest are in the filesystem.
  180. Status s = target_->Sync();
  181. if (s.ok()) {
  182. state_.pos_at_last_sync_ = state_.pos_;
  183. }
  184. if (env_->IsFileCreatedSinceLastDirSync(state_.filename_)) {
  185. Status ps = SyncParent();
  186. if (s.ok() && !ps.ok()) {
  187. s = ps;
  188. }
  189. }
  190. return s;
  191. }
  192. Status FaultInjectionTestEnv::NewWritableFile(const std::string& fname,
  193. WritableFile** result) {
  194. WritableFile* actual_writable_file;
  195. Status s = target()->NewWritableFile(fname, &actual_writable_file);
  196. if (s.ok()) {
  197. *result = new TestWritableFile(fname, actual_writable_file, this);
  198. // WritableFile doesn't append to files, so if the same file is opened again
  199. // then it will be truncated - so forget our saved state.
  200. UntrackFile(fname);
  201. MutexLock l(&mutex_);
  202. new_files_since_last_dir_sync_.insert(fname);
  203. }
  204. return s;
  205. }
  206. Status FaultInjectionTestEnv::DropUnsyncedFileData() {
  207. Status s;
  208. MutexLock l(&mutex_);
  209. for (std::map<std::string, FileState>::const_iterator it =
  210. db_file_state_.begin();
  211. s.ok() && it != db_file_state_.end(); ++it) {
  212. const FileState& state = it->second;
  213. if (!state.IsFullySynced()) {
  214. s = state.DropUnsyncedData();
  215. }
  216. }
  217. return s;
  218. }
  219. void FaultInjectionTestEnv::DirWasSynced() {
  220. MutexLock l(&mutex_);
  221. new_files_since_last_dir_sync_.clear();
  222. }
  223. bool FaultInjectionTestEnv::IsFileCreatedSinceLastDirSync(
  224. const std::string& filename) {
  225. MutexLock l(&mutex_);
  226. return new_files_since_last_dir_sync_.find(filename) !=
  227. new_files_since_last_dir_sync_.end();
  228. }
  229. void FaultInjectionTestEnv::UntrackFile(const std::string& f) {
  230. MutexLock l(&mutex_);
  231. db_file_state_.erase(f);
  232. new_files_since_last_dir_sync_.erase(f);
  233. }
  234. Status FaultInjectionTestEnv::DeleteFile(const std::string& f) {
  235. Status s = EnvWrapper::DeleteFile(f);
  236. ASSERT_OK(s);
  237. if (s.ok()) {
  238. UntrackFile(f);
  239. }
  240. return s;
  241. }
  242. Status FaultInjectionTestEnv::RenameFile(const std::string& s,
  243. const std::string& t) {
  244. Status ret = EnvWrapper::RenameFile(s, t);
  245. if (ret.ok()) {
  246. MutexLock l(&mutex_);
  247. if (db_file_state_.find(s) != db_file_state_.end()) {
  248. db_file_state_[t] = db_file_state_[s];
  249. db_file_state_.erase(s);
  250. }
  251. if (new_files_since_last_dir_sync_.erase(s) != 0) {
  252. assert(new_files_since_last_dir_sync_.find(t) ==
  253. new_files_since_last_dir_sync_.end());
  254. new_files_since_last_dir_sync_.insert(t);
  255. }
  256. }
  257. return ret;
  258. }
  259. void FaultInjectionTestEnv::ResetState() {
  260. MutexLock l(&mutex_);
  261. db_file_state_.clear();
  262. new_files_since_last_dir_sync_.clear();
  263. SetFilesystemActive(true);
  264. }
  265. Status FaultInjectionTestEnv::DeleteFilesCreatedAfterLastDirSync() {
  266. // Because DeleteFile access this container make a copy to avoid deadlock
  267. mutex_.Lock();
  268. std::set<std::string> new_files(new_files_since_last_dir_sync_.begin(),
  269. new_files_since_last_dir_sync_.end());
  270. mutex_.Unlock();
  271. Status s;
  272. std::set<std::string>::const_iterator it;
  273. for (it = new_files.begin(); s.ok() && it != new_files.end(); ++it) {
  274. s = DeleteFile(*it);
  275. }
  276. return s;
  277. }
  278. void FaultInjectionTestEnv::WritableFileClosed(const FileState& state) {
  279. MutexLock l(&mutex_);
  280. db_file_state_[state.filename_] = state;
  281. }
  282. Status FileState::DropUnsyncedData() const {
  283. ssize_t sync_pos = pos_at_last_sync_ == -1 ? 0 : pos_at_last_sync_;
  284. return Truncate(filename_, sync_pos);
  285. }
  286. class FaultInjectionTest {
  287. public:
  288. enum ExpectedVerifResult { VAL_EXPECT_NO_ERROR, VAL_EXPECT_ERROR };
  289. enum ResetMethod { RESET_DROP_UNSYNCED_DATA, RESET_DELETE_UNSYNCED_FILES };
  290. FaultInjectionTestEnv* env_;
  291. std::string dbname_;
  292. Cache* tiny_cache_;
  293. Options options_;
  294. DB* db_;
  295. FaultInjectionTest() : env_(NULL), tiny_cache_(NULL), db_(NULL) { NewDB(); }
  296. ~FaultInjectionTest() { ASSERT_OK(TearDown()); }
  297. Status NewDB() {
  298. assert(db_ == NULL);
  299. assert(tiny_cache_ == NULL);
  300. assert(env_ == NULL);
  301. env_ = new FaultInjectionTestEnv();
  302. options_ = Options();
  303. options_.env = env_;
  304. options_.paranoid_checks = true;
  305. tiny_cache_ = NewLRUCache(100);
  306. options_.block_cache = tiny_cache_;
  307. dbname_ = test::TmpDir() + "/fault_test";
  308. options_.create_if_missing = true;
  309. Status s = OpenDB();
  310. options_.create_if_missing = false;
  311. return s;
  312. }
  313. Status SetUp() {
  314. Status s = TearDown();
  315. if (s.ok()) {
  316. s = NewDB();
  317. }
  318. return s;
  319. }
  320. Status TearDown() {
  321. CloseDB();
  322. Status s = DestroyDB(dbname_, Options());
  323. delete tiny_cache_;
  324. tiny_cache_ = NULL;
  325. delete env_;
  326. env_ = NULL;
  327. return s;
  328. }
  329. void Build(int start_idx, int num_vals) {
  330. std::string key_space, value_space;
  331. WriteBatch batch;
  332. for (int i = start_idx; i < start_idx + num_vals; i++) {
  333. Slice key = Key(i, &key_space);
  334. batch.Clear();
  335. batch.Put(key, Value(i, &value_space));
  336. WriteOptions options;
  337. ASSERT_OK(db_->Write(options, &batch));
  338. }
  339. }
  340. Status ReadValue(int i, std::string* val) const {
  341. std::string key_space, value_space;
  342. Slice key = Key(i, &key_space);
  343. Value(i, &value_space);
  344. ReadOptions options;
  345. return db_->Get(options, key, val);
  346. }
  347. Status Verify(int start_idx, int num_vals,
  348. ExpectedVerifResult expected) const {
  349. std::string val;
  350. std::string value_space;
  351. Status s;
  352. for (int i = start_idx; i < start_idx + num_vals && s.ok(); i++) {
  353. Value(i, &value_space);
  354. s = ReadValue(i, &val);
  355. if (expected == VAL_EXPECT_NO_ERROR) {
  356. if (s.ok()) {
  357. ASSERT_EQ(value_space, val);
  358. }
  359. } else if (s.ok()) {
  360. fprintf(stderr, "Expected an error at %d, but was OK\n", i);
  361. s = Status::IOError(dbname_, "Expected value error:");
  362. } else {
  363. s = Status::OK(); // An expected error
  364. }
  365. }
  366. return s;
  367. }
  368. // Return the ith key
  369. Slice Key(int i, std::string* storage) const {
  370. char buf[100];
  371. snprintf(buf, sizeof(buf), "%016d", i);
  372. storage->assign(buf, strlen(buf));
  373. return Slice(*storage);
  374. }
  375. // Return the value to associate with the specified key
  376. Slice Value(int k, std::string* storage) const {
  377. Random r(k);
  378. return test::RandomString(&r, kValueSize, storage);
  379. }
  380. Status OpenDB() {
  381. delete db_;
  382. db_ = NULL;
  383. env_->ResetState();
  384. return DB::Open(options_, dbname_, &db_);
  385. }
  386. void CloseDB() {
  387. delete db_;
  388. db_ = NULL;
  389. }
  390. void DeleteAllData() {
  391. Iterator* iter = db_->NewIterator(ReadOptions());
  392. WriteOptions options;
  393. for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
  394. ASSERT_OK(db_->Delete(WriteOptions(), iter->key()));
  395. }
  396. delete iter;
  397. }
  398. void ResetDBState(ResetMethod reset_method) {
  399. switch (reset_method) {
  400. case RESET_DROP_UNSYNCED_DATA:
  401. ASSERT_OK(env_->DropUnsyncedFileData());
  402. break;
  403. case RESET_DELETE_UNSYNCED_FILES:
  404. ASSERT_OK(env_->DeleteFilesCreatedAfterLastDirSync());
  405. break;
  406. default:
  407. assert(false);
  408. }
  409. }
  410. void PartialCompactTestPreFault(int num_pre_sync, int num_post_sync) {
  411. DeleteAllData();
  412. Build(0, num_pre_sync);
  413. db_->CompactRange(NULL, NULL);
  414. Build(num_pre_sync, num_post_sync);
  415. }
  416. void PartialCompactTestReopenWithFault(ResetMethod reset_method,
  417. int num_pre_sync,
  418. int num_post_sync) {
  419. env_->SetFilesystemActive(false);
  420. CloseDB();
  421. ResetDBState(reset_method);
  422. ASSERT_OK(OpenDB());
  423. ASSERT_OK(Verify(0, num_pre_sync, FaultInjectionTest::VAL_EXPECT_NO_ERROR));
  424. ASSERT_OK(Verify(num_pre_sync, num_post_sync, FaultInjectionTest::VAL_EXPECT_ERROR));
  425. }
  426. void NoWriteTestPreFault() {
  427. }
  428. void NoWriteTestReopenWithFault(ResetMethod reset_method) {
  429. CloseDB();
  430. ResetDBState(reset_method);
  431. ASSERT_OK(OpenDB());
  432. }
  433. };
  434. TEST(FaultInjectionTest, FaultTest) {
  435. Random rnd(0);
  436. ASSERT_OK(SetUp());
  437. for (size_t idx = 0; idx < kNumIterations; idx++) {
  438. int num_pre_sync = rnd.Uniform(kMaxNumValues);
  439. int num_post_sync = rnd.Uniform(kMaxNumValues);
  440. PartialCompactTestPreFault(num_pre_sync, num_post_sync);
  441. PartialCompactTestReopenWithFault(RESET_DROP_UNSYNCED_DATA,
  442. num_pre_sync,
  443. num_post_sync);
  444. NoWriteTestPreFault();
  445. NoWriteTestReopenWithFault(RESET_DROP_UNSYNCED_DATA);
  446. PartialCompactTestPreFault(num_pre_sync, num_post_sync);
  447. // No new files created so we expect all values since no files will be
  448. // dropped.
  449. PartialCompactTestReopenWithFault(RESET_DELETE_UNSYNCED_FILES,
  450. num_pre_sync + num_post_sync,
  451. 0);
  452. NoWriteTestPreFault();
  453. NoWriteTestReopenWithFault(RESET_DELETE_UNSYNCED_FILES);
  454. }
  455. }
  456. } // namespace leveldb
  457. int main(int argc, char** argv) {
  458. return leveldb::test::RunAllTests();
  459. }