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).
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.
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".
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"
28 namespace ROCKSDB_NAMESPACE
{
30 class TestWritableFile
;
31 class FaultInjectionTestEnv
;
34 std::string filename_
;
36 ssize_t pos_at_last_sync_
;
37 ssize_t pos_at_last_flush_
;
39 explicit FileState(const std::string
& filename
)
40 : filename_(filename
),
42 pos_at_last_sync_(-1),
43 pos_at_last_flush_(-1) {}
45 FileState() : pos_(-1), pos_at_last_sync_(-1), pos_at_last_flush_(-1) {}
47 bool IsFullySynced() const { return pos_
<= 0 || pos_
== pos_at_last_sync_
; }
49 Status
DropUnsyncedData(Env
* env
) const;
51 Status
DropRandomUnsyncedData(Env
* env
, Random
* rand
) const;
54 // A wrapper around WritableFileWriter* file
55 // is written to or sync'ed.
56 class TestWritableFile
: public WritableFile
{
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
;
63 virtual Status
Truncate(uint64_t size
) override
{
64 return target_
->Truncate(size
);
66 virtual Status
Close() override
;
67 virtual Status
Flush() override
;
68 virtual Status
Sync() override
;
69 virtual bool IsSyncThreadSafe() const override
{ return true; }
70 virtual Status
PositionedAppend(const Slice
& data
,
71 uint64_t offset
) override
{
72 return target_
->PositionedAppend(data
, offset
);
74 virtual bool use_direct_io() const override
{
75 return target_
->use_direct_io();
80 std::unique_ptr
<WritableFile
> target_
;
81 bool writable_file_opened_
;
82 FaultInjectionTestEnv
* env_
;
85 // A wrapper around WritableFileWriter* file
86 // is written to or sync'ed.
87 class TestRandomRWFile
: public RandomRWFile
{
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
;
99 size_t GetRequiredBufferAlignment() const override
{
100 return target_
->GetRequiredBufferAlignment();
102 bool use_direct_io() const override
{ return target_
->use_direct_io(); };
105 std::unique_ptr
<RandomRWFile
> target_
;
107 FaultInjectionTestEnv
* env_
;
110 class TestDirectory
: public Directory
{
112 explicit TestDirectory(FaultInjectionTestEnv
* env
, std::string dirname
,
114 : env_(env
), dirname_(dirname
), dir_(dir
) {}
117 virtual Status
Fsync() override
;
120 FaultInjectionTestEnv
* env_
;
121 std::string dirname_
;
122 std::unique_ptr
<Directory
> dir_
;
125 class FaultInjectionTestEnv
: public EnvWrapper
{
127 explicit FaultInjectionTestEnv(Env
* base
)
128 : EnvWrapper(base
), filesystem_active_(true) {}
129 virtual ~FaultInjectionTestEnv() {}
131 Status
NewDirectory(const std::string
& name
,
132 std::unique_ptr
<Directory
>* result
) override
;
134 Status
NewWritableFile(const std::string
& fname
,
135 std::unique_ptr
<WritableFile
>* result
,
136 const EnvOptions
& soptions
) override
;
138 Status
ReopenWritableFile(const std::string
& fname
,
139 std::unique_ptr
<WritableFile
>* result
,
140 const EnvOptions
& soptions
) override
;
142 Status
NewRandomRWFile(const std::string
& fname
,
143 std::unique_ptr
<RandomRWFile
>* result
,
144 const EnvOptions
& soptions
) override
;
146 Status
NewRandomAccessFile(const std::string
& fname
,
147 std::unique_ptr
<RandomAccessFile
>* result
,
148 const EnvOptions
& soptions
) override
;
150 virtual Status
DeleteFile(const std::string
& f
) override
;
152 virtual Status
RenameFile(const std::string
& s
,
153 const std::string
& t
) override
;
155 // Undef to eliminate clash on Windows
157 virtual Status
GetFreeSpace(const std::string
& path
,
158 uint64_t* disk_free
) override
{
159 if (!IsFilesystemActive() && error_
== Status::NoSpace()) {
163 return target()->GetFreeSpace(path
, disk_free
);
167 void WritableFileClosed(const FileState
& state
);
169 void WritableFileSynced(const FileState
& state
);
171 void WritableFileAppended(const FileState
& state
);
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
);
177 Status
DropUnsyncedFileData();
179 Status
DropRandomUnsyncedFileData(Random
* rnd
);
181 Status
DeleteFilesCreatedAfterLastDirSync();
185 void UntrackFile(const std::string
& f
);
187 void SyncDir(const std::string
& dirname
) {
188 MutexLock
l(&mutex_
);
189 dir_to_new_files_since_last_sync_
.erase(dirname
);
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.
196 bool IsFilesystemActive() {
197 MutexLock
l(&mutex_
);
198 return filesystem_active_
;
200 void SetFilesystemActiveNoLock(bool active
,
201 Status error
= Status::Corruption("Not active")) {
202 filesystem_active_
= active
;
207 void SetFilesystemActive(bool active
,
208 Status error
= Status::Corruption("Not active")) {
209 MutexLock
l(&mutex_
);
210 SetFilesystemActiveNoLock(active
, error
);
212 void AssertNoOpenFile() { assert(open_files_
.empty()); }
213 Status
GetError() { return error_
; }
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
225 } // namespace ROCKSDB_NAMESPACE