fault_injection_env.cc 16 KB

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