fault_injection_test_env.cc 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437
  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. #include "test_util/fault_injection_test_env.h"
  13. #include <functional>
  14. #include <utility>
  15. namespace ROCKSDB_NAMESPACE {
  16. // Assume a filename, and not a directory name like "/foo/bar/"
  17. std::string GetDirName(const std::string filename) {
  18. size_t found = filename.find_last_of("/\\");
  19. if (found == std::string::npos) {
  20. return "";
  21. } else {
  22. return filename.substr(0, found);
  23. }
  24. }
  25. // A basic file truncation function suitable for this test.
  26. Status Truncate(Env* env, const std::string& filename, uint64_t length) {
  27. std::unique_ptr<SequentialFile> orig_file;
  28. const EnvOptions options;
  29. Status s = env->NewSequentialFile(filename, &orig_file, options);
  30. if (!s.ok()) {
  31. fprintf(stderr, "Cannot open file %s for truncation: %s\n",
  32. filename.c_str(), s.ToString().c_str());
  33. return s;
  34. }
  35. std::unique_ptr<char[]> scratch(new char[length]);
  36. ROCKSDB_NAMESPACE::Slice result;
  37. s = orig_file->Read(length, &result, scratch.get());
  38. #ifdef OS_WIN
  39. orig_file.reset();
  40. #endif
  41. if (s.ok()) {
  42. std::string tmp_name = GetDirName(filename) + "/truncate.tmp";
  43. std::unique_ptr<WritableFile> tmp_file;
  44. s = env->NewWritableFile(tmp_name, &tmp_file, options);
  45. if (s.ok()) {
  46. s = tmp_file->Append(result);
  47. if (s.ok()) {
  48. s = env->RenameFile(tmp_name, filename);
  49. } else {
  50. fprintf(stderr, "Cannot rename file %s to %s: %s\n", tmp_name.c_str(),
  51. filename.c_str(), s.ToString().c_str());
  52. env->DeleteFile(tmp_name);
  53. }
  54. }
  55. }
  56. if (!s.ok()) {
  57. fprintf(stderr, "Cannot truncate file %s: %s\n", filename.c_str(),
  58. s.ToString().c_str());
  59. }
  60. return s;
  61. }
  62. // Trim the tailing "/" in the end of `str`
  63. std::string TrimDirname(const std::string& str) {
  64. size_t found = str.find_last_not_of("/");
  65. if (found == std::string::npos) {
  66. return str;
  67. }
  68. return str.substr(0, found + 1);
  69. }
  70. // Return pair <parent directory name, file name> of a full path.
  71. std::pair<std::string, std::string> GetDirAndName(const std::string& name) {
  72. std::string dirname = GetDirName(name);
  73. std::string fname = name.substr(dirname.size() + 1);
  74. return std::make_pair(dirname, fname);
  75. }
  76. Status FileState::DropUnsyncedData(Env* env) const {
  77. ssize_t sync_pos = pos_at_last_sync_ == -1 ? 0 : pos_at_last_sync_;
  78. return Truncate(env, filename_, sync_pos);
  79. }
  80. Status FileState::DropRandomUnsyncedData(Env* env, Random* rand) const {
  81. ssize_t sync_pos = pos_at_last_sync_ == -1 ? 0 : pos_at_last_sync_;
  82. assert(pos_ >= sync_pos);
  83. int range = static_cast<int>(pos_ - sync_pos);
  84. uint64_t truncated_size =
  85. static_cast<uint64_t>(sync_pos) + rand->Uniform(range);
  86. return Truncate(env, filename_, truncated_size);
  87. }
  88. Status TestDirectory::Fsync() {
  89. if (!env_->IsFilesystemActive()) {
  90. return env_->GetError();
  91. }
  92. env_->SyncDir(dirname_);
  93. return dir_->Fsync();
  94. }
  95. TestWritableFile::TestWritableFile(const std::string& fname,
  96. std::unique_ptr<WritableFile>&& f,
  97. FaultInjectionTestEnv* env)
  98. : state_(fname),
  99. target_(std::move(f)),
  100. writable_file_opened_(true),
  101. env_(env) {
  102. assert(target_ != nullptr);
  103. state_.pos_ = 0;
  104. }
  105. TestWritableFile::~TestWritableFile() {
  106. if (writable_file_opened_) {
  107. Close();
  108. }
  109. }
  110. Status TestWritableFile::Append(const Slice& data) {
  111. if (!env_->IsFilesystemActive()) {
  112. return env_->GetError();
  113. }
  114. Status s = target_->Append(data);
  115. if (s.ok()) {
  116. state_.pos_ += data.size();
  117. env_->WritableFileAppended(state_);
  118. }
  119. return s;
  120. }
  121. Status TestWritableFile::Close() {
  122. writable_file_opened_ = false;
  123. Status s = target_->Close();
  124. if (s.ok()) {
  125. env_->WritableFileClosed(state_);
  126. }
  127. return s;
  128. }
  129. Status TestWritableFile::Flush() {
  130. Status s = target_->Flush();
  131. if (s.ok() && env_->IsFilesystemActive()) {
  132. state_.pos_at_last_flush_ = state_.pos_;
  133. }
  134. return s;
  135. }
  136. Status TestWritableFile::Sync() {
  137. if (!env_->IsFilesystemActive()) {
  138. return Status::IOError("FaultInjectionTestEnv: not active");
  139. }
  140. // No need to actual sync.
  141. state_.pos_at_last_sync_ = state_.pos_;
  142. env_->WritableFileSynced(state_);
  143. return Status::OK();
  144. }
  145. TestRandomRWFile::TestRandomRWFile(const std::string& /*fname*/,
  146. std::unique_ptr<RandomRWFile>&& f,
  147. FaultInjectionTestEnv* env)
  148. : target_(std::move(f)), file_opened_(true), env_(env) {
  149. assert(target_ != nullptr);
  150. }
  151. TestRandomRWFile::~TestRandomRWFile() {
  152. if (file_opened_) {
  153. Close();
  154. }
  155. }
  156. Status TestRandomRWFile::Write(uint64_t offset, const Slice& data) {
  157. if (!env_->IsFilesystemActive()) {
  158. return env_->GetError();
  159. }
  160. return target_->Write(offset, data);
  161. }
  162. Status TestRandomRWFile::Read(uint64_t offset, size_t n, Slice* result,
  163. char* scratch) const {
  164. if (!env_->IsFilesystemActive()) {
  165. return env_->GetError();
  166. }
  167. return target_->Read(offset, n, result, scratch);
  168. }
  169. Status TestRandomRWFile::Close() {
  170. file_opened_ = false;
  171. return target_->Close();
  172. }
  173. Status TestRandomRWFile::Flush() {
  174. if (!env_->IsFilesystemActive()) {
  175. return env_->GetError();
  176. }
  177. return target_->Flush();
  178. }
  179. Status TestRandomRWFile::Sync() {
  180. if (!env_->IsFilesystemActive()) {
  181. return env_->GetError();
  182. }
  183. return target_->Sync();
  184. }
  185. Status FaultInjectionTestEnv::NewDirectory(const std::string& name,
  186. std::unique_ptr<Directory>* result) {
  187. std::unique_ptr<Directory> r;
  188. Status s = target()->NewDirectory(name, &r);
  189. assert(s.ok());
  190. if (!s.ok()) {
  191. return s;
  192. }
  193. result->reset(new TestDirectory(this, TrimDirname(name), r.release()));
  194. return Status::OK();
  195. }
  196. Status FaultInjectionTestEnv::NewWritableFile(
  197. const std::string& fname, std::unique_ptr<WritableFile>* result,
  198. const EnvOptions& soptions) {
  199. if (!IsFilesystemActive()) {
  200. return GetError();
  201. }
  202. // Not allow overwriting files
  203. Status s = target()->FileExists(fname);
  204. if (s.ok()) {
  205. return Status::Corruption("File already exists.");
  206. } else if (!s.IsNotFound()) {
  207. assert(s.IsIOError());
  208. return s;
  209. }
  210. s = target()->NewWritableFile(fname, result, soptions);
  211. if (s.ok()) {
  212. result->reset(new TestWritableFile(fname, std::move(*result), this));
  213. // WritableFileWriter* file is opened
  214. // again then it will be truncated - so forget our saved state.
  215. UntrackFile(fname);
  216. MutexLock l(&mutex_);
  217. open_files_.insert(fname);
  218. auto dir_and_name = GetDirAndName(fname);
  219. auto& list = dir_to_new_files_since_last_sync_[dir_and_name.first];
  220. list.insert(dir_and_name.second);
  221. }
  222. return s;
  223. }
  224. Status FaultInjectionTestEnv::ReopenWritableFile(
  225. const std::string& fname, std::unique_ptr<WritableFile>* result,
  226. const EnvOptions& soptions) {
  227. if (!IsFilesystemActive()) {
  228. return GetError();
  229. }
  230. Status s = target()->ReopenWritableFile(fname, result, soptions);
  231. if (s.ok()) {
  232. result->reset(new TestWritableFile(fname, std::move(*result), this));
  233. // WritableFileWriter* file is opened
  234. // again then it will be truncated - so forget our saved state.
  235. UntrackFile(fname);
  236. MutexLock l(&mutex_);
  237. open_files_.insert(fname);
  238. auto dir_and_name = GetDirAndName(fname);
  239. auto& list = dir_to_new_files_since_last_sync_[dir_and_name.first];
  240. list.insert(dir_and_name.second);
  241. }
  242. return s;
  243. }
  244. Status FaultInjectionTestEnv::NewRandomRWFile(
  245. const std::string& fname, std::unique_ptr<RandomRWFile>* result,
  246. const EnvOptions& soptions) {
  247. if (!IsFilesystemActive()) {
  248. return GetError();
  249. }
  250. Status s = target()->NewRandomRWFile(fname, result, soptions);
  251. if (s.ok()) {
  252. result->reset(new TestRandomRWFile(fname, std::move(*result), this));
  253. // WritableFileWriter* file is opened
  254. // again then it will be truncated - so forget our saved state.
  255. UntrackFile(fname);
  256. MutexLock l(&mutex_);
  257. open_files_.insert(fname);
  258. auto dir_and_name = GetDirAndName(fname);
  259. auto& list = dir_to_new_files_since_last_sync_[dir_and_name.first];
  260. list.insert(dir_and_name.second);
  261. }
  262. return s;
  263. }
  264. Status FaultInjectionTestEnv::NewRandomAccessFile(
  265. const std::string& fname, std::unique_ptr<RandomAccessFile>* result,
  266. const EnvOptions& soptions) {
  267. if (!IsFilesystemActive()) {
  268. return GetError();
  269. }
  270. return target()->NewRandomAccessFile(fname, result, soptions);
  271. }
  272. Status FaultInjectionTestEnv::DeleteFile(const std::string& f) {
  273. if (!IsFilesystemActive()) {
  274. return GetError();
  275. }
  276. Status s = EnvWrapper::DeleteFile(f);
  277. if (!s.ok()) {
  278. fprintf(stderr, "Cannot delete file %s: %s\n", f.c_str(),
  279. s.ToString().c_str());
  280. }
  281. if (s.ok()) {
  282. UntrackFile(f);
  283. }
  284. return s;
  285. }
  286. Status FaultInjectionTestEnv::RenameFile(const std::string& s,
  287. const std::string& t) {
  288. if (!IsFilesystemActive()) {
  289. return GetError();
  290. }
  291. Status ret = EnvWrapper::RenameFile(s, t);
  292. if (ret.ok()) {
  293. MutexLock l(&mutex_);
  294. if (db_file_state_.find(s) != db_file_state_.end()) {
  295. db_file_state_[t] = db_file_state_[s];
  296. db_file_state_.erase(s);
  297. }
  298. auto sdn = GetDirAndName(s);
  299. auto tdn = GetDirAndName(t);
  300. if (dir_to_new_files_since_last_sync_[sdn.first].erase(sdn.second) != 0) {
  301. auto& tlist = dir_to_new_files_since_last_sync_[tdn.first];
  302. assert(tlist.find(tdn.second) == tlist.end());
  303. tlist.insert(tdn.second);
  304. }
  305. }
  306. return ret;
  307. }
  308. void FaultInjectionTestEnv::WritableFileClosed(const FileState& state) {
  309. MutexLock l(&mutex_);
  310. if (open_files_.find(state.filename_) != open_files_.end()) {
  311. db_file_state_[state.filename_] = state;
  312. open_files_.erase(state.filename_);
  313. }
  314. }
  315. void FaultInjectionTestEnv::WritableFileSynced(const FileState& state) {
  316. MutexLock l(&mutex_);
  317. if (open_files_.find(state.filename_) != open_files_.end()) {
  318. if (db_file_state_.find(state.filename_) == db_file_state_.end()) {
  319. db_file_state_.insert(std::make_pair(state.filename_, state));
  320. } else {
  321. db_file_state_[state.filename_] = state;
  322. }
  323. }
  324. }
  325. void FaultInjectionTestEnv::WritableFileAppended(const FileState& state) {
  326. MutexLock l(&mutex_);
  327. if (open_files_.find(state.filename_) != open_files_.end()) {
  328. if (db_file_state_.find(state.filename_) == db_file_state_.end()) {
  329. db_file_state_.insert(std::make_pair(state.filename_, state));
  330. } else {
  331. db_file_state_[state.filename_] = state;
  332. }
  333. }
  334. }
  335. // For every file that is not fully synced, make a call to `func` with
  336. // FileState of the file as the parameter.
  337. Status FaultInjectionTestEnv::DropFileData(
  338. std::function<Status(Env*, FileState)> func) {
  339. Status s;
  340. MutexLock l(&mutex_);
  341. for (std::map<std::string, FileState>::const_iterator it =
  342. db_file_state_.begin();
  343. s.ok() && it != db_file_state_.end(); ++it) {
  344. const FileState& state = it->second;
  345. if (!state.IsFullySynced()) {
  346. s = func(target(), state);
  347. }
  348. }
  349. return s;
  350. }
  351. Status FaultInjectionTestEnv::DropUnsyncedFileData() {
  352. return DropFileData([&](Env* env, const FileState& state) {
  353. return state.DropUnsyncedData(env);
  354. });
  355. }
  356. Status FaultInjectionTestEnv::DropRandomUnsyncedFileData(Random* rnd) {
  357. return DropFileData([&](Env* env, const FileState& state) {
  358. return state.DropRandomUnsyncedData(env, rnd);
  359. });
  360. }
  361. Status FaultInjectionTestEnv::DeleteFilesCreatedAfterLastDirSync() {
  362. // Because DeleteFile access this container make a copy to avoid deadlock
  363. std::map<std::string, std::set<std::string>> map_copy;
  364. {
  365. MutexLock l(&mutex_);
  366. map_copy.insert(dir_to_new_files_since_last_sync_.begin(),
  367. dir_to_new_files_since_last_sync_.end());
  368. }
  369. for (auto& pair : map_copy) {
  370. for (std::string name : pair.second) {
  371. Status s = DeleteFile(pair.first + "/" + name);
  372. if (!s.ok()) {
  373. return s;
  374. }
  375. }
  376. }
  377. return Status::OK();
  378. }
  379. void FaultInjectionTestEnv::ResetState() {
  380. MutexLock l(&mutex_);
  381. db_file_state_.clear();
  382. dir_to_new_files_since_last_sync_.clear();
  383. SetFilesystemActiveNoLock(true);
  384. }
  385. void FaultInjectionTestEnv::UntrackFile(const std::string& f) {
  386. MutexLock l(&mutex_);
  387. auto dir_and_name = GetDirAndName(f);
  388. dir_to_new_files_since_last_sync_[dir_and_name.first].erase(
  389. dir_and_name.second);
  390. db_file_state_.erase(f);
  391. open_files_.erase(f);
  392. }
  393. } // namespace ROCKSDB_NAMESPACE