選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

554 行
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 FileState& state,
  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 NewAppendableFile(const std::string& fname,
  110. WritableFile** result);
  111. virtual Status DeleteFile(const std::string& f);
  112. virtual Status RenameFile(const std::string& s, const std::string& t);
  113. void WritableFileClosed(const FileState& state);
  114. Status DropUnsyncedFileData();
  115. Status DeleteFilesCreatedAfterLastDirSync();
  116. void DirWasSynced();
  117. bool IsFileCreatedSinceLastDirSync(const std::string& filename);
  118. void ResetState();
  119. void UntrackFile(const std::string& f);
  120. // Setting the filesystem to inactive is the test equivalent to simulating a
  121. // system reset. Setting to inactive will freeze our saved filesystem state so
  122. // that it will stop being recorded. It can then be reset back to the state at
  123. // the time of the reset.
  124. bool IsFilesystemActive() const { return filesystem_active_; }
  125. void SetFilesystemActive(bool active) { filesystem_active_ = active; }
  126. private:
  127. port::Mutex mutex_;
  128. std::map<std::string, FileState> db_file_state_;
  129. std::set<std::string> new_files_since_last_dir_sync_;
  130. bool filesystem_active_; // Record flushes, syncs, writes
  131. };
  132. TestWritableFile::TestWritableFile(const FileState& state,
  133. WritableFile* f,
  134. FaultInjectionTestEnv* env)
  135. : state_(state),
  136. target_(f),
  137. writable_file_opened_(true),
  138. env_(env) {
  139. assert(f != NULL);
  140. }
  141. TestWritableFile::~TestWritableFile() {
  142. if (writable_file_opened_) {
  143. Close();
  144. }
  145. delete target_;
  146. }
  147. Status TestWritableFile::Append(const Slice& data) {
  148. Status s = target_->Append(data);
  149. if (s.ok() && env_->IsFilesystemActive()) {
  150. state_.pos_ += data.size();
  151. }
  152. return s;
  153. }
  154. Status TestWritableFile::Close() {
  155. writable_file_opened_ = false;
  156. Status s = target_->Close();
  157. if (s.ok()) {
  158. env_->WritableFileClosed(state_);
  159. }
  160. return s;
  161. }
  162. Status TestWritableFile::Flush() {
  163. Status s = target_->Flush();
  164. if (s.ok() && env_->IsFilesystemActive()) {
  165. state_.pos_at_last_flush_ = state_.pos_;
  166. }
  167. return s;
  168. }
  169. Status TestWritableFile::SyncParent() {
  170. Status s = SyncDir(GetDirName(state_.filename_));
  171. if (s.ok()) {
  172. env_->DirWasSynced();
  173. }
  174. return s;
  175. }
  176. Status TestWritableFile::Sync() {
  177. if (!env_->IsFilesystemActive()) {
  178. return Status::OK();
  179. }
  180. // Ensure new files referred to by the manifest are in the filesystem.
  181. Status s = target_->Sync();
  182. if (s.ok()) {
  183. state_.pos_at_last_sync_ = state_.pos_;
  184. }
  185. if (env_->IsFileCreatedSinceLastDirSync(state_.filename_)) {
  186. Status ps = SyncParent();
  187. if (s.ok() && !ps.ok()) {
  188. s = ps;
  189. }
  190. }
  191. return s;
  192. }
  193. Status FaultInjectionTestEnv::NewWritableFile(const std::string& fname,
  194. WritableFile** result) {
  195. WritableFile* actual_writable_file;
  196. Status s = target()->NewWritableFile(fname, &actual_writable_file);
  197. if (s.ok()) {
  198. FileState state(fname);
  199. state.pos_ = 0;
  200. *result = new TestWritableFile(state, actual_writable_file, this);
  201. // NewWritableFile doesn't append to files, so if the same file is
  202. // opened again then it will be truncated - so forget our saved
  203. // state.
  204. UntrackFile(fname);
  205. MutexLock l(&mutex_);
  206. new_files_since_last_dir_sync_.insert(fname);
  207. }
  208. return s;
  209. }
  210. Status FaultInjectionTestEnv::NewAppendableFile(const std::string& fname,
  211. WritableFile** result) {
  212. WritableFile* actual_writable_file;
  213. Status s = target()->NewAppendableFile(fname, &actual_writable_file);
  214. if (s.ok()) {
  215. FileState state(fname);
  216. state.pos_ = 0;
  217. {
  218. MutexLock l(&mutex_);
  219. if (db_file_state_.count(fname) == 0) {
  220. new_files_since_last_dir_sync_.insert(fname);
  221. } else {
  222. state = db_file_state_[fname];
  223. }
  224. }
  225. *result = new TestWritableFile(state, actual_writable_file, this);
  226. }
  227. return s;
  228. }
  229. Status FaultInjectionTestEnv::DropUnsyncedFileData() {
  230. Status s;
  231. MutexLock l(&mutex_);
  232. for (std::map<std::string, FileState>::const_iterator it =
  233. db_file_state_.begin();
  234. s.ok() && it != db_file_state_.end(); ++it) {
  235. const FileState& state = it->second;
  236. if (!state.IsFullySynced()) {
  237. s = state.DropUnsyncedData();
  238. }
  239. }
  240. return s;
  241. }
  242. void FaultInjectionTestEnv::DirWasSynced() {
  243. MutexLock l(&mutex_);
  244. new_files_since_last_dir_sync_.clear();
  245. }
  246. bool FaultInjectionTestEnv::IsFileCreatedSinceLastDirSync(
  247. const std::string& filename) {
  248. MutexLock l(&mutex_);
  249. return new_files_since_last_dir_sync_.find(filename) !=
  250. new_files_since_last_dir_sync_.end();
  251. }
  252. void FaultInjectionTestEnv::UntrackFile(const std::string& f) {
  253. MutexLock l(&mutex_);
  254. db_file_state_.erase(f);
  255. new_files_since_last_dir_sync_.erase(f);
  256. }
  257. Status FaultInjectionTestEnv::DeleteFile(const std::string& f) {
  258. Status s = EnvWrapper::DeleteFile(f);
  259. ASSERT_OK(s);
  260. if (s.ok()) {
  261. UntrackFile(f);
  262. }
  263. return s;
  264. }
  265. Status FaultInjectionTestEnv::RenameFile(const std::string& s,
  266. const std::string& t) {
  267. Status ret = EnvWrapper::RenameFile(s, t);
  268. if (ret.ok()) {
  269. MutexLock l(&mutex_);
  270. if (db_file_state_.find(s) != db_file_state_.end()) {
  271. db_file_state_[t] = db_file_state_[s];
  272. db_file_state_.erase(s);
  273. }
  274. if (new_files_since_last_dir_sync_.erase(s) != 0) {
  275. assert(new_files_since_last_dir_sync_.find(t) ==
  276. new_files_since_last_dir_sync_.end());
  277. new_files_since_last_dir_sync_.insert(t);
  278. }
  279. }
  280. return ret;
  281. }
  282. void FaultInjectionTestEnv::ResetState() {
  283. // Since we are not destroying the database, the existing files
  284. // should keep their recorded synced/flushed state. Therefore
  285. // we do not reset db_file_state_ and new_files_since_last_dir_sync_.
  286. MutexLock l(&mutex_);
  287. SetFilesystemActive(true);
  288. }
  289. Status FaultInjectionTestEnv::DeleteFilesCreatedAfterLastDirSync() {
  290. // Because DeleteFile access this container make a copy to avoid deadlock
  291. mutex_.Lock();
  292. std::set<std::string> new_files(new_files_since_last_dir_sync_.begin(),
  293. new_files_since_last_dir_sync_.end());
  294. mutex_.Unlock();
  295. Status s;
  296. std::set<std::string>::const_iterator it;
  297. for (it = new_files.begin(); s.ok() && it != new_files.end(); ++it) {
  298. s = DeleteFile(*it);
  299. }
  300. return s;
  301. }
  302. void FaultInjectionTestEnv::WritableFileClosed(const FileState& state) {
  303. MutexLock l(&mutex_);
  304. db_file_state_[state.filename_] = state;
  305. }
  306. Status FileState::DropUnsyncedData() const {
  307. ssize_t sync_pos = pos_at_last_sync_ == -1 ? 0 : pos_at_last_sync_;
  308. return Truncate(filename_, sync_pos);
  309. }
  310. class FaultInjectionTest {
  311. public:
  312. enum ExpectedVerifResult { VAL_EXPECT_NO_ERROR, VAL_EXPECT_ERROR };
  313. enum ResetMethod { RESET_DROP_UNSYNCED_DATA, RESET_DELETE_UNSYNCED_FILES };
  314. FaultInjectionTestEnv* env_;
  315. std::string dbname_;
  316. Cache* tiny_cache_;
  317. Options options_;
  318. DB* db_;
  319. FaultInjectionTest()
  320. : env_(new FaultInjectionTestEnv),
  321. tiny_cache_(NewLRUCache(100)),
  322. db_(NULL) {
  323. dbname_ = test::TmpDir() + "/fault_test";
  324. DestroyDB(dbname_, Options()); // Destroy any db from earlier run
  325. options_.reuse_logs = true;
  326. options_.env = env_;
  327. options_.paranoid_checks = true;
  328. options_.block_cache = tiny_cache_;
  329. options_.create_if_missing = true;
  330. }
  331. ~FaultInjectionTest() {
  332. CloseDB();
  333. DestroyDB(dbname_, Options());
  334. delete tiny_cache_;
  335. delete env_;
  336. }
  337. void ReuseLogs(bool reuse) {
  338. options_.reuse_logs = reuse;
  339. }
  340. void Build(int start_idx, int num_vals) {
  341. std::string key_space, value_space;
  342. WriteBatch batch;
  343. for (int i = start_idx; i < start_idx + num_vals; i++) {
  344. Slice key = Key(i, &key_space);
  345. batch.Clear();
  346. batch.Put(key, Value(i, &value_space));
  347. WriteOptions options;
  348. ASSERT_OK(db_->Write(options, &batch));
  349. }
  350. }
  351. Status ReadValue(int i, std::string* val) const {
  352. std::string key_space, value_space;
  353. Slice key = Key(i, &key_space);
  354. Value(i, &value_space);
  355. ReadOptions options;
  356. return db_->Get(options, key, val);
  357. }
  358. Status Verify(int start_idx, int num_vals,
  359. ExpectedVerifResult expected) const {
  360. std::string val;
  361. std::string value_space;
  362. Status s;
  363. for (int i = start_idx; i < start_idx + num_vals && s.ok(); i++) {
  364. Value(i, &value_space);
  365. s = ReadValue(i, &val);
  366. if (expected == VAL_EXPECT_NO_ERROR) {
  367. if (s.ok()) {
  368. ASSERT_EQ(value_space, val);
  369. }
  370. } else if (s.ok()) {
  371. fprintf(stderr, "Expected an error at %d, but was OK\n", i);
  372. s = Status::IOError(dbname_, "Expected value error:");
  373. } else {
  374. s = Status::OK(); // An expected error
  375. }
  376. }
  377. return s;
  378. }
  379. // Return the ith key
  380. Slice Key(int i, std::string* storage) const {
  381. char buf[100];
  382. snprintf(buf, sizeof(buf), "%016d", i);
  383. storage->assign(buf, strlen(buf));
  384. return Slice(*storage);
  385. }
  386. // Return the value to associate with the specified key
  387. Slice Value(int k, std::string* storage) const {
  388. Random r(k);
  389. return test::RandomString(&r, kValueSize, storage);
  390. }
  391. Status OpenDB() {
  392. delete db_;
  393. db_ = NULL;
  394. env_->ResetState();
  395. return DB::Open(options_, dbname_, &db_);
  396. }
  397. void CloseDB() {
  398. delete db_;
  399. db_ = NULL;
  400. }
  401. void DeleteAllData() {
  402. Iterator* iter = db_->NewIterator(ReadOptions());
  403. WriteOptions options;
  404. for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
  405. ASSERT_OK(db_->Delete(WriteOptions(), iter->key()));
  406. }
  407. delete iter;
  408. }
  409. void ResetDBState(ResetMethod reset_method) {
  410. switch (reset_method) {
  411. case RESET_DROP_UNSYNCED_DATA:
  412. ASSERT_OK(env_->DropUnsyncedFileData());
  413. break;
  414. case RESET_DELETE_UNSYNCED_FILES:
  415. ASSERT_OK(env_->DeleteFilesCreatedAfterLastDirSync());
  416. break;
  417. default:
  418. assert(false);
  419. }
  420. }
  421. void PartialCompactTestPreFault(int num_pre_sync, int num_post_sync) {
  422. DeleteAllData();
  423. Build(0, num_pre_sync);
  424. db_->CompactRange(NULL, NULL);
  425. Build(num_pre_sync, num_post_sync);
  426. }
  427. void PartialCompactTestReopenWithFault(ResetMethod reset_method,
  428. int num_pre_sync,
  429. int num_post_sync) {
  430. env_->SetFilesystemActive(false);
  431. CloseDB();
  432. ResetDBState(reset_method);
  433. ASSERT_OK(OpenDB());
  434. ASSERT_OK(Verify(0, num_pre_sync, FaultInjectionTest::VAL_EXPECT_NO_ERROR));
  435. ASSERT_OK(Verify(num_pre_sync, num_post_sync, FaultInjectionTest::VAL_EXPECT_ERROR));
  436. }
  437. void NoWriteTestPreFault() {
  438. }
  439. void NoWriteTestReopenWithFault(ResetMethod reset_method) {
  440. CloseDB();
  441. ResetDBState(reset_method);
  442. ASSERT_OK(OpenDB());
  443. }
  444. void DoTest() {
  445. Random rnd(0);
  446. ASSERT_OK(OpenDB());
  447. for (size_t idx = 0; idx < kNumIterations; idx++) {
  448. int num_pre_sync = rnd.Uniform(kMaxNumValues);
  449. int num_post_sync = rnd.Uniform(kMaxNumValues);
  450. PartialCompactTestPreFault(num_pre_sync, num_post_sync);
  451. PartialCompactTestReopenWithFault(RESET_DROP_UNSYNCED_DATA,
  452. num_pre_sync,
  453. num_post_sync);
  454. NoWriteTestPreFault();
  455. NoWriteTestReopenWithFault(RESET_DROP_UNSYNCED_DATA);
  456. PartialCompactTestPreFault(num_pre_sync, num_post_sync);
  457. // No new files created so we expect all values since no files will be
  458. // dropped.
  459. PartialCompactTestReopenWithFault(RESET_DELETE_UNSYNCED_FILES,
  460. num_pre_sync + num_post_sync,
  461. 0);
  462. NoWriteTestPreFault();
  463. NoWriteTestReopenWithFault(RESET_DELETE_UNSYNCED_FILES);
  464. }
  465. }
  466. };
  467. TEST(FaultInjectionTest, FaultTestNoLogReuse) {
  468. ReuseLogs(false);
  469. DoTest();
  470. }
  471. TEST(FaultInjectionTest, FaultTestWithLogReuse) {
  472. ReuseLogs(true);
  473. DoTest();
  474. }
  475. } // namespace leveldb
  476. int main(int argc, char** argv) {
  477. return leveldb::test::RunAllTests();
  478. }