#At file:///C:/source/bzr/mysql-6.0-bug-33414/
2673 Chuck Bell 2008-07-25
BUG#33414 : Backup: DDL hangs indefinitely if ongoing backup
Added ability to set a timeout for blocked DDL operations. The
timeout is the number of seconds a blocked DDL operation will
wait before aborting with an error message. The variable can be
set only as a session variable.
Note: timeout must be set before DDL is executed.
modified:
sql/ddl_blocker.cc
sql/set_var.cc
sql/set_var.h
sql/share/errmsg.txt
sql/sql_class.cc
sql/sql_class.h
sql/sql_parse.cc
per-file messages:
sql/ddl_blocker.cc
BUG#33414 : Backup: DDL hangs indefinitely if ongoing backup
Added ability to timeout based on the variable backup_wait_timeout.
sql/set_var.cc
BUG#33414 : Backup: DDL hangs indefinitely if ongoing backup
Added session variable backup_wait_timeout.
sql/set_var.h
BUG#33414 : Backup: DDL hangs indefinitely if ongoing backup
Added session variable backup_wait_timeout class.
sql/share/errmsg.txt
BUG#33414 : Backup: DDL hangs indefinitely if ongoing backup
Added new error message for DDL timeout.
sql/sql_class.cc
BUG#33414 : Backup: DDL hangs indefinitely if ongoing backup
Added default value setting for backup_wait_timeout.
sql/sql_class.h
BUG#33414 : Backup: DDL hangs indefinitely if ongoing backup
Added thread variable backup_wait_timeout.
sql/sql_parse.cc
BUG#33414 : Backup: DDL hangs indefinitely if ongoing backup
Modified code to check for a failed return which indicates the
ddl blocker has timed out.
=== modified file 'sql/ddl_blocker.cc'
--- a/sql/ddl_blocker.cc 2008-06-25 13:39:04 +0000
+++ b/sql/ddl_blocker.cc 2008-07-25 18:52:07 +0000
@@ -101,29 +101,47 @@ void DDL_blocker_class::end_DDL()
/**
check_DDL_blocker
- Check to see if we are blocked from continuing. If so,
- wait until the blocked process signals the condition.
+ Check to see if we are blocked from continuing. If so, wait until block is
+ removed or the timeout specified by backup_wait_timeout variable occurs.
+
+ @param[in] thd The THD object from the caller.
- @param thd The THD object from the caller.
- @returns TRUE
+ @returns TRUE if not blocked
+ @returns FALSE if ddl is blocked and timeout occurs
*/
my_bool DDL_blocker_class::check_DDL_blocker(THD *thd)
{
+ int ret = 0;
+ struct timespec ddl_timeout;
DBUG_ENTER("check_DDL_blocker()");
DEBUG_SYNC(thd, "before_check_ddl_blocked");
+ set_timespec(ddl_timeout, thd->backup_wait_timeout);
+
/*
Check the ddl blocker condition. Rest until ddl blocker is released.
*/
pthread_mutex_lock(&THR_LOCK_DDL_is_blocked);
thd->enter_cond(&COND_DDL_blocker, &THR_LOCK_DDL_is_blocked,
"DDL blocker: DDL is blocked");
- while (DDL_blocked && !thd->DDL_exception)
- pthread_cond_wait(&COND_DDL_blocker, &THR_LOCK_DDL_is_blocked);
- start_DDL();
- thd->exit_cond("DDL blocker: Ok to run DDL");
- DEBUG_SYNC(thd, "after_start_ddl");
- DBUG_RETURN(TRUE);
+ while (DDL_blocked && !thd->DDL_exception && (ret == 0))
+ {
+ if (thd->backup_wait_timeout == 0)
+ ret = -1;
+ else
+ ret= pthread_cond_timedwait(&COND_DDL_blocker, &THR_LOCK_DDL_is_blocked,
+ &ddl_timeout);
+ }
+ thd->exit_cond("DDL blocker: DDL is not blocked");
+ if (ret == 0)
+ {
+ start_DDL();
+ DEBUG_SYNC(thd, "after_start_ddl");
+ }
+ else
+ my_error(ER_DDL_TIMEOUT, MYF(0), thd->query);
+
+ DBUG_RETURN(ret == 0);
}
/**
=== modified file 'sql/set_var.cc'
--- a/sql/set_var.cc 2008-07-09 07:12:43 +0000
+++ b/sql/set_var.cc 2008-07-25 18:52:07 +0000
@@ -227,6 +227,7 @@ static sys_var_long_ptr sys_concurrent_i
static sys_var_long_ptr sys_connect_timeout(&vars, "connect_timeout",
&connect_timeout);
static sys_var_const_str sys_datadir(&vars, "datadir", mysql_real_data_home);
+static sys_var_backup_wait_timeout sys_backup_wait_timeout(&vars, "backup_wait_timeout");
#ifndef DBUG_OFF
static sys_var_thd_dbug sys_dbug(&vars, "debug");
#endif
@@ -2688,6 +2689,24 @@ uchar *sys_var_last_insert_id::value_ptr
bool sys_var_insert_id::update(THD *thd, set_var *var)
{
thd->force_one_auto_inc_interval(var->save_result.ulonglong_value);
+ return 0;
+}
+
+
+uchar *sys_var_backup_wait_timeout::value_ptr(THD *thd, enum_var_type type,
+ LEX_STRING *base)
+{
+ thd->sys_var_tmp.ulong_value= thd->backup_wait_timeout;
+ return (uchar*) &thd->sys_var_tmp.ulonglong_value;
+}
+
+
+bool sys_var_backup_wait_timeout::update(THD *thd, set_var *var)
+{
+ if (var->save_result.ulong_value > (LONG_MAX/1000))
+ thd->backup_wait_timeout= LONG_MAX/1000;
+ else
+ thd->backup_wait_timeout= var->save_result.ulong_value;
return 0;
}
=== modified file 'sql/set_var.h'
--- a/sql/set_var.h 2008-07-09 07:12:43 +0000
+++ b/sql/set_var.h 2008-07-25 18:52:07 +0000
@@ -655,6 +655,19 @@ public:
};
+class sys_var_backup_wait_timeout :public sys_var
+{
+public:
+ sys_var_backup_wait_timeout(sys_var_chain *chain, const char *name_arg)
+ :sys_var(name_arg)
+ { chain_sys_var(chain); }
+ bool update(THD *thd, set_var *var);
+ bool check_type(enum_var_type type) { return type == OPT_GLOBAL; }
+ SHOW_TYPE show_type() { return SHOW_LONG; }
+ uchar *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base);
+};
+
+
class sys_var_insert_id :public sys_var
{
public:
=== modified file 'sql/share/errmsg.txt'
--- a/sql/share/errmsg.txt 2008-07-09 07:12:43 +0000
+++ b/sql/share/errmsg.txt 2008-07-25 18:52:07 +0000
@@ -6372,3 +6372,7 @@ ER_BACKUP_OBTAIN_NAME_LOCK_FAILED
eng "Restore failed to obtain the name locks on the tables."
ER_BACKUP_RELEASE_NAME_LOCK_FAILED
eng "Restore failed to release the name locks on the tables."
+
+ER_DDL_TIMEOUT
+ eng "The backup wait timeout has expired for query '%-64s'."
+
=== modified file 'sql/sql_class.cc'
--- a/sql/sql_class.cc 2008-07-14 22:06:19 +0000
+++ b/sql/sql_class.cc 2008-07-25 18:52:07 +0000
@@ -536,6 +536,7 @@ THD::THD()
when the DDL blocker is engaged.
*/
DDL_exception(FALSE),
+ backup_wait_timeout(50),
#if defined(ENABLED_DEBUG_SYNC)
debug_sync_control(0),
#endif /* defined(ENABLED_DEBUG_SYNC) */
=== modified file 'sql/sql_class.h'
--- a/sql/sql_class.h 2008-07-14 22:06:19 +0000
+++ b/sql/sql_class.h 2008-07-25 18:52:07 +0000
@@ -1933,6 +1933,7 @@ public:
when the DDL blocker is engaged.
*/
my_bool DDL_exception; // Allow some DDL if there is an exception
+ ulong backup_wait_timeout;
Locked_tables_list locked_tables_list;
=== modified file 'sql/sql_parse.cc'
--- a/sql/sql_parse.cc 2008-07-14 14:56:49 +0000
+++ b/sql/sql_parse.cc 2008-07-25 18:52:07 +0000
@@ -2326,7 +2326,11 @@ mysql_execute_command(THD *thd)
TABLE in the same way. That way we avoid that a new table is
created during a gobal read lock.
*/
- DDL_blocker->check_DDL_blocker(thd);
+ if (!DDL_blocker->check_DDL_blocker(thd))
+ {
+ res= 1;
+ goto end_with_restore_list;
+ }
if (!thd->locked_tables_mode &&
!(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1)))
{
@@ -2467,7 +2471,8 @@ end_with_restore_list:
table without having to do a full rebuild.
*/
{
- DDL_blocker->check_DDL_blocker(thd);
+ if (!DDL_blocker->check_DDL_blocker(thd))
+ goto error;
/* Prepare stack copies to be re-execution safe */
HA_CREATE_INFO create_info;
Alter_info alter_info(lex->alter_info, thd->mem_root);
@@ -2537,7 +2542,8 @@ end_with_restore_list:
case SQLCOM_ALTER_TABLE:
{
- DDL_blocker->check_DDL_blocker(thd);
+ if (!DDL_blocker->check_DDL_blocker(thd))
+ goto error;
ulong priv=0;
ulong priv_needed= ALTER_ACL;
@@ -2652,7 +2658,8 @@ end_with_restore_list:
goto error;
}
- DDL_blocker->check_DDL_blocker(thd);
+ if (!DDL_blocker->check_DDL_blocker(thd))
+ goto error;
if (end_active_trans(thd) || mysql_rename_tables(thd, first_table, 0))
{
DDL_blocker->end_DDL();
@@ -2752,7 +2759,8 @@ end_with_restore_list:
if (check_table_access(thd, SELECT_ACL | INSERT_ACL, all_tables, FALSE, FALSE, UINT_MAX))
goto error; /* purecov: inspected */
thd->enable_slow_log= opt_log_slow_admin_statements;
- DDL_blocker->check_DDL_blocker(thd);
+ if (!DDL_blocker->check_DDL_blocker(thd))
+ goto error;
res= mysql_repair_table(thd, first_table, &lex->check_opt);
DDL_blocker->end_DDL();
/* ! we write after unlocking the table */
@@ -2804,7 +2812,8 @@ end_with_restore_list:
if (check_table_access(thd, SELECT_ACL | INSERT_ACL, all_tables, FALSE, FALSE, UINT_MAX))
goto error; /* purecov: inspected */
thd->enable_slow_log= opt_log_slow_admin_statements;
- DDL_blocker->check_DDL_blocker(thd);
+ if (!DDL_blocker->check_DDL_blocker(thd))
+ goto error;
res= (specialflag & (SPECIAL_SAFE_MODE | SPECIAL_NO_NEW_FUNC)) ?
mysql_recreate_table(thd, first_table) :
mysql_optimize_table(thd, first_table, &lex->check_opt);
@@ -3050,7 +3059,8 @@ end_with_restore_list:
goto error;
}
- DDL_blocker->check_DDL_blocker(thd);
+ if (!DDL_blocker->check_DDL_blocker(thd))
+ goto error;
res= mysql_truncate(thd, first_table, 0);
DDL_blocker->end_DDL();
@@ -3155,7 +3165,8 @@ end_with_restore_list:
/* So that DROP TEMPORARY TABLE gets to binlog at commit/rollback */
thd->options|= OPTION_KEEP_LOG;
}
- DDL_blocker->check_DDL_blocker(thd);
+ if (!DDL_blocker->check_DDL_blocker(thd))
+ goto error;
/* DDL and binlog write order protected by LOCK_open */
res= mysql_rm_table(thd, first_table, lex->drop_if_exists,
lex->drop_temporary);
@@ -3409,7 +3420,8 @@ end_with_restore_list:
if (check_access(thd,CREATE_ACL,lex->name.str, 0, 1, 0,
is_schema_db(lex->name.str)))
break;
- DDL_blocker->check_DDL_blocker(thd);
+ if (!DDL_blocker->check_DDL_blocker(thd))
+ goto error;
res= mysql_create_db(thd,(lower_case_table_names == 2 ? alias :
lex->name.str), &create_info, 0);
DDL_blocker->end_DDL();
@@ -3452,7 +3464,8 @@ end_with_restore_list:
ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
goto error;
}
- DDL_blocker->check_DDL_blocker(thd);
+ if (!DDL_blocker->check_DDL_blocker(thd))
+ goto error;
res= mysql_rm_db(thd, lex->name.str, lex->drop_if_exists, 0);
DDL_blocker->end_DDL();
break;
@@ -3495,7 +3508,8 @@ end_with_restore_list:
goto error;
}
- DDL_blocker->check_DDL_blocker(thd);
+ if (!DDL_blocker->check_DDL_blocker(thd))
+ goto error;
res= mysql_upgrade_db(thd, db);
DDL_blocker->end_DDL();
if (!res)
@@ -3535,7 +3549,8 @@ end_with_restore_list:
ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
goto error;
}
- DDL_blocker->check_DDL_blocker(thd);
+ if (!DDL_blocker->check_DDL_blocker(thd))
+ goto error;
res= mysql_alter_db(thd, db->str, &create_info);
DDL_blocker->end_DDL();
break;