]>
Commit | Line | Data |
---|---|---|
f67539c2 TL |
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: | |
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 | |
25 | ~DbStressListener() override { assert(num_pending_file_creations_ == 0); } | |
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 | ||
33 | void OnFlushBegin(DB* /*db*/, | |
34 | const FlushJobInfo& /*flush_job_info*/) override { | |
35 | RandomSleep(); | |
36 | } | |
37 | ||
38 | void OnTableFileDeleted(const TableFileDeletionInfo& /*info*/) override { | |
39 | RandomSleep(); | |
40 | } | |
41 | ||
42 | void OnCompactionBegin(DB* /*db*/, const CompactionJobInfo& /*ci*/) override { | |
43 | RandomSleep(); | |
44 | } | |
45 | ||
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 | ||
59 | void OnTableFileCreationStarted( | |
60 | const TableFileCreationBriefInfo& /*info*/) override { | |
61 | ++num_pending_file_creations_; | |
62 | } | |
63 | ||
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 | ||
80 | void OnMemTableSealed(const MemTableInfo& /*info*/) override { | |
81 | RandomSleep(); | |
82 | } | |
83 | ||
84 | void OnColumnFamilyHandleDeletionStarted( | |
85 | ColumnFamilyHandle* /*handle*/) override { | |
86 | RandomSleep(); | |
87 | } | |
88 | ||
89 | void OnExternalFileIngested( | |
90 | DB* /*db*/, const ExternalFileIngestionInfo& /*info*/) override { | |
91 | RandomSleep(); | |
92 | } | |
93 | ||
94 | void OnBackgroundError(BackgroundErrorReason /* reason */, | |
95 | Status* /* bg_error */) override { | |
96 | RandomSleep(); | |
97 | } | |
98 | ||
99 | void OnStallConditionsChanged(const WriteStallInfo& /*info*/) override { | |
100 | RandomSleep(); | |
101 | } | |
102 | ||
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 | ||
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 | ||
129 | bool ShouldBeNotifiedOnFileIO() override { | |
130 | RandomSleep(); | |
131 | return static_cast<bool>(Random::GetTLSInstance()->OneIn(1)); | |
132 | } | |
133 | ||
134 | void OnErrorRecoveryBegin(BackgroundErrorReason /* reason */, | |
135 | Status /* bg_error */, | |
136 | bool* /* auto_recovery */) override { | |
137 | RandomSleep(); | |
138 | } | |
139 | ||
140 | void OnErrorRecoveryCompleted(Status /* old_bg_error */) override { | |
141 | RandomSleep(); | |
142 | } | |
143 | ||
144 | protected: | |
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 | ||
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 | ||
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 | ||
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 | ||
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 |