]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | // Copyright (c) 2011-present, Facebook, Inc. All rights reserved. |
2 | // This source code is licensed under the BSD-style license found in the | |
3 | // LICENSE file in the root directory of this source tree. An additional grant | |
4 | // of patent rights can be found in the PATENTS file in the same 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 | ||
10 | // This test uses a custom Env to keep track of the state of a filesystem as of | |
11 | // the last "sync". It then checks for data loss errors by purposely dropping | |
12 | // file data (or entire files) not protected by a "sync". | |
13 | ||
14 | #ifndef UTIL_FAULT_INJECTION_TEST_ENV_H_ | |
15 | #define UTIL_FAULT_INJECTION_TEST_ENV_H_ | |
16 | ||
17 | #include <map> | |
18 | #include <set> | |
19 | #include <string> | |
20 | ||
21 | #include "db/version_set.h" | |
22 | #include "env/mock_env.h" | |
23 | #include "rocksdb/db.h" | |
24 | #include "rocksdb/env.h" | |
25 | #include "util/filename.h" | |
26 | #include "util/mutexlock.h" | |
27 | #include "util/random.h" | |
28 | ||
29 | namespace rocksdb { | |
30 | ||
31 | class TestWritableFile; | |
32 | class FaultInjectionTestEnv; | |
33 | ||
34 | struct FileState { | |
35 | std::string filename_; | |
36 | ssize_t pos_; | |
37 | ssize_t pos_at_last_sync_; | |
38 | ssize_t pos_at_last_flush_; | |
39 | ||
40 | explicit FileState(const std::string& filename) | |
41 | : filename_(filename), | |
42 | pos_(-1), | |
43 | pos_at_last_sync_(-1), | |
44 | pos_at_last_flush_(-1) {} | |
45 | ||
46 | FileState() : pos_(-1), pos_at_last_sync_(-1), pos_at_last_flush_(-1) {} | |
47 | ||
48 | bool IsFullySynced() const { return pos_ <= 0 || pos_ == pos_at_last_sync_; } | |
49 | ||
50 | Status DropUnsyncedData(Env* env) const; | |
51 | ||
52 | Status DropRandomUnsyncedData(Env* env, Random* rand) const; | |
53 | }; | |
54 | ||
55 | // A wrapper around WritableFileWriter* file | |
56 | // is written to or sync'ed. | |
57 | class TestWritableFile : public WritableFile { | |
58 | public: | |
59 | explicit TestWritableFile(const std::string& fname, | |
60 | unique_ptr<WritableFile>&& f, | |
61 | FaultInjectionTestEnv* env); | |
62 | virtual ~TestWritableFile(); | |
63 | virtual Status Append(const Slice& data) override; | |
64 | virtual Status Truncate(uint64_t size) override { | |
65 | return target_->Truncate(size); | |
66 | } | |
67 | virtual Status Close() override; | |
68 | virtual Status Flush() override; | |
69 | virtual Status Sync() override; | |
70 | virtual bool IsSyncThreadSafe() const override { return true; } | |
71 | ||
72 | private: | |
73 | FileState state_; | |
74 | unique_ptr<WritableFile> target_; | |
75 | bool writable_file_opened_; | |
76 | FaultInjectionTestEnv* env_; | |
77 | }; | |
78 | ||
79 | class TestDirectory : public Directory { | |
80 | public: | |
81 | explicit TestDirectory(FaultInjectionTestEnv* env, std::string dirname, | |
82 | Directory* dir) | |
83 | : env_(env), dirname_(dirname), dir_(dir) {} | |
84 | ~TestDirectory() {} | |
85 | ||
86 | virtual Status Fsync() override; | |
87 | ||
88 | private: | |
89 | FaultInjectionTestEnv* env_; | |
90 | std::string dirname_; | |
91 | unique_ptr<Directory> dir_; | |
92 | }; | |
93 | ||
94 | class FaultInjectionTestEnv : public EnvWrapper { | |
95 | public: | |
96 | explicit FaultInjectionTestEnv(Env* base) | |
97 | : EnvWrapper(base), filesystem_active_(true) {} | |
98 | virtual ~FaultInjectionTestEnv() {} | |
99 | ||
100 | Status NewDirectory(const std::string& name, | |
101 | unique_ptr<Directory>* result) override; | |
102 | ||
103 | Status NewWritableFile(const std::string& fname, | |
104 | unique_ptr<WritableFile>* result, | |
105 | const EnvOptions& soptions) override; | |
106 | ||
107 | virtual Status DeleteFile(const std::string& f) override; | |
108 | ||
109 | virtual Status RenameFile(const std::string& s, | |
110 | const std::string& t) override; | |
111 | ||
112 | void WritableFileClosed(const FileState& state); | |
113 | ||
114 | // For every file that is not fully synced, make a call to `func` with | |
115 | // FileState of the file as the parameter. | |
116 | Status DropFileData(std::function<Status(Env*, FileState)> func); | |
117 | ||
118 | Status DropUnsyncedFileData(); | |
119 | ||
120 | Status DropRandomUnsyncedFileData(Random* rnd); | |
121 | ||
122 | Status DeleteFilesCreatedAfterLastDirSync(); | |
123 | ||
124 | void ResetState(); | |
125 | ||
126 | void UntrackFile(const std::string& f); | |
127 | ||
128 | void SyncDir(const std::string& dirname) { | |
129 | MutexLock l(&mutex_); | |
130 | dir_to_new_files_since_last_sync_.erase(dirname); | |
131 | } | |
132 | ||
133 | // Setting the filesystem to inactive is the test equivalent to simulating a | |
134 | // system reset. Setting to inactive will freeze our saved filesystem state so | |
135 | // that it will stop being recorded. It can then be reset back to the state at | |
136 | // the time of the reset. | |
137 | bool IsFilesystemActive() { | |
138 | MutexLock l(&mutex_); | |
139 | return filesystem_active_; | |
140 | } | |
141 | void SetFilesystemActiveNoLock(bool active) { filesystem_active_ = active; } | |
142 | void SetFilesystemActive(bool active) { | |
143 | MutexLock l(&mutex_); | |
144 | SetFilesystemActiveNoLock(active); | |
145 | } | |
146 | void AssertNoOpenFile() { assert(open_files_.empty()); } | |
147 | ||
148 | private: | |
149 | port::Mutex mutex_; | |
150 | std::map<std::string, FileState> db_file_state_; | |
151 | std::set<std::string> open_files_; | |
152 | std::unordered_map<std::string, std::set<std::string>> | |
153 | dir_to_new_files_since_last_sync_; | |
154 | bool filesystem_active_; // Record flushes, syncs, writes | |
155 | }; | |
156 | ||
157 | } // namespace rocksdb | |
158 | ||
159 | #endif // UTIL_FAULT_INJECTION_TEST_ENV_H_ |