@@ -58,6 +58,8 @@ Place, Suite 330, Boston, MA 02111-1307 USA
5858#include " backup_mysql.h"
5959#include < btr0btr.h>
6060
61+ #define ROCKSDB_BACKUP_DIR " #rocksdb"
62+
6163/* list of files to sync for --rsync mode */
6264static std::set<std::string> rsync_list;
6365/* locations of tablespaces read from .isl files */
@@ -66,6 +68,21 @@ static std::map<std::string, std::string> tablespace_locations;
6668/* Whether LOCK BINLOG FOR BACKUP has been issued during backup */
6769bool binlog_locked;
6870
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+
6986/* ***********************************************************************
7087Struct represents file or directory. */
7188struct datadir_node_t {
@@ -1138,7 +1155,8 @@ bool
11381155copy_or_move_file (const char *src_file_path,
11391156 const char *dst_file_path,
11401157 const char *dst_dir,
1141- uint thread_n)
1158+ uint thread_n,
1159+ bool copy = xtrabackup_copy_back)
11421160{
11431161ds_ctxt_t *datasink = ds_data;/* copy to datadir by default */
11441162char filedir[FN_REFLEN];
@@ -1186,7 +1204,7 @@ copy_or_move_file(const char *src_file_path,
11861204free (link_filepath);
11871205}
11881206
1189- ret = (xtrabackup_copy_back ?
1207+ ret = (copy ?
11901208copy_file (datasink, src_file_path, dst_file_path, thread_n) :
11911209move_file (datasink, src_file_path, dst_file_path,
11921210 dst_dir, thread_n));
@@ -1371,6 +1389,10 @@ bool backup_start()
13711389return false ;
13721390}
13731391
1392+ if (has_rocksdb_plugin ()) {
1393+ rocksdb_create_checkpoint ();
1394+ }
1395+
13741396// There is no need to stop slave thread before coping non-Innodb data when
13751397// --no-lock option is used because --no-lock option requires that no DDL or
13761398// DML to non-transaction tables can occur.
@@ -1456,6 +1478,10 @@ bool backup_finish()
14561478}
14571479}
14581480
1481+ if (has_rocksdb_plugin ()) {
1482+ rocksdb_backup_checkpoint ();
1483+ }
1484+
14591485msg_ts (" Backup created in directory '%s'\n " , xtrabackup_target_dir);
14601486if (mysql_binlog_position != NULL ) {
14611487msg (" MySQL binlog position: %s\n " , mysql_binlog_position);
@@ -1771,6 +1797,16 @@ copy_back()
17711797int i_tmp;
17721798bool is_ibdata_file;
17731799
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+
17741810/* create empty directories */
17751811if (node.is_empty_dir ) {
17761812char path[FN_REFLEN];
@@ -1855,6 +1891,8 @@ copy_back()
18551891}
18561892}
18571893
1894+ rocksdb_copy_back ();
1895+
18581896cleanup:
18591897if (it != NULL ) {
18601898datadir_iter_free (it);
@@ -2031,3 +2069,234 @@ static bool backup_files_from_datadir(const char *dir_path)
20312069os_file_closedir (dir);
20322070return ret;
20332071}
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