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 #ifdef GFLAGS
7 #pragma once
8 
9 #include "rocksdb/listener.h"
10 #include "util/gflags_compat.h"
11 
12 DECLARE_int32(compact_files_one_in);
13 
14 namespace ROCKSDB_NAMESPACE {
15 class DbStressListener : public EventListener {
16  public:
DbStressListener(const std::string & db_name,const std::vector<DbPath> & db_paths,const std::vector<ColumnFamilyDescriptor> & column_families)17   DbStressListener(const std::string& db_name,
18                    const std::vector<DbPath>& db_paths,
19                    const std::vector<ColumnFamilyDescriptor>& column_families)
20       : db_name_(db_name),
21         db_paths_(db_paths),
22         column_families_(column_families),
23         num_pending_file_creations_(0) {}
24 #ifndef ROCKSDB_LITE
~DbStressListener()25   ~DbStressListener() override { assert(num_pending_file_creations_ == 0); }
OnFlushCompleted(DB *,const FlushJobInfo & info)26   void OnFlushCompleted(DB* /*db*/, const FlushJobInfo& info) override {
27     assert(IsValidColumnFamilyName(info.cf_name));
28     VerifyFilePath(info.file_path);
29     // pretending doing some work here
30     RandomSleep();
31   }
32 
OnFlushBegin(DB *,const FlushJobInfo &)33   void OnFlushBegin(DB* /*db*/,
34                     const FlushJobInfo& /*flush_job_info*/) override {
35     RandomSleep();
36   }
37 
OnTableFileDeleted(const TableFileDeletionInfo &)38   void OnTableFileDeleted(const TableFileDeletionInfo& /*info*/) override {
39     RandomSleep();
40   }
41 
OnCompactionBegin(DB *,const CompactionJobInfo &)42   void OnCompactionBegin(DB* /*db*/, const CompactionJobInfo& /*ci*/) override {
43     RandomSleep();
44   }
45 
OnCompactionCompleted(DB *,const CompactionJobInfo & ci)46   void OnCompactionCompleted(DB* /*db*/, const CompactionJobInfo& ci) override {
47     assert(IsValidColumnFamilyName(ci.cf_name));
48     assert(ci.input_files.size() + ci.output_files.size() > 0U);
49     for (const auto& file_path : ci.input_files) {
50       VerifyFilePath(file_path);
51     }
52     for (const auto& file_path : ci.output_files) {
53       VerifyFilePath(file_path);
54     }
55     // pretending doing some work here
56     RandomSleep();
57   }
58 
OnTableFileCreationStarted(const TableFileCreationBriefInfo &)59   void OnTableFileCreationStarted(
60       const TableFileCreationBriefInfo& /*info*/) override {
61     ++num_pending_file_creations_;
62   }
63 
OnTableFileCreated(const TableFileCreationInfo & info)64   void OnTableFileCreated(const TableFileCreationInfo& info) override {
65     assert(info.db_name == db_name_);
66     assert(IsValidColumnFamilyName(info.cf_name));
67     if (info.file_size) {
68       VerifyFilePath(info.file_path);
69     }
70     assert(info.job_id > 0 || FLAGS_compact_files_one_in > 0);
71     if (info.status.ok() && info.file_size > 0) {
72       assert(info.table_properties.data_size > 0 ||
73              info.table_properties.num_range_deletions > 0);
74       assert(info.table_properties.raw_key_size > 0);
75       assert(info.table_properties.num_entries > 0);
76     }
77     --num_pending_file_creations_;
78   }
79 
OnMemTableSealed(const MemTableInfo &)80   void OnMemTableSealed(const MemTableInfo& /*info*/) override {
81     RandomSleep();
82   }
83 
OnColumnFamilyHandleDeletionStarted(ColumnFamilyHandle *)84   void OnColumnFamilyHandleDeletionStarted(
85       ColumnFamilyHandle* /*handle*/) override {
86     RandomSleep();
87   }
88 
OnExternalFileIngested(DB *,const ExternalFileIngestionInfo &)89   void OnExternalFileIngested(
90       DB* /*db*/, const ExternalFileIngestionInfo& /*info*/) override {
91     RandomSleep();
92   }
93 
OnBackgroundError(BackgroundErrorReason,Status *)94   void OnBackgroundError(BackgroundErrorReason /* reason */,
95                          Status* /* bg_error */) override {
96     RandomSleep();
97   }
98 
OnStallConditionsChanged(const WriteStallInfo &)99   void OnStallConditionsChanged(const WriteStallInfo& /*info*/) override {
100     RandomSleep();
101   }
102 
OnFileReadFinish(const FileOperationInfo & info)103   void OnFileReadFinish(const FileOperationInfo& info) override {
104     // Even empty callback is valuable because sometimes some locks are
105     // released in order to make the callback.
106 
107     // Sleep carefully here as it is a frequent operation and we don't want
108     // to slow down the tests. We always sleep when the read is large.
109     // When read is small, sleep in a small chance.
110     size_t length_read = info.length;
111     if (length_read >= 1000000 || Random::GetTLSInstance()->OneIn(1000)) {
112       RandomSleep();
113     }
114   }
115 
OnFileWriteFinish(const FileOperationInfo & info)116   void OnFileWriteFinish(const FileOperationInfo& info) override {
117     // Even empty callback is valuable because sometimes some locks are
118     // released in order to make the callback.
119 
120     // Sleep carefully here as it is a frequent operation and we don't want
121     // to slow down the tests. When the write is large, always sleep.
122     // Otherwise, sleep in a relatively small chance.
123     size_t length_write = info.length;
124     if (length_write >= 1000000 || Random::GetTLSInstance()->OneIn(64)) {
125       RandomSleep();
126     }
127   }
128 
ShouldBeNotifiedOnFileIO()129   bool ShouldBeNotifiedOnFileIO() override {
130     RandomSleep();
131     return static_cast<bool>(Random::GetTLSInstance()->OneIn(1));
132   }
133 
OnErrorRecoveryBegin(BackgroundErrorReason,Status,bool *)134   void OnErrorRecoveryBegin(BackgroundErrorReason /* reason */,
135                             Status /* bg_error */,
136                             bool* /* auto_recovery */) override {
137     RandomSleep();
138   }
139 
OnErrorRecoveryCompleted(Status)140   void OnErrorRecoveryCompleted(Status /* old_bg_error */) override {
141     RandomSleep();
142   }
143 
144  protected:
IsValidColumnFamilyName(const std::string & cf_name)145   bool IsValidColumnFamilyName(const std::string& cf_name) const {
146     if (cf_name == kDefaultColumnFamilyName) {
147       return true;
148     }
149     // The column family names in the stress tests are numbers.
150     for (size_t i = 0; i < cf_name.size(); ++i) {
151       if (cf_name[i] < '0' || cf_name[i] > '9') {
152         return false;
153       }
154     }
155     return true;
156   }
157 
VerifyFileDir(const std::string & file_dir)158   void VerifyFileDir(const std::string& file_dir) {
159 #ifndef NDEBUG
160     if (db_name_ == file_dir) {
161       return;
162     }
163     for (const auto& db_path : db_paths_) {
164       if (db_path.path == file_dir) {
165         return;
166       }
167     }
168     for (auto& cf : column_families_) {
169       for (const auto& cf_path : cf.options.cf_paths) {
170         if (cf_path.path == file_dir) {
171           return;
172         }
173       }
174     }
175     assert(false);
176 #else
177     (void)file_dir;
178 #endif  // !NDEBUG
179   }
180 
VerifyFileName(const std::string & file_name)181   void VerifyFileName(const std::string& file_name) {
182 #ifndef NDEBUG
183     uint64_t file_number;
184     FileType file_type;
185     bool result = ParseFileName(file_name, &file_number, &file_type);
186     assert(result);
187     assert(file_type == kTableFile);
188 #else
189     (void)file_name;
190 #endif  // !NDEBUG
191   }
192 
VerifyFilePath(const std::string & file_path)193   void VerifyFilePath(const std::string& file_path) {
194 #ifndef NDEBUG
195     size_t pos = file_path.find_last_of("/");
196     if (pos == std::string::npos) {
197       VerifyFileName(file_path);
198     } else {
199       if (pos > 0) {
200         VerifyFileDir(file_path.substr(0, pos));
201       }
202       VerifyFileName(file_path.substr(pos));
203     }
204 #else
205     (void)file_path;
206 #endif  // !NDEBUG
207   }
208 
RandomSleep()209   void RandomSleep() {
210     std::this_thread::sleep_for(
211         std::chrono::microseconds(Random::GetTLSInstance()->Uniform(5000)));
212   }
213 #endif  // !ROCKSDB_LITE
214 
215  private:
216   std::string db_name_;
217   std::vector<DbPath> db_paths_;
218   std::vector<ColumnFamilyDescriptor> column_families_;
219   std::atomic<int> num_pending_file_creations_;
220 };
221 }  // namespace ROCKSDB_NAMESPACE
222 #endif  // GFLAGS
223