@@ -58,6 +58,8 @@ Place, Suite 330, Boston, MA 02111-1307 USA
58
58
#include " backup_mysql.h"
59
59
#include < btr0btr.h>
60
60
61
+ #define ROCKSDB_BACKUP_DIR " #rocksdb"
62
+
61
63
/* list of files to sync for --rsync mode */
62
64
static std::set<std::string> rsync_list;
63
65
/* locations of tablespaces read from .isl files */
@@ -66,6 +68,21 @@ static std::map<std::string, std::string> tablespace_locations;
66
68
/* Whether LOCK BINLOG FOR BACKUP has been issued during backup */
67
69
bool binlog_locked;
68
70
71
+ static void rocksdb_create_checkpoint ();
72
+ static bool has_rocksdb_plugin ();
73
+ static void copy_or_move_dir (const char *from, const char *to, bool copy, bool allow_hardlinks);
74
+ static void rocksdb_backup_checkpoint ();
75
+ static void rocksdb_copy_back ();
76
+
77
+ static bool is_abs_path (const char *path)
78
+ {
79
+ #ifdef _WIN32
80
+ return path[0 ] && path[1 ] == ' :' && (path[2 ] == ' /' || path[2 ] == ' \\ ' );
81
+ #else
82
+ return path[0 ] == ' /' ;
83
+ #endif
84
+ }
85
+
69
86
/* ***********************************************************************
70
87
Struct represents file or directory. */
71
88
struct datadir_node_t {
@@ -1138,7 +1155,8 @@ bool
1138
1155
copy_or_move_file (const char *src_file_path,
1139
1156
const char *dst_file_path,
1140
1157
const char *dst_dir,
1141
- uint thread_n)
1158
+ uint thread_n,
1159
+ bool copy = xtrabackup_copy_back)
1142
1160
{
1143
1161
ds_ctxt_t *datasink = ds_data; /* copy to datadir by default */
1144
1162
char filedir[FN_REFLEN];
@@ -1186,7 +1204,7 @@ copy_or_move_file(const char *src_file_path,
1186
1204
free (link_filepath);
1187
1205
}
1188
1206
1189
- ret = (xtrabackup_copy_back ?
1207
+ ret = (copy ?
1190
1208
copy_file (datasink, src_file_path, dst_file_path, thread_n) :
1191
1209
move_file (datasink, src_file_path, dst_file_path,
1192
1210
dst_dir, thread_n));
@@ -1371,6 +1389,10 @@ bool backup_start()
1371
1389
return false ;
1372
1390
}
1373
1391
1392
+ if (has_rocksdb_plugin ()) {
1393
+ rocksdb_create_checkpoint ();
1394
+ }
1395
+
1374
1396
// There is no need to stop slave thread before coping non-Innodb data when
1375
1397
// --no-lock option is used because --no-lock option requires that no DDL or
1376
1398
// DML to non-transaction tables can occur.
@@ -1456,6 +1478,10 @@ bool backup_finish()
1456
1478
}
1457
1479
}
1458
1480
1481
+ if (has_rocksdb_plugin ()) {
1482
+ rocksdb_backup_checkpoint ();
1483
+ }
1484
+
1459
1485
msg_ts (" Backup created in directory '%s'\n " , xtrabackup_target_dir);
1460
1486
if (mysql_binlog_position != NULL ) {
1461
1487
msg (" MySQL binlog position: %s\n " , mysql_binlog_position);
@@ -1771,6 +1797,16 @@ copy_back()
1771
1797
int i_tmp;
1772
1798
bool is_ibdata_file;
1773
1799
1800
+ if (strstr (node.filepath ," /" ROCKSDB_BACKUP_DIR " /" )
1801
+ #ifdef _WIN32
1802
+ || strstr (node.filepath ," \\ " ROCKSDB_BACKUP_DIR " \\ " )
1803
+ #endif
1804
+ )
1805
+ {
1806
+ // copied at later step
1807
+ continue ;
1808
+ }
1809
+
1774
1810
/* create empty directories */
1775
1811
if (node.is_empty_dir ) {
1776
1812
char path[FN_REFLEN];
@@ -1855,6 +1891,8 @@ copy_back()
1855
1891
}
1856
1892
}
1857
1893
1894
+ rocksdb_copy_back ();
1895
+
1858
1896
cleanup:
1859
1897
if (it != NULL ) {
1860
1898
datadir_iter_free (it);
@@ -2031,3 +2069,234 @@ static bool backup_files_from_datadir(const char *dir_path)
2031
2069
os_file_closedir (dir);
2032
2070
return ret;
2033
2071
}
2072
+
2073
+
2074
+ static int rocksdb_remove_checkpoint_directory ()
2075
+ {
2076
+ xb_mysql_query (mysql_connection, " set global rocksdb_remove_mariabackup_checkpoint=ON" , false );
2077
+ return 0 ;
2078
+ }
2079
+
2080
+ static bool has_rocksdb_plugin ()
2081
+ {
2082
+ static bool first_time = true ;
2083
+ static bool has_plugin= false ;
2084
+ if (!first_time || !xb_backup_rocksdb)
2085
+ return has_plugin;
2086
+
2087
+ const char *query = " SELECT COUNT(*) FROM information_schema.plugins WHERE plugin_name='rocksdb'" ;
2088
+ MYSQL_RES* result = xb_mysql_query (mysql_connection, query, true );
2089
+ MYSQL_ROW row = mysql_fetch_row (result);
2090
+ if (row)
2091
+ has_plugin = !strcmp (row[0 ], " 1" );
2092
+ mysql_free_result (result);
2093
+ first_time = false ;
2094
+ return has_plugin;
2095
+ }
2096
+
2097
+ static char *trim_trailing_dir_sep (char *path)
2098
+ {
2099
+ size_t path_len = strlen (path);
2100
+ while (path_len)
2101
+ {
2102
+ char c = path[path_len - 1 ];
2103
+ if (c == ' /' IF_WIN (|| c == ' \\ ' , ))
2104
+ path_len--;
2105
+ else
2106
+ break ;
2107
+ }
2108
+ path[path_len] = 0 ;
2109
+ return path;
2110
+ }
2111
+
2112
+ /*
2113
+ Create a file hardlink.
2114
+ @return true on success, false on error.
2115
+ */
2116
+ static bool make_hardlink (const char *from_path, const char *to_path)
2117
+ {
2118
+ DBUG_EXECUTE_IF (" no_hardlinks" , return false ;);
2119
+ char to_path_full[FN_REFLEN];
2120
+ if (!is_abs_path (to_path))
2121
+ {
2122
+ fn_format (to_path_full, to_path, ds_data->root , " " , MYF (MY_RELATIVE_PATH));
2123
+ }
2124
+ else
2125
+ {
2126
+ strncpy (to_path_full, to_path, sizeof (to_path_full));
2127
+ }
2128
+ #ifdef _WIN32
2129
+ return CreateHardLink (to_path_full, from_path, NULL );
2130
+ #else
2131
+ return !link (from_path, to_path_full);
2132
+ #endif
2133
+ }
2134
+
2135
+ /*
2136
+ Copies or moves a directory (non-recursively so far).
2137
+ Helper function used to backup rocksdb checkpoint, or copy-back the
2138
+ rocksdb files.
2139
+
2140
+ Has optimization that allows to use hardlinks when possible
2141
+ (source and destination are directories on the same device)
2142
+ */
2143
+ static void copy_or_move_dir (const char *from, const char *to, bool do_copy, bool allow_hardlinks)
2144
+ {
2145
+ datadir_node_t node;
2146
+ datadir_node_init (&node);
2147
+ datadir_iter_t *it = datadir_iter_new (from, false );
2148
+
2149
+ while (datadir_iter_next (it, &node))
2150
+ {
2151
+ char to_path[FN_REFLEN];
2152
+ const char *from_path = node.filepath ;
2153
+ snprintf (to_path, sizeof (to_path), " %s/%s" , to, base_name (from_path));
2154
+ bool rc = false ;
2155
+ if (do_copy && allow_hardlinks)
2156
+ {
2157
+ rc = make_hardlink (from_path, to_path);
2158
+ if (rc)
2159
+ {
2160
+ msg_ts (" [%02u] Creating hardlink from %s to %s\n " ,
2161
+ 1 , from_path, to_path);
2162
+ }
2163
+ else
2164
+ {
2165
+ allow_hardlinks = false ;
2166
+ }
2167
+ }
2168
+
2169
+ if (!rc)
2170
+ {
2171
+ rc = (do_copy ?
2172
+ copy_file (ds_data, from_path, to_path, 1 ) :
2173
+ move_file (ds_data, from_path, node.filepath_rel ,
2174
+ to, 1 ));
2175
+ }
2176
+ if (!rc)
2177
+ exit (EXIT_FAILURE);
2178
+ }
2179
+ datadir_iter_free (it);
2180
+ datadir_node_free (&node);
2181
+
2182
+ }
2183
+
2184
+ /*
2185
+ Obtain user level lock , to protect the checkpoint directory of the server
2186
+ from being user/overwritten by different backup processes, if backups are
2187
+ running in parallel.
2188
+
2189
+ This lock will be acquired before rocksdb checkpoint is created, held
2190
+ while all files from it are being copied to their final backup destination,
2191
+ and finally released after the checkpoint is removed.
2192
+ */
2193
+ static void rocksdb_lock_checkpoint ()
2194
+ {
2195
+ msg_ts (" Obtaining rocksdb checkpoint lock.\n " );
2196
+ MYSQL_RES *res =
2197
+ xb_mysql_query (mysql_connection, " SELECT GET_LOCK('mariabackup_rocksdb_checkpoint',3600)" , true , true );
2198
+
2199
+ MYSQL_ROW r = mysql_fetch_row (res);
2200
+ if (r && r[0 ] && strcmp (r[0 ], " 1" ))
2201
+ {
2202
+ msg_ts (" Could not obtain rocksdb checkpont lock\n " );
2203
+ exit (EXIT_FAILURE);
2204
+ }
2205
+ }
2206
+
2207
+ static void rocksdb_unlock_checkpoint ()
2208
+ {
2209
+ xb_mysql_query (mysql_connection,
2210
+ " SELECT RELEASE_LOCK('mariabackup_rocksdb_checkpoint')" , false , true );
2211
+ }
2212
+
2213
+
2214
+ /*
2215
+ Create temporary checkpoint in $rocksdb_datadir/mariabackup-checkpoint
2216
+ directory.
2217
+ A (user-level) lock named 'mariabackup_rocksdb_checkpoint' will also be
2218
+ acquired be this function.
2219
+ */
2220
+ #define MARIADB_CHECKPOINT_DIR " mariabackup-checkpoint"
2221
+ static char rocksdb_checkpoint_dir[FN_REFLEN];
2222
+
2223
+ static void rocksdb_create_checkpoint ()
2224
+ {
2225
+ MYSQL_RES *result = xb_mysql_query (mysql_connection, " SELECT @@rocksdb_datadir,@@datadir" , true , true );
2226
+ MYSQL_ROW row = mysql_fetch_row (result);
2227
+
2228
+ DBUG_ASSERT (row && row[0 ] && row[1 ]);
2229
+
2230
+ char *rocksdbdir = row[0 ];
2231
+ char *datadir = row[1 ];
2232
+
2233
+ if (is_abs_path (rocksdbdir))
2234
+ {
2235
+ snprintf (rocksdb_checkpoint_dir, sizeof (rocksdb_checkpoint_dir),
2236
+ " %s/" MARIADB_CHECKPOINT_DIR, trim_trailing_dir_sep (rocksdbdir));
2237
+ }
2238
+ else
2239
+ {
2240
+ snprintf (rocksdb_checkpoint_dir, sizeof (rocksdb_checkpoint_dir),
2241
+ " %s/%s/" MARIADB_CHECKPOINT_DIR, trim_trailing_dir_sep (datadir),
2242
+ trim_dotslash (rocksdbdir));
2243
+ }
2244
+ mysql_free_result (result);
2245
+
2246
+ #ifdef _WIN32
2247
+ for (char *p = rocksdb_checkpoint_dir; *p; p++)
2248
+ if (*p == ' \\ ' ) *p = ' /' ;
2249
+ #endif
2250
+
2251
+ rocksdb_lock_checkpoint ();
2252
+
2253
+ if (!access (rocksdb_checkpoint_dir, 0 ))
2254
+ {
2255
+ msg_ts (" Removing rocksdb checkpoint from previous backup attempt.\n " );
2256
+ rocksdb_remove_checkpoint_directory ();
2257
+ }
2258
+
2259
+ char query[FN_REFLEN + 32 ];
2260
+ snprintf (query, sizeof (query), " SET GLOBAL rocksdb_create_checkpoint='%s'" , rocksdb_checkpoint_dir);
2261
+ xb_mysql_query (mysql_connection, query, false , true );
2262
+ }
2263
+
2264
+ /*
2265
+ Copy files from rocksdb temporary checkpoint to final destination.
2266
+ remove temp.checkpoint directory (in server's datadir)
2267
+ and release user level lock acquired inside rocksdb_create_checkpoint().
2268
+ */
2269
+ static void rocksdb_backup_checkpoint ()
2270
+ {
2271
+ msg_ts (" Backing up rocksdb files.\n " );
2272
+ char rocksdb_backup_dir[FN_REFLEN];
2273
+ snprintf (rocksdb_backup_dir, sizeof (rocksdb_backup_dir), " %s/" ROCKSDB_BACKUP_DIR , xtrabackup_target_dir);
2274
+ bool backup_to_directory = xtrabackup_backup && xtrabackup_stream_fmt == XB_STREAM_FMT_NONE;
2275
+ if (backup_to_directory)
2276
+ {
2277
+ if (my_mkdir (rocksdb_backup_dir, 0777 , MYF (0 ))){
2278
+ msg_ts (" Can't create rocksdb backup directory %s\n " , rocksdb_backup_dir);
2279
+ exit (EXIT_FAILURE);
2280
+ }
2281
+ }
2282
+ copy_or_move_dir (rocksdb_checkpoint_dir, ROCKSDB_BACKUP_DIR, true , backup_to_directory);
2283
+ rocksdb_remove_checkpoint_directory ();
2284
+ rocksdb_unlock_checkpoint ();
2285
+ }
2286
+
2287
+ /*
2288
+ Copies #rocksdb directory to the $rockdb_data_dir, on copy-back
2289
+ */
2290
+ static void rocksdb_copy_back () {
2291
+ if (access (ROCKSDB_BACKUP_DIR, 0 ))
2292
+ return ;
2293
+ char rocksdb_home_dir[FN_REFLEN];
2294
+ if (xb_rocksdb_datadir && is_abs_path (xb_rocksdb_datadir)) {
2295
+ strncpy (rocksdb_home_dir, xb_rocksdb_datadir, sizeof (rocksdb_home_dir));
2296
+ } else {
2297
+ snprintf (rocksdb_home_dir, sizeof (rocksdb_home_dir), " %s/%s" , mysql_data_home,
2298
+ xb_rocksdb_datadir?trim_dotslash (xb_rocksdb_datadir): ROCKSDB_BACKUP_DIR);
2299
+ }
2300
+ mkdirp (rocksdb_home_dir, 0777 , MYF (0 ));
2301
+ copy_or_move_dir (ROCKSDB_BACKUP_DIR, rocksdb_home_dir, xtrabackup_copy_back, xtrabackup_copy_back);
2302
+ }
0 commit comments