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 10 // This test uses a custom FileSystem to keep track of the state of a file 11 // system the last "Sync". The data being written is cached in a "buffer". 12 // Only when "Sync" is called, the data will be persistent. It can similate 13 // file data loss (or entire files) not protected by a "Sync". For any of the 14 // FileSystem related operations, by specify the "IOStatus Error", a specific 15 // error can be returned when file system is not activated. 16 17 #pragma once 18 19 #include <map> 20 #include <set> 21 #include <string> 22 23 #include "db/version_set.h" 24 #include "env/mock_env.h" 25 #include "file/filename.h" 26 #include "include/rocksdb/file_system.h" 27 #include "rocksdb/db.h" 28 #include "rocksdb/env.h" 29 #include "util/mutexlock.h" 30 #include "util/random.h" 31 32 namespace ROCKSDB_NAMESPACE { 33 34 class TestFSWritableFile; 35 class FaultInjectionTestFS; 36 37 struct FSFileState { 38 std::string filename_; 39 ssize_t pos_; 40 ssize_t pos_at_last_sync_; 41 ssize_t pos_at_last_flush_; 42 std::string buffer_; 43 FSFileStateFSFileState44 explicit FSFileState(const std::string& filename) 45 : filename_(filename), 46 pos_(-1), 47 pos_at_last_sync_(-1), 48 pos_at_last_flush_(-1) {} 49 FSFileStateFSFileState50 FSFileState() : pos_(-1), pos_at_last_sync_(-1), pos_at_last_flush_(-1) {} 51 IsFullySyncedFSFileState52 bool IsFullySynced() const { return pos_ <= 0 || pos_ == pos_at_last_sync_; } 53 54 IOStatus DropUnsyncedData(); 55 56 IOStatus DropRandomUnsyncedData(Random* rand); 57 }; 58 59 // A wrapper around WritableFileWriter* file 60 // is written to or sync'ed. 61 class TestFSWritableFile : public FSWritableFile { 62 public: 63 explicit TestFSWritableFile(const std::string& fname, 64 std::unique_ptr<FSWritableFile>&& f, 65 FaultInjectionTestFS* fs); 66 virtual ~TestFSWritableFile(); 67 virtual IOStatus Append(const Slice& data, const IOOptions&, 68 IODebugContext*) override; Truncate(uint64_t size,const IOOptions & options,IODebugContext * dbg)69 virtual IOStatus Truncate(uint64_t size, const IOOptions& options, 70 IODebugContext* dbg) override { 71 return target_->Truncate(size, options, dbg); 72 } 73 virtual IOStatus Close(const IOOptions& options, 74 IODebugContext* dbg) override; 75 virtual IOStatus Flush(const IOOptions&, IODebugContext*) override; 76 virtual IOStatus Sync(const IOOptions& options, IODebugContext* dbg) override; IsSyncThreadSafe()77 virtual bool IsSyncThreadSafe() const override { return true; } PositionedAppend(const Slice & data,uint64_t offset,const IOOptions & options,IODebugContext * dbg)78 virtual IOStatus PositionedAppend(const Slice& data, uint64_t offset, 79 const IOOptions& options, 80 IODebugContext* dbg) override { 81 return target_->PositionedAppend(data, offset, options, dbg); 82 } use_direct_io()83 virtual bool use_direct_io() const override { 84 return target_->use_direct_io(); 85 }; 86 87 private: 88 FSFileState state_; 89 std::unique_ptr<FSWritableFile> target_; 90 bool writable_file_opened_; 91 FaultInjectionTestFS* fs_; 92 port::Mutex mutex_; 93 }; 94 95 // A wrapper around WritableFileWriter* file 96 // is written to or sync'ed. 97 class TestFSRandomRWFile : public FSRandomRWFile { 98 public: 99 explicit TestFSRandomRWFile(const std::string& fname, 100 std::unique_ptr<FSRandomRWFile>&& f, 101 FaultInjectionTestFS* fs); 102 virtual ~TestFSRandomRWFile(); 103 IOStatus Write(uint64_t offset, const Slice& data, const IOOptions& options, 104 IODebugContext* dbg) override; 105 IOStatus Read(uint64_t offset, size_t n, const IOOptions& options, 106 Slice* result, char* scratch, 107 IODebugContext* dbg) const override; 108 IOStatus Close(const IOOptions& options, IODebugContext* dbg) override; 109 IOStatus Flush(const IOOptions& options, IODebugContext* dbg) override; 110 IOStatus Sync(const IOOptions& options, IODebugContext* dbg) override; GetRequiredBufferAlignment()111 size_t GetRequiredBufferAlignment() const override { 112 return target_->GetRequiredBufferAlignment(); 113 } use_direct_io()114 bool use_direct_io() const override { return target_->use_direct_io(); }; 115 116 private: 117 std::unique_ptr<FSRandomRWFile> target_; 118 bool file_opened_; 119 FaultInjectionTestFS* fs_; 120 }; 121 122 class TestFSDirectory : public FSDirectory { 123 public: TestFSDirectory(FaultInjectionTestFS * fs,std::string dirname,FSDirectory * dir)124 explicit TestFSDirectory(FaultInjectionTestFS* fs, std::string dirname, 125 FSDirectory* dir) 126 : fs_(fs), dirname_(dirname), dir_(dir) {} ~TestFSDirectory()127 ~TestFSDirectory() {} 128 129 virtual IOStatus Fsync(const IOOptions& options, 130 IODebugContext* dbg) override; 131 132 private: 133 FaultInjectionTestFS* fs_; 134 std::string dirname_; 135 std::unique_ptr<FSDirectory> dir_; 136 }; 137 138 class FaultInjectionTestFS : public FileSystemWrapper { 139 public: FaultInjectionTestFS(std::shared_ptr<FileSystem> base)140 explicit FaultInjectionTestFS(std::shared_ptr<FileSystem> base) 141 : FileSystemWrapper(base), filesystem_active_(true) {} ~FaultInjectionTestFS()142 virtual ~FaultInjectionTestFS() {} 143 Name()144 const char* Name() const override { return "FaultInjectionTestFS"; } 145 146 IOStatus NewDirectory(const std::string& name, const IOOptions& options, 147 std::unique_ptr<FSDirectory>* result, 148 IODebugContext* dbg) override; 149 150 IOStatus NewWritableFile(const std::string& fname, 151 const FileOptions& file_opts, 152 std::unique_ptr<FSWritableFile>* result, 153 IODebugContext* dbg) override; 154 155 IOStatus ReopenWritableFile(const std::string& fname, 156 const FileOptions& file_opts, 157 std::unique_ptr<FSWritableFile>* result, 158 IODebugContext* dbg) override; 159 160 IOStatus NewRandomRWFile(const std::string& fname, 161 const FileOptions& file_opts, 162 std::unique_ptr<FSRandomRWFile>* result, 163 IODebugContext* dbg) override; 164 165 IOStatus NewRandomAccessFile(const std::string& fname, 166 const FileOptions& file_opts, 167 std::unique_ptr<FSRandomAccessFile>* result, 168 IODebugContext* dbg) override; 169 170 virtual IOStatus DeleteFile(const std::string& f, const IOOptions& options, 171 IODebugContext* dbg) override; 172 173 virtual IOStatus RenameFile(const std::string& s, const std::string& t, 174 const IOOptions& options, 175 IODebugContext* dbg) override; 176 177 // Undef to eliminate clash on Windows 178 #undef GetFreeSpace GetFreeSpace(const std::string & path,const IOOptions & options,uint64_t * disk_free,IODebugContext * dbg)179 virtual IOStatus GetFreeSpace(const std::string& path, 180 const IOOptions& options, uint64_t* disk_free, 181 IODebugContext* dbg) override { 182 if (!IsFilesystemActive() && error_ == IOStatus::NoSpace()) { 183 *disk_free = 0; 184 return IOStatus::OK(); 185 } else { 186 return target()->GetFreeSpace(path, options, disk_free, dbg); 187 } 188 } 189 190 void WritableFileClosed(const FSFileState& state); 191 192 void WritableFileSynced(const FSFileState& state); 193 194 void WritableFileAppended(const FSFileState& state); 195 196 IOStatus DropUnsyncedFileData(); 197 198 IOStatus DropRandomUnsyncedFileData(Random* rnd); 199 200 IOStatus DeleteFilesCreatedAfterLastDirSync(const IOOptions& options, 201 IODebugContext* dbg); 202 203 void ResetState(); 204 205 void UntrackFile(const std::string& f); 206 SyncDir(const std::string & dirname)207 void SyncDir(const std::string& dirname) { 208 MutexLock l(&mutex_); 209 dir_to_new_files_since_last_sync_.erase(dirname); 210 } 211 212 // Setting the filesystem to inactive is the test equivalent to simulating a 213 // system reset. Setting to inactive will freeze our saved filesystem state so 214 // that it will stop being recorded. It can then be reset back to the state at 215 // the time of the reset. IsFilesystemActive()216 bool IsFilesystemActive() { 217 MutexLock l(&mutex_); 218 return filesystem_active_; 219 } 220 void SetFilesystemActiveNoLock( 221 bool active, IOStatus error = IOStatus::Corruption("Not active")) { 222 filesystem_active_ = active; 223 if (!active) { 224 error_ = error; 225 } 226 } 227 void SetFilesystemActive( 228 bool active, IOStatus error = IOStatus::Corruption("Not active")) { 229 MutexLock l(&mutex_); 230 SetFilesystemActiveNoLock(active, error); 231 } AssertNoOpenFile()232 void AssertNoOpenFile() { assert(open_files_.empty()); } 233 GetError()234 IOStatus GetError() { return error_; } 235 SetFileSystemIOError(IOStatus io_error)236 void SetFileSystemIOError(IOStatus io_error) { 237 MutexLock l(&mutex_); 238 error_ = io_error; 239 } 240 241 private: 242 port::Mutex mutex_; 243 std::map<std::string, FSFileState> db_file_state_; 244 std::set<std::string> open_files_; 245 std::unordered_map<std::string, std::set<std::string>> 246 dir_to_new_files_since_last_sync_; 247 bool filesystem_active_; // Record flushes, syncs, writes 248 IOStatus error_; 249 }; 250 251 } // namespace ROCKSDB_NAMESPACE 252