Commit 31774f0e authored by Vladislav Vaintroub's avatar Vladislav Vaintroub

MDEV-13563 lock DDL for mariabackup in 10.2

Implement lock-ddl-per-table option that locks tables before it
is copied to backup, and helds the lock until backup finished

The "DDL-lock" itself is implemented as "SELECT * from <table> LIMIT 0",
inside a transaction, and "COMMIT" of this transaction is the DDL-unlock.
parent 6b5c0eff
......@@ -117,6 +117,7 @@ xb_mysql_connect()
mysql_options(connection, MYSQL_PLUGIN_DIR, xb_plugin_dir);
}
mysql_options(connection, MYSQL_OPT_PROTOCOL, &opt_protocol);
mysql_options(connection,MYSQL_SET_CHARSET_NAME, "utf8");
msg_ts("Connecting to MySQL server host: %s, user: %s, password: %s, "
"port: %s, socket: %s\n", opt_host ? opt_host : "localhost",
......@@ -1629,3 +1630,96 @@ backup_cleanup()
mysql_close(mysql_connection);
}
}
static pthread_mutex_t mdl_lock_con_mutex;
static MYSQL *mdl_con = NULL;
void
mdl_lock_init()
{
pthread_mutex_init(&mdl_lock_con_mutex, NULL);
mdl_con = xb_mysql_connect();
if (mdl_con)
{
xb_mysql_query(mdl_con, "BEGIN", false, true);
}
}
#ifndef DBUF_OFF
/* Test that table is really locked, if lock_ddl_per_table is set.
The test is executed in DBUG_EXECUTE_IF block inside mdl_lock_table().
*/
static void check_mdl_lock_works(const char *table_name)
{
MYSQL *test_con= xb_mysql_connect();
char *query;
xb_a(asprintf(&query,
"SET STATEMENT max_statement_time=1 FOR ALTER TABLE %s"
" ADD COLUMN mdl_lock_column int", table_name));
int err = mysql_query(test_con, query);
DBUG_ASSERT(err);
int err_no = mysql_errno(test_con);
DBUG_ASSERT(err_no == ER_STATEMENT_TIMEOUT);
mysql_close(test_con);
}
#endif
extern void
dict_fs2utf8(const char*, char*, size_t, char*, size_t);
void
mdl_lock_table(ulint space_id)
{
char *query;
pthread_mutex_lock(&mdl_lock_con_mutex);
xb_a(asprintf(&query,
"SELECT NAME FROM INFORMATION_SCHEMA.INNODB_SYS_TABLES "
"WHERE SPACE = %llu AND NAME LIKE '%%/%%'", (ulonglong)space_id));
xb_mysql_query(mdl_con, query, true, true);
MYSQL_RES *mysql_result = xb_mysql_query(mdl_con, query, true);
MYSQL_ROW row;
while ((row = mysql_fetch_row(mysql_result))) {
char full_table_name[2*FN_REFLEN +2];
char db_utf8[FN_REFLEN];
char table_utf8[FN_REFLEN];
dict_fs2utf8(row[0], db_utf8, sizeof(db_utf8),table_utf8,sizeof(table_utf8));
snprintf(full_table_name,sizeof(full_table_name),"`%s`.`%s`",db_utf8,table_utf8);
char *lock_query;
msg_ts("Locking MDL for %s\n", full_table_name);
xb_a(asprintf(&lock_query,
"SELECT * FROM %s LIMIT 0",
full_table_name));
xb_mysql_query(mdl_con, lock_query, false, false);
free(lock_query);
DBUG_EXECUTE_IF("check_mdl_lock_works",
check_mdl_lock_works(full_table_name););
}
mysql_free_result(mysql_result);
free(query);
pthread_mutex_unlock(&mdl_lock_con_mutex);
}
void
mdl_unlock_all()
{
msg_ts("Unlocking MDL for all tables");
xb_mysql_query(mdl_con, "COMMIT", false, true);
mysql_close(mdl_con);
pthread_mutex_destroy(&mdl_lock_con_mutex);
}
......@@ -296,6 +296,8 @@ my_bool opt_noversioncheck = FALSE;
my_bool opt_no_backup_locks = FALSE;
my_bool opt_decompress = FALSE;
my_bool opt_lock_ddl_per_table = FALSE;
static const char *binlog_info_values[] = {"off", "lockless", "on", "auto",
NullS};
static TYPELIB binlog_info_typelib = {array_elements(binlog_info_values)-1, "",
......@@ -537,7 +539,8 @@ enum options_xtrabackup
OPT_XTRA_TABLES_EXCLUDE,
OPT_XTRA_DATABASES_EXCLUDE,
OPT_PROTOCOL
OPT_PROTOCOL,
OPT_LOCK_DDL_PER_TABLE
};
struct my_option xb_client_options[] =
......@@ -1072,6 +1075,11 @@ struct my_option xb_server_options[] =
(G_PTR*) &xb_open_files_limit, (G_PTR*) &xb_open_files_limit, 0, GET_ULONG,
REQUIRED_ARG, 0, 0, UINT_MAX, 0, 1, 0},
{"lock-ddl-per-table", OPT_LOCK_DDL_PER_TABLE, "Lock DDL for each table "
"before xtrabackup starts to copy it and until the backup is completed.",
(uchar*) &opt_lock_ddl_per_table, (uchar*) &opt_lock_ddl_per_table, 0,
GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
{ 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
};
......@@ -2205,6 +2213,10 @@ xtrabackup_copy_datafile(fil_node_t* node, uint thread_n)
return(FALSE);
}
if (opt_lock_ddl_per_table) {
mdl_lock_table(node->space->id);
}
if (!changed_page_bitmap) {
read_filter = &rf_pass_through;
}
......@@ -3552,6 +3564,10 @@ xtrabackup_backup_func()
"or RENAME TABLE during the backup, inconsistent backup will be "
"produced.\n");
if (opt_lock_ddl_per_table) {
mdl_lock_init();
}
/* initialize components */
if(innodb_init_param()) {
fail:
......@@ -3930,6 +3946,10 @@ xtrabackup_backup_func()
goto fail;
}
if (opt_lock_ddl_per_table) {
mdl_unlock_all();
}
xtrabackup_destroy_datasinks();
msg("xtrabackup: Redo log (from LSN " LSN_PF " to " LSN_PF
......
......@@ -193,4 +193,8 @@ xb_get_one_option(int optid,
const char*
xb_get_copy_action(const char *dflt = "Copying");
void mdl_lock_init();
void mdl_lock_table(ulint space_id);
void mdl_unlock_all();
#endif /* XB_XTRABACKUP_H */
CREATE TABLE t(i INT) ENGINE INNODB;
INSERT INTO t VALUES(1);
# xtrabackup backup
DROP TABLE t;
--source include/have_debug.inc
CREATE TABLE t(i INT) ENGINE INNODB;
INSERT INTO t VALUES(1);
echo # xtrabackup backup;
let $targetdir=$MYSQLTEST_VARDIR/tmp/backup;
--disable_result_log
exec $XTRABACKUP --defaults-file=$MYSQLTEST_VARDIR/my.cnf --backup --target-dir=$targetdir --lock-ddl-per-table=1 --dbug=+d,check_mdl_lock_works;
--enable_result_log
DROP TABLE t;
rmdir $targetdir;
\ No newline at end of file
--innodb --loose-changed_page_bitmaps --innodb-file-format=Barracuda
\ No newline at end of file
--innodb --loose-changed_page_bitmaps --innodb-file-format=Barracuda --innodb-sys-tables
\ No newline at end of file
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment