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#include "rdb_mariadb_port.h"
19
20/* C++ standard header files */
21#include <chrono>
22#include <string>
23#include <vector>
24
25/* MySQL header files */
26#include "../sql/log.h"
27#include "./my_stacktrace.h"
28#include "./sql_string.h"
29
30/* RocksDB header files */
31#include "rocksdb/slice.h"
32#include "rocksdb/status.h"
33
34#ifdef HAVE_JEMALLOC
35#include <jemalloc/jemalloc.h>
36#endif
37
38namespace myrocks {
39
40/*
41 Guess what?
42 An interface is a class where all members are public by default.
43*/
44
45#ifndef interface
46#define interface struct
47#endif // interface
48
49/*
50 Introduce C-style pseudo-namespaces, a handy way to make code more readble
51 when calling into a legacy API, which does not have any namespace defined.
52 Since we cannot or don't want to change the API in any way, we can use this
53 mechanism to define readability tokens that look like C++ namespaces, but are
54 not enforced in any way by the compiler, since the pre-compiler strips them
55 out. However, on the calling side, code looks like my_core::thd_ha_data()
56 rather than plain a thd_ha_data() call. This technique adds an immediate
57 visible cue on what type of API we are calling into.
58*/
59
60#ifndef my_core
61// C-style pseudo-namespace for MySQL Core API, to be used in decorating calls
62// to non-obvious MySQL functions, like the ones that do not start with well
63// known prefixes: "my_", "sql_", and "mysql_".
64#define my_core
65#endif // my_core
66
67/*
68 The intent behind a SHIP_ASSERT() macro is to have a mechanism for validating
69 invariants in retail builds. Traditionally assertions (such as macros defined
70 in <cassert>) are evaluated for performance reasons only in debug builds and
71 become NOOP in retail builds when NDEBUG is defined.
72
73 This macro is intended to validate the invariants which are critical for
74 making sure that data corruption and data loss won't take place. Proper
75 intended usage can be described as "If a particular condition is not true then
76 stop everything what's going on and terminate the process because continued
77 execution will cause really bad things to happen".
78
79 Use the power of SHIP_ASSERT() wisely.
80*/
81
82#ifndef SHIP_ASSERT
83#define SHIP_ASSERT(expr) \
84 do { \
85 if (!(expr)) { \
86 my_safe_printf_stderr("\nShip assert failure: \'%s\'\n", #expr); \
87 abort(); \
88 } \
89 } while (0)
90#endif // SHIP_ASSERT
91
92/*
93 Assert a implies b.
94 If a is true, then b must be true.
95 If a is false, then the value is b does not matter.
96*/
97#ifndef DBUG_ASSERT_IMP
98#define DBUG_ASSERT_IMP(a, b) DBUG_ASSERT(!(a) || (b))
99#endif
100
101/*
102 Assert a if and only if b.
103 a and b must be both true or both false.
104*/
105#ifndef DBUG_ASSERT_IFF
106#define DBUG_ASSERT_IFF(a, b) \
107 DBUG_ASSERT(static_cast<bool>(a) == static_cast<bool>(b))
108#endif
109
110
111/*
112 Portability: use __PRETTY_FUNCTION__ when available, otherwise use __func__
113 which is in the standard.
114*/
115
116#ifdef __GNUC__
117# define __MYROCKS_PORTABLE_PRETTY_FUNCTION__ __PRETTY_FUNCTION__
118#else
119# define __MYROCKS_PORTABLE_PRETTY_FUNCTION__ __func__
120#endif
121
122/*
123 Intent behind this macro is to avoid manually typing the function name every
124 time we want to add the debugging statement and use the compiler for this
125 work. This avoids typical refactoring problems when one renames a function,
126 but the tracing message doesn't get updated.
127
128 We could use __func__ or __FUNCTION__ macros, but __PRETTY_FUNCTION__
129 contains the signature of the function as well as its bare name and provides
130 therefore more context when interpreting the logs.
131*/
132#define DBUG_ENTER_FUNC() DBUG_ENTER(__MYROCKS_PORTABLE_PRETTY_FUNCTION__)
133
134/*
135 Error handling pattern used across MySQL abides by the following rules: "All
136 functions that can report an error (usually an allocation error), should
137 return 0/FALSE/false on success, 1/TRUE/true on failure."
138
139 https://dev.mysql.com/doc/internals/en/additional-suggestions.html has more
140 details.
141
142 To increase the comprehension and readability of MyRocks codebase we'll use
143 constants similar to ones from C standard (EXIT_SUCCESS and EXIT_FAILURE) to
144 make sure that both failure and success paths are clearly identifiable. The
145 definitions of FALSE and TRUE come from <my_global.h>.
146*/
147#define HA_EXIT_SUCCESS FALSE
148#define HA_EXIT_FAILURE TRUE
149
150/*
151 Macros to better convey the intent behind checking the results from locking
152 and unlocking mutexes.
153*/
154#define RDB_MUTEX_LOCK_CHECK(m) \
155 rdb_check_mutex_call_result(__MYROCKS_PORTABLE_PRETTY_FUNCTION__, true, \
156 mysql_mutex_lock(&m))
157#define RDB_MUTEX_UNLOCK_CHECK(m) \
158 rdb_check_mutex_call_result(__MYROCKS_PORTABLE_PRETTY_FUNCTION__, false, \
159 mysql_mutex_unlock(&m))
160
161/*
162 Generic constant.
163*/
164const size_t RDB_MAX_HEXDUMP_LEN = 1000;
165
166/*
167 Helper function to get an NULL terminated uchar* out of a given MySQL String.
168*/
169
170inline uchar *rdb_mysql_str_to_uchar_str(my_core::String *str) {
171 DBUG_ASSERT(str != nullptr);
172 return reinterpret_cast<uchar *>(str->c_ptr());
173}
174
175/*
176 Helper function to get plain (not necessary NULL terminated) uchar* out of a
177 given STL string.
178*/
179
180inline const uchar *rdb_std_str_to_uchar_ptr(const std::string &str) {
181 return reinterpret_cast<const uchar *>(str.data());
182}
183
184/*
185 Helper function to convert seconds to milliseconds.
186*/
187
188constexpr int rdb_convert_sec_to_ms(int sec) {
189 return std::chrono::milliseconds(std::chrono::seconds(sec)).count();
190}
191
192/*
193 Helper function to get plain (not necessary NULL terminated) uchar* out of a
194 given RocksDB item.
195*/
196
197inline const uchar *rdb_slice_to_uchar_ptr(const rocksdb::Slice *item) {
198 DBUG_ASSERT(item != nullptr);
199 return reinterpret_cast<const uchar *>(item->data());
200}
201
202/*
203 Call this function in cases when you can't rely on garbage collector and need
204 to explicitly purge all unused dirty pages. This should be a relatively rare
205 scenario for cases where it has been verified that this intervention has
206 noticeable benefits.
207*/
208inline int purge_all_jemalloc_arenas() {
209#ifdef HAVE_JEMALLOC
210 unsigned narenas = 0;
211 size_t sz = sizeof(unsigned);
212 char name[25] = {0};
213
214 // Get the number of arenas first. Please see `jemalloc` documentation for
215 // all the various options.
216 int result = mallctl("arenas.narenas", &narenas, &sz, nullptr, 0);
217
218 // `mallctl` returns 0 on success and we really want caller to know if all the
219 // trickery actually works.
220 if (result) {
221 return result;
222 }
223
224 // Form the command to be passed to `mallctl` and purge all the unused dirty
225 // pages.
226 snprintf(name, sizeof(name) / sizeof(char), "arena.%d.purge", narenas);
227 result = mallctl(name, nullptr, nullptr, nullptr, 0);
228
229 return result;
230#else
231 return EXIT_SUCCESS;
232#endif
233}
234
235/*
236 Helper function to check the result of locking or unlocking a mutex. We'll
237 intentionally abort in case of a failure because it's better to terminate
238 the process instead of continuing in an undefined state and corrupting data
239 as a result.
240*/
241inline void rdb_check_mutex_call_result(const char *function_name,
242 const bool attempt_lock,
243 const int result) {
244 if (unlikely(result)) {
245 /* NO_LINT_DEBUG */
246 sql_print_error("%s a mutex inside %s failed with an "
247 "error code %d.",
248 attempt_lock ? "Locking" : "Unlocking", function_name,
249 result);
250
251 // This will hopefully result in a meaningful stack trace which we can use
252 // to efficiently debug the root cause.
253 abort();
254 }
255}
256
257void rdb_log_status_error(const rocksdb::Status &s, const char *msg = nullptr);
258
259// return true if the marker file exists which indicates that the corruption
260// has been detected
261bool rdb_check_rocksdb_corruption();
262
263// stores a marker file in the data directory so that after restart server
264// is still aware that rocksdb data is corrupted
265void rdb_persist_corruption_marker();
266
267/*
268 Helper functions to parse strings.
269*/
270
271const char *rdb_skip_spaces(const struct charset_info_st *const cs,
272 const char *str)
273 MY_ATTRIBUTE((__nonnull__, __warn_unused_result__));
274
275bool rdb_compare_strings_ic(const char *const str1, const char *const str2)
276 MY_ATTRIBUTE((__nonnull__, __warn_unused_result__));
277
278const char *rdb_find_in_string(const char *str, const char *pattern,
279 bool *const succeeded)
280 MY_ATTRIBUTE((__nonnull__, __warn_unused_result__));
281
282const char *rdb_check_next_token(const struct charset_info_st *const cs,
283 const char *str, const char *const pattern,
284 bool *const succeeded)
285 MY_ATTRIBUTE((__nonnull__, __warn_unused_result__));
286
287const char *rdb_parse_id(const struct charset_info_st *const cs,
288 const char *str, std::string *const id)
289 MY_ATTRIBUTE((__nonnull__(1, 2), __warn_unused_result__));
290
291const char *rdb_skip_id(const struct charset_info_st *const cs, const char *str)
292 MY_ATTRIBUTE((__nonnull__, __warn_unused_result__));
293
294const std::vector<std::string> parse_into_tokens(const std::string& s,
295 const char delim);
296
297/*
298 Helper functions to populate strings.
299*/
300
301std::string rdb_hexdump(const char *data, const std::size_t data_len,
302 const std::size_t maxsize = 0)
303 MY_ATTRIBUTE((__nonnull__));
304
305/*
306 Helper function to see if a database exists
307 */
308bool rdb_database_exists(const std::string &db_name);
309
310const char *get_rocksdb_supported_compression_types();
311
312} // namespace myrocks
313