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