fault_injection_test_env.h 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. // Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
  2. // This source code is licensed under both the GPLv2 (found in the
  3. // COPYING file in the root directory) and Apache 2.0 License
  4. // (found in the LICENSE.Apache file in the root directory).
  5. //
  6. // Copyright 2014 The LevelDB Authors. All rights reserved.
  7. // Use of this source code is governed by a BSD-style license that can be
  8. // found in the LICENSE file. See the AUTHORS file for names of contributors.
  9. // This test uses a custom Env to keep track of the state of a filesystem as of
  10. // the last "sync". It then checks for data loss errors by purposely dropping
  11. // file data (or entire files) not protected by a "sync".
  12. #pragma once
  13. #include <map>
  14. #include <set>
  15. #include <string>
  16. #include "db/version_set.h"
  17. #include "env/mock_env.h"
  18. #include "file/filename.h"
  19. #include "rocksdb/db.h"
  20. #include "rocksdb/env.h"
  21. #include "util/mutexlock.h"
  22. #include "util/random.h"
  23. namespace ROCKSDB_NAMESPACE {
  24. class TestWritableFile;
  25. class FaultInjectionTestEnv;
  26. struct FileState {
  27. std::string filename_;
  28. ssize_t pos_;
  29. ssize_t pos_at_last_sync_;
  30. ssize_t pos_at_last_flush_;
  31. explicit FileState(const std::string& filename)
  32. : filename_(filename),
  33. pos_(-1),
  34. pos_at_last_sync_(-1),
  35. pos_at_last_flush_(-1) {}
  36. FileState() : pos_(-1), pos_at_last_sync_(-1), pos_at_last_flush_(-1) {}
  37. bool IsFullySynced() const { return pos_ <= 0 || pos_ == pos_at_last_sync_; }
  38. Status DropUnsyncedData(Env* env) const;
  39. Status DropRandomUnsyncedData(Env* env, Random* rand) const;
  40. };
  41. // A wrapper around WritableFileWriter* file
  42. // is written to or sync'ed.
  43. class TestWritableFile : public WritableFile {
  44. public:
  45. explicit TestWritableFile(const std::string& fname,
  46. std::unique_ptr<WritableFile>&& f,
  47. FaultInjectionTestEnv* env);
  48. virtual ~TestWritableFile();
  49. virtual Status Append(const Slice& data) override;
  50. virtual Status Truncate(uint64_t size) override {
  51. return target_->Truncate(size);
  52. }
  53. virtual Status Close() override;
  54. virtual Status Flush() override;
  55. virtual Status Sync() override;
  56. virtual bool IsSyncThreadSafe() const override { return true; }
  57. virtual Status PositionedAppend(const Slice& data,
  58. uint64_t offset) override {
  59. return target_->PositionedAppend(data, offset);
  60. }
  61. virtual bool use_direct_io() const override {
  62. return target_->use_direct_io();
  63. };
  64. private:
  65. FileState state_;
  66. std::unique_ptr<WritableFile> target_;
  67. bool writable_file_opened_;
  68. FaultInjectionTestEnv* env_;
  69. };
  70. // A wrapper around WritableFileWriter* file
  71. // is written to or sync'ed.
  72. class TestRandomRWFile : public RandomRWFile {
  73. public:
  74. explicit TestRandomRWFile(const std::string& fname,
  75. std::unique_ptr<RandomRWFile>&& f,
  76. FaultInjectionTestEnv* env);
  77. virtual ~TestRandomRWFile();
  78. Status Write(uint64_t offset, const Slice& data) override;
  79. Status Read(uint64_t offset, size_t n, Slice* result,
  80. char* scratch) const override;
  81. Status Close() override;
  82. Status Flush() override;
  83. Status Sync() override;
  84. size_t GetRequiredBufferAlignment() const override {
  85. return target_->GetRequiredBufferAlignment();
  86. }
  87. bool use_direct_io() const override { return target_->use_direct_io(); };
  88. private:
  89. std::unique_ptr<RandomRWFile> target_;
  90. bool file_opened_;
  91. FaultInjectionTestEnv* env_;
  92. };
  93. class TestDirectory : public Directory {
  94. public:
  95. explicit TestDirectory(FaultInjectionTestEnv* env, std::string dirname,
  96. Directory* dir)
  97. : env_(env), dirname_(dirname), dir_(dir) {}
  98. ~TestDirectory() {}
  99. virtual Status Fsync() override;
  100. private:
  101. FaultInjectionTestEnv* env_;
  102. std::string dirname_;
  103. std::unique_ptr<Directory> dir_;
  104. };
  105. class FaultInjectionTestEnv : public EnvWrapper {
  106. public:
  107. explicit FaultInjectionTestEnv(Env* base)
  108. : EnvWrapper(base), filesystem_active_(true) {}
  109. virtual ~FaultInjectionTestEnv() {}
  110. Status NewDirectory(const std::string& name,
  111. std::unique_ptr<Directory>* result) override;
  112. Status NewWritableFile(const std::string& fname,
  113. std::unique_ptr<WritableFile>* result,
  114. const EnvOptions& soptions) override;
  115. Status ReopenWritableFile(const std::string& fname,
  116. std::unique_ptr<WritableFile>* result,
  117. const EnvOptions& soptions) override;
  118. Status NewRandomRWFile(const std::string& fname,
  119. std::unique_ptr<RandomRWFile>* result,
  120. const EnvOptions& soptions) override;
  121. Status NewRandomAccessFile(const std::string& fname,
  122. std::unique_ptr<RandomAccessFile>* result,
  123. const EnvOptions& soptions) override;
  124. virtual Status DeleteFile(const std::string& f) override;
  125. virtual Status RenameFile(const std::string& s,
  126. const std::string& t) override;
  127. // Undef to eliminate clash on Windows
  128. #undef GetFreeSpace
  129. virtual Status GetFreeSpace(const std::string& path,
  130. uint64_t* disk_free) override {
  131. if (!IsFilesystemActive() && error_ == Status::NoSpace()) {
  132. *disk_free = 0;
  133. return Status::OK();
  134. } else {
  135. return target()->GetFreeSpace(path, disk_free);
  136. }
  137. }
  138. void WritableFileClosed(const FileState& state);
  139. void WritableFileSynced(const FileState& state);
  140. void WritableFileAppended(const FileState& state);
  141. // For every file that is not fully synced, make a call to `func` with
  142. // FileState of the file as the parameter.
  143. Status DropFileData(std::function<Status(Env*, FileState)> func);
  144. Status DropUnsyncedFileData();
  145. Status DropRandomUnsyncedFileData(Random* rnd);
  146. Status DeleteFilesCreatedAfterLastDirSync();
  147. void ResetState();
  148. void UntrackFile(const std::string& f);
  149. void SyncDir(const std::string& dirname) {
  150. MutexLock l(&mutex_);
  151. dir_to_new_files_since_last_sync_.erase(dirname);
  152. }
  153. // Setting the filesystem to inactive is the test equivalent to simulating a
  154. // system reset. Setting to inactive will freeze our saved filesystem state so
  155. // that it will stop being recorded. It can then be reset back to the state at
  156. // the time of the reset.
  157. bool IsFilesystemActive() {
  158. MutexLock l(&mutex_);
  159. return filesystem_active_;
  160. }
  161. void SetFilesystemActiveNoLock(bool active,
  162. Status error = Status::Corruption("Not active")) {
  163. filesystem_active_ = active;
  164. if (!active) {
  165. error_ = error;
  166. }
  167. }
  168. void SetFilesystemActive(bool active,
  169. Status error = Status::Corruption("Not active")) {
  170. MutexLock l(&mutex_);
  171. SetFilesystemActiveNoLock(active, error);
  172. }
  173. void AssertNoOpenFile() { assert(open_files_.empty()); }
  174. Status GetError() { return error_; }
  175. private:
  176. port::Mutex mutex_;
  177. std::map<std::string, FileState> db_file_state_;
  178. std::set<std::string> open_files_;
  179. std::unordered_map<std::string, std::set<std::string>>
  180. dir_to_new_files_since_last_sync_;
  181. bool filesystem_active_; // Record flushes, syncs, writes
  182. Status error_;
  183. };
  184. } // namespace ROCKSDB_NAMESPACE