1 /*
2 Copyright (c) 2016, Facebook, Inc.
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; version 2 of the License.
7
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12
13 You should have received a copy of the GNU General Public License
14 along with this program; if not, write to the Free Software
15 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
16 #pragma once
17
18 /* C++ standard header files */
19 #include <chrono>
20 #include <regex>
21 #include <string>
22 #include <vector>
23 #include <unordered_map>
24
25 /* MySQL header files */
26 #include "log.h"
27 #include "my_stacktrace.h"
28 #include "sql_regex.h"
29 #include "sql_string.h"
30
31 /* RocksDB header files */
32 #include "rocksdb/slice.h"
33 #include "rocksdb/status.h"
34
35 /* MyRocks header files */
36 #include "./rdb_global.h"
37
38 #ifdef HAVE_JEMALLOC
39 #include <jemalloc/jemalloc.h>
40 #endif
41
42 namespace myrocks {
43
44 /*
45 Guess what?
46 An interface is a class where all members are public by default.
47 */
48
49 #ifndef interface
50 #define interface struct
51 #endif // interface
52
53 /*
54 Introduce C-style pseudo-namespaces, a handy way to make code more readble
55 when calling into a legacy API, which does not have any namespace defined.
56 Since we cannot or don't want to change the API in any way, we can use this
57 mechanism to define readability tokens that look like C++ namespaces, but are
58 not enforced in any way by the compiler, since the pre-compiler strips them
59 out. However, on the calling side, code looks like my_core::thd_ha_data()
60 rather than plain a thd_ha_data() call. This technique adds an immediate
61 visible cue on what type of API we are calling into.
62 */
63
64 #ifndef my_core
65 // C-style pseudo-namespace for MySQL Core API, to be used in decorating calls
66 // to non-obvious MySQL functions, like the ones that do not start with well
67 // known prefixes: "my_", "sql_", and "mysql_".
68 #define my_core
69 #endif // my_core
70
71 /*
72 The intent behind a SHIP_ASSERT() macro is to have a mechanism for validating
73 invariants in retail builds. Traditionally assertions (such as macros defined
74 in <cassert>) are evaluated for performance reasons only in debug builds and
75 become NOOP in retail builds when NDEBUG is defined.
76
77 This macro is intended to validate the invariants which are critical for
78 making sure that data corruption and data loss won't take place. Proper
79 intended usage can be described as "If a particular condition is not true then
80 stop everything what's going on and terminate the process because continued
81 execution will cause really bad things to happen".
82
83 Use the power of SHIP_ASSERT() wisely.
84 */
85 #ifndef SHIP_ASSERT
86 #define SHIP_ASSERT(expr) \
87 do { \
88 if (!(expr)) { \
89 my_safe_printf_stderr("\nShip assert failure: \'%s\'\n", #expr); \
90 abort(); \
91 } \
92 } while (0)
93 #endif // SHIP_ASSERT
94
95 /*
96 Assert a implies b.
97 If a is true, then b must be true.
98 If a is false, then the value is b does not matter.
99 */
100 #ifndef assert_IMP
101 #define assert_IMP(a, b) assert(!(a) || (b))
102 #endif
103
104 /*
105 Assert a if and only if b.
106 a and b must be both true or both false.
107 */
108 #ifndef assert_IFF
109 #define assert_IFF(a, b) \
110 assert(static_cast<bool>(a) == static_cast<bool>(b))
111 #endif
112
113 /*
114 Intent behind this macro is to avoid manually typing the function name every
115 time we want to add the debugging statement and use the compiler for this
116 work. This avoids typical refactoring problems when one renames a function,
117 but the tracing message doesn't get updated.
118
119 We could use __func__ or __FUNCTION__ macros, but __PRETTY_FUNCTION__
120 contains the signature of the function as well as its bare name and provides
121 therefore more context when interpreting the logs.
122 */
123 #define DBUG_ENTER_FUNC() DBUG_ENTER(__PRETTY_FUNCTION__)
124
125 /*
126 Error handling pattern used across MySQL abides by the following rules: "All
127 functions that can report an error (usually an allocation error), should
128 return 0/FALSE/false on success, 1/TRUE/true on failure."
129
130 https://dev.mysql.com/doc/internals/en/additional-suggestions.html has more
131 details.
132
133 To increase the comprehension and readability of MyRocks codebase we'll use
134 constants similar to ones from C standard (EXIT_SUCCESS and EXIT_FAILURE) to
135 make sure that both failure and success paths are clearly identifiable. The
136 definitions of FALSE and TRUE come from <my_global.h>.
137 */
138 #define HA_EXIT_SUCCESS false
139 #define HA_EXIT_FAILURE true
140
141 /*
142 Macros to better convey the intent behind checking the results from locking
143 and unlocking mutexes.
144 */
145 #define RDB_MUTEX_LOCK_CHECK(m) \
146 rdb_check_mutex_call_result(__PRETTY_FUNCTION__, true, mysql_mutex_lock(&m))
147 #define RDB_MUTEX_UNLOCK_CHECK(m) \
148 rdb_check_mutex_call_result(__PRETTY_FUNCTION__, false, \
149 mysql_mutex_unlock(&m))
150
151 /*
152 Generic constant.
153 */
154 const constexpr size_t RDB_MAX_HEXDUMP_LEN = 1000;
155
156 /*
157 Helper function to get an NULL terminated uchar* out of a given MySQL String.
158 */
159
rdb_mysql_str_to_uchar_str(my_core::String * str)160 inline uchar *rdb_mysql_str_to_uchar_str(my_core::String *str) {
161 assert(str != nullptr);
162 return reinterpret_cast<uchar *>(str->c_ptr());
163 }
164
165 /*
166 Helper function to get plain (not necessary NULL terminated) uchar* out of a
167 given STL string.
168 */
169
rdb_std_str_to_uchar_ptr(const std::string & str)170 inline const uchar *rdb_std_str_to_uchar_ptr(const std::string &str) {
171 return reinterpret_cast<const uchar *>(str.data());
172 }
173
174 /*
175 Helper function to convert seconds to milliseconds.
176 */
177
rdb_convert_sec_to_ms(int sec)178 constexpr int rdb_convert_sec_to_ms(int sec) {
179 return std::chrono::milliseconds(std::chrono::seconds(sec)).count();
180 }
181
182 /*
183 Helper function to get plain (not necessary NULL terminated) uchar* out of a
184 given RocksDB item.
185 */
186
rdb_slice_to_uchar_ptr(const rocksdb::Slice * item)187 inline const uchar *rdb_slice_to_uchar_ptr(const rocksdb::Slice *item) {
188 assert(item != nullptr);
189 return reinterpret_cast<const uchar *>(item->data());
190 }
191
192 /*
193 Call this function in cases when you can't rely on garbage collector and need
194 to explicitly purge all unused dirty pages. This should be a relatively rare
195 scenario for cases where it has been verified that this intervention has
196 noticeable benefits.
197 */
purge_all_jemalloc_arenas()198 inline int purge_all_jemalloc_arenas() {
199 #ifdef HAVE_JEMALLOC
200 unsigned narenas = 0;
201 size_t sz = sizeof(unsigned);
202 char name[25] = {0};
203
204 // Get the number of arenas first. Please see `jemalloc` documentation for
205 // all the various options.
206 int result = mallctl("arenas.narenas", &narenas, &sz, nullptr, 0);
207
208 // `mallctl` returns 0 on success and we really want caller to know if all the
209 // trickery actually works.
210 if (result) {
211 return result;
212 }
213
214 // Form the command to be passed to `mallctl` and purge all the unused dirty
215 // pages.
216 snprintf(name, sizeof(name) / sizeof(char), "arena.%d.purge", narenas);
217 result = mallctl(name, nullptr, nullptr, nullptr, 0);
218
219 return result;
220 #else
221 return EXIT_SUCCESS;
222 #endif
223 }
224
225 /*
226 Helper function to check the result of locking or unlocking a mutex. We'll
227 intentionally abort in case of a failure because it's better to terminate
228 the process instead of continuing in an undefined state and corrupting data
229 as a result.
230 */
rdb_check_mutex_call_result(const char * function_name,const bool attempt_lock,const int result)231 inline void rdb_check_mutex_call_result(const char *function_name,
232 const bool attempt_lock,
233 const int result) {
234 if (unlikely(result)) {
235 /* NO_LINT_DEBUG */
236 sql_print_error(
237 "%s a mutex inside %s failed with an "
238 "error code %d.",
239 attempt_lock ? "Locking" : "Unlocking", function_name, result);
240
241 // This will hopefully result in a meaningful stack trace which we can use
242 // to efficiently debug the root cause.
243 abort();
244 }
245 }
246
247 void rdb_log_status_error(const rocksdb::Status &s, const char *msg = nullptr);
248
249 // return true if the marker file exists which indicates that the corruption
250 // has been detected
251 bool rdb_check_rocksdb_corruption();
252
253 // stores a marker file in the data directory so that after restart server
254 // is still aware that rocksdb data is corrupted
255 void rdb_persist_corruption_marker();
256
257 /*
258 Helper functions to parse strings.
259 */
260
261 const char *rdb_skip_spaces(const struct charset_info_st *const cs,
262 const char *str)
263 MY_ATTRIBUTE((__warn_unused_result__));
264
265 bool rdb_compare_strings_ic(const char *const str1, const char *const str2)
266 MY_ATTRIBUTE((__warn_unused_result__));
267
268 const char *rdb_find_in_string(const char *str, const char *pattern,
269 bool *const succeeded)
270 MY_ATTRIBUTE((__warn_unused_result__));
271
272 const char *rdb_check_next_token(const struct charset_info_st *const cs,
273 const char *str, const char *const pattern,
274 bool *const succeeded)
275 MY_ATTRIBUTE((__warn_unused_result__));
276
277 const char *rdb_parse_id(const struct charset_info_st *const cs,
278 const char *str, std::string *const id)
279 MY_ATTRIBUTE((__warn_unused_result__));
280
281 const char *rdb_skip_id(const struct charset_info_st *const cs, const char *str)
282 MY_ATTRIBUTE((__warn_unused_result__));
283
284 const std::vector<std::string> parse_into_tokens(const std::string &s,
285 const char delim);
286
287 /*
288 Helper functions to populate strings.
289 */
290
291 std::string rdb_hexdump(const char *data, const std::size_t data_len,
292 const std::size_t maxsize = 0);
293
294 /*
295 Helper function to see if a database exists
296 */
297 bool rdb_database_exists(const std::string &db_name);
298
299 void warn_about_bad_patterns(const Regex ®ex, const char *name);
300
301 std::vector<std::string> split_into_vector(const std::string &input,
302 char delimiter);
303
304 /**
305 Helper class wrappers to meansure startup time
306 Warning: not thread safe. It is mainly designed to
307 be used during the server startup to collect stats
308 on startup function's exection time.
309
310 Usage:
311 * member function: MyClass::func(args...)
312 * Rdb_exec_time::record(
313 * str, std::mem_fn(MyClass::func), &obj, args...);
314 * static function: static MyClass::fun(args...)
315 * Rdb_exec_time::record(
316 * str, &MyClass::func, args...);
317 *
318 * To report:
319 * Rdb_exec_time::report();
320 */
321 class Rdb_exec_time {
322 private:
323 std::unordered_map<std::string, uint64_t> entries_;
324
325 struct Auto_timer {
Auto_timerAuto_timer326 explicit Auto_timer(std::function<void(uint64_t &)> &&cb)
327 : start_(std::chrono::high_resolution_clock::now()), callback_(cb) {}
328
~Auto_timerAuto_timer329 ~Auto_timer() {
330 auto end = std::chrono::high_resolution_clock::now();
331 uint64_t elapsed =
332 std::chrono::duration_cast<std::chrono::microseconds>(end - start_)
333 .count();
334
335 callback_(elapsed);
336 }
337
338 std::chrono::high_resolution_clock::time_point start_;
339 std::function<void(uint64_t &)> callback_;
340 };
341
342 public:
343 /**
344 * Currently overloaded functions are not cleanly supported as
345 * template (static_cast on the designated function is required
346 * so that compiler can select the right overloaded version,
347 * but the syntax is ugly). Boost has a lift API (BOOST_HOF_LIFT)
348 * which solves this problem but it only supports c++14 and
349 * we're currently on c++11. Until then, this API requires
350 * static cast on overloaded functions passed in as template param.
351 */
352 template <class Fn, class... Args>
353 auto exec(const std::string &key, const Fn &&fn, Args &&... args)
354 -> decltype(fn(args...)) {
355 Auto_timer timer([&](uint64_t &e) { entries_.emplace(key, e); });
356 return fn(args...);
357 }
358
report()359 void report() {
360 if (entries_.size() == 0) {
361 return;
362 }
363
364 std::string result = "\n{\n";
365 for (auto const &t : entries_) {
366 result += " \"" + t.first + "\" : ";
367 result += std::to_string(t.second) + "\n";
368 }
369 entries_.clear();
370
371 result += "}";
372
373 /* NO_LINT_DEBUG */
374 sql_print_information("MyRocks: rdb execution report (microsec): %s",
375 result.c_str());
376 }
377 };
378
379 /*
380 Helper class to make sure cleanup always happens. Helpful for complicated
381 logic where there can be multiple exits/returns requiring cleanup
382 */
383 class Ensure_cleanup {
384 public:
Ensure_cleanup(std::function<void ()> cleanup)385 explicit Ensure_cleanup(std::function<void()> cleanup)
386 : m_cleanup(cleanup), m_skip_cleanup(false) {}
387
~Ensure_cleanup()388 ~Ensure_cleanup() {
389 if (!m_skip_cleanup) {
390 m_cleanup();
391 }
392 }
393
394 // If you want to skip cleanup (such as when the operation is successful)
skip()395 void skip() { m_skip_cleanup = true; }
396
397 private:
398 std::function<void()> m_cleanup;
399 bool m_skip_cleanup;
400 };
401 } // namespace myrocks
402