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 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 #pragma once 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 "file/filename.h" 23 #include "rocksdb/db.h" 24 #include "rocksdb/env.h" 25 #include "util/mutexlock.h" 26 #include "util/random.h" 27 28 namespace ROCKSDB_NAMESPACE { 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 FileStateFileState39 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 FileStateFileState45 FileState() : pos_(-1), pos_at_last_sync_(-1), pos_at_last_flush_(-1) {} 46 IsFullySyncedFileState47 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, 59 std::unique_ptr<WritableFile>&& f, 60 FaultInjectionTestEnv* env); 61 virtual ~TestWritableFile(); 62 virtual Status Append(const Slice& data) override; Truncate(uint64_t size)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; IsSyncThreadSafe()69 virtual bool IsSyncThreadSafe() const override { return true; } PositionedAppend(const Slice & data,uint64_t offset)70 virtual Status PositionedAppend(const Slice& data, 71 uint64_t offset) override { 72 return target_->PositionedAppend(data, offset); 73 } use_direct_io()74 virtual bool use_direct_io() const override { 75 return target_->use_direct_io(); 76 }; 77 78 private: 79 FileState state_; 80 std::unique_ptr<WritableFile> target_; 81 bool writable_file_opened_; 82 FaultInjectionTestEnv* env_; 83 }; 84 85 // A wrapper around WritableFileWriter* file 86 // is written to or sync'ed. 87 class TestRandomRWFile : public RandomRWFile { 88 public: 89 explicit TestRandomRWFile(const std::string& fname, 90 std::unique_ptr<RandomRWFile>&& f, 91 FaultInjectionTestEnv* env); 92 virtual ~TestRandomRWFile(); 93 Status Write(uint64_t offset, const Slice& data) override; 94 Status Read(uint64_t offset, size_t n, Slice* result, 95 char* scratch) const override; 96 Status Close() override; 97 Status Flush() override; 98 Status Sync() override; GetRequiredBufferAlignment()99 size_t GetRequiredBufferAlignment() const override { 100 return target_->GetRequiredBufferAlignment(); 101 } use_direct_io()102 bool use_direct_io() const override { return target_->use_direct_io(); }; 103 104 private: 105 std::unique_ptr<RandomRWFile> target_; 106 bool file_opened_; 107 FaultInjectionTestEnv* env_; 108 }; 109 110 class TestDirectory : public Directory { 111 public: TestDirectory(FaultInjectionTestEnv * env,std::string dirname,Directory * dir)112 explicit TestDirectory(FaultInjectionTestEnv* env, std::string dirname, 113 Directory* dir) 114 : env_(env), dirname_(dirname), dir_(dir) {} ~TestDirectory()115 ~TestDirectory() {} 116 117 virtual Status Fsync() override; 118 119 private: 120 FaultInjectionTestEnv* env_; 121 std::string dirname_; 122 std::unique_ptr<Directory> dir_; 123 }; 124 125 class FaultInjectionTestEnv : public EnvWrapper { 126 public: FaultInjectionTestEnv(Env * base)127 explicit FaultInjectionTestEnv(Env* base) 128 : EnvWrapper(base), filesystem_active_(true) {} ~FaultInjectionTestEnv()129 virtual ~FaultInjectionTestEnv() {} 130 131 Status NewDirectory(const std::string& name, 132 std::unique_ptr<Directory>* result) override; 133 134 Status NewWritableFile(const std::string& fname, 135 std::unique_ptr<WritableFile>* result, 136 const EnvOptions& soptions) override; 137 138 Status ReopenWritableFile(const std::string& fname, 139 std::unique_ptr<WritableFile>* result, 140 const EnvOptions& soptions) override; 141 142 Status NewRandomRWFile(const std::string& fname, 143 std::unique_ptr<RandomRWFile>* result, 144 const EnvOptions& soptions) override; 145 146 Status NewRandomAccessFile(const std::string& fname, 147 std::unique_ptr<RandomAccessFile>* result, 148 const EnvOptions& soptions) override; 149 150 virtual Status DeleteFile(const std::string& f) override; 151 152 virtual Status RenameFile(const std::string& s, 153 const std::string& t) override; 154 155 // Undef to eliminate clash on Windows 156 #undef GetFreeSpace GetFreeSpace(const std::string & path,uint64_t * disk_free)157 virtual Status GetFreeSpace(const std::string& path, 158 uint64_t* disk_free) override { 159 if (!IsFilesystemActive() && error_ == Status::NoSpace()) { 160 *disk_free = 0; 161 return Status::OK(); 162 } else { 163 return target()->GetFreeSpace(path, disk_free); 164 } 165 } 166 167 void WritableFileClosed(const FileState& state); 168 169 void WritableFileSynced(const FileState& state); 170 171 void WritableFileAppended(const FileState& state); 172 173 // For every file that is not fully synced, make a call to `func` with 174 // FileState of the file as the parameter. 175 Status DropFileData(std::function<Status(Env*, FileState)> func); 176 177 Status DropUnsyncedFileData(); 178 179 Status DropRandomUnsyncedFileData(Random* rnd); 180 181 Status DeleteFilesCreatedAfterLastDirSync(); 182 183 void ResetState(); 184 185 void UntrackFile(const std::string& f); 186 SyncDir(const std::string & dirname)187 void SyncDir(const std::string& dirname) { 188 MutexLock l(&mutex_); 189 dir_to_new_files_since_last_sync_.erase(dirname); 190 } 191 192 // Setting the filesystem to inactive is the test equivalent to simulating a 193 // system reset. Setting to inactive will freeze our saved filesystem state so 194 // that it will stop being recorded. It can then be reset back to the state at 195 // the time of the reset. IsFilesystemActive()196 bool IsFilesystemActive() { 197 MutexLock l(&mutex_); 198 return filesystem_active_; 199 } 200 void SetFilesystemActiveNoLock(bool active, 201 Status error = Status::Corruption("Not active")) { 202 filesystem_active_ = active; 203 if (!active) { 204 error_ = error; 205 } 206 } 207 void SetFilesystemActive(bool active, 208 Status error = Status::Corruption("Not active")) { 209 MutexLock l(&mutex_); 210 SetFilesystemActiveNoLock(active, error); 211 } AssertNoOpenFile()212 void AssertNoOpenFile() { assert(open_files_.empty()); } GetError()213 Status GetError() { return error_; } 214 215 private: 216 port::Mutex mutex_; 217 std::map<std::string, FileState> db_file_state_; 218 std::set<std::string> open_files_; 219 std::unordered_map<std::string, std::set<std::string>> 220 dir_to_new_files_since_last_sync_; 221 bool filesystem_active_; // Record flushes, syncs, writes 222 Status error_; 223 }; 224 225 } // namespace ROCKSDB_NAMESPACE 226