MDEV-32614 LeakSanitizer errors in copy_data_between_tables
The memory leak occurs on error when backup_reset_alter_copy_lock fails with timeout. This leads to the alter rollback, but flush_unused is not called. Move table flushing on error handling to a single place and mind more possible failures this time.
This commit is contained in:
parent
c2e16b3ad5
commit
50095046f3
@ -1807,6 +1807,28 @@ a b
|
|||||||
456 NULL
|
456 NULL
|
||||||
789 NULL
|
789 NULL
|
||||||
drop table t1;
|
drop table t1;
|
||||||
|
# MDEV-32614 LeakSanitizer errors in copy_data_between_tables
|
||||||
|
create table t (a int, b int) engine=aria;
|
||||||
|
insert into t select seq, seq from seq_1_to_5;
|
||||||
|
backup stage start;
|
||||||
|
connect con_lock,localhost,root,,;
|
||||||
|
set lock_wait_timeout= 1;
|
||||||
|
set debug_sync='copy_data_between_tables_before_reset_backup_lock wait_for continue';
|
||||||
|
alter table t add index (b), algorithm=copy, lock=none;
|
||||||
|
connection default;
|
||||||
|
backup stage block_commit;
|
||||||
|
set debug_sync='now signal continue';
|
||||||
|
connection con_lock;
|
||||||
|
ERROR HY000: Lock wait timeout exceeded; try restarting transaction
|
||||||
|
set debug_sync='copy_data_between_tables_before_reset_backup_lock wait_for continue';
|
||||||
|
alter table t add index (a), algorithm=copy, lock=none;
|
||||||
|
connection default;
|
||||||
|
backup stage end;
|
||||||
|
set debug_sync='now signal continue';
|
||||||
|
connection con_lock;
|
||||||
|
disconnect con_lock;
|
||||||
|
connection default;
|
||||||
|
drop table t;
|
||||||
set global default_storage_engine= MyISAM;
|
set global default_storage_engine= MyISAM;
|
||||||
disconnect con1;
|
disconnect con1;
|
||||||
disconnect con2;
|
disconnect con2;
|
||||||
|
@ -2066,6 +2066,41 @@ select * from t1;
|
|||||||
drop table t1;
|
drop table t1;
|
||||||
|
|
||||||
|
|
||||||
|
--echo # MDEV-32614 LeakSanitizer errors in copy_data_between_tables
|
||||||
|
create table t (a int, b int) engine=aria;
|
||||||
|
insert into t select seq, seq from seq_1_to_5;
|
||||||
|
backup stage start;
|
||||||
|
|
||||||
|
--connect (con_lock,localhost,root,,)
|
||||||
|
set lock_wait_timeout= 1;
|
||||||
|
|
||||||
|
set debug_sync='copy_data_between_tables_before_reset_backup_lock wait_for continue';
|
||||||
|
send alter table t add index (b), algorithm=copy, lock=none;
|
||||||
|
|
||||||
|
--connection default
|
||||||
|
backup stage block_commit;
|
||||||
|
|
||||||
|
set debug_sync='now signal continue';
|
||||||
|
|
||||||
|
--connection con_lock
|
||||||
|
--error ER_LOCK_WAIT_TIMEOUT
|
||||||
|
--reap
|
||||||
|
# --echo # error $mysql_errno
|
||||||
|
|
||||||
|
set debug_sync='copy_data_between_tables_before_reset_backup_lock wait_for continue';
|
||||||
|
send alter table t add index (a), algorithm=copy, lock=none;
|
||||||
|
|
||||||
|
--connection default
|
||||||
|
backup stage end;
|
||||||
|
set debug_sync='now signal continue';
|
||||||
|
|
||||||
|
--connection con_lock
|
||||||
|
--reap
|
||||||
|
--disconnect con_lock
|
||||||
|
--connection default
|
||||||
|
drop table t;
|
||||||
|
|
||||||
|
|
||||||
eval set global default_storage_engine= $default_storage_engine;
|
eval set global default_storage_engine= $default_storage_engine;
|
||||||
|
|
||||||
--disconnect con1
|
--disconnect con1
|
||||||
|
@ -12130,6 +12130,7 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to,
|
|||||||
#ifdef HAVE_REPLICATION
|
#ifdef HAVE_REPLICATION
|
||||||
if (online)
|
if (online)
|
||||||
{
|
{
|
||||||
|
DBUG_ASSERT(from->s->online_alter_binlog == NULL);
|
||||||
from->s->online_alter_binlog= new Cache_flip_event_log();
|
from->s->online_alter_binlog= new Cache_flip_event_log();
|
||||||
if (!from->s->online_alter_binlog)
|
if (!from->s->online_alter_binlog)
|
||||||
goto err;
|
goto err;
|
||||||
@ -12363,29 +12364,10 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to,
|
|||||||
thd_progress_next_stage(thd);
|
thd_progress_next_stage(thd);
|
||||||
error= online_alter_read_from_binlog(thd, &rgi, binlog, &found_count);
|
error= online_alter_read_from_binlog(thd, &rgi, binlog, &found_count);
|
||||||
}
|
}
|
||||||
if (error)
|
|
||||||
from->s->tdc->flush_unused(1); // to free the binlog
|
|
||||||
to->pos_in_table_list= NULL; // Safety
|
to->pos_in_table_list= NULL; // Safety
|
||||||
DBUG_ASSERT(thd->lex->sql_command == saved_sql_command);
|
DBUG_ASSERT(thd->lex->sql_command == saved_sql_command);
|
||||||
thd->lex->sql_command= saved_sql_command; // Just in case
|
thd->lex->sql_command= saved_sql_command; // Just in case
|
||||||
}
|
}
|
||||||
else if (online) // error was on copy stage
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
We can't free the resources properly now, as we can still be in
|
|
||||||
non-exclusive state. So this s->online_alter_binlog will be used
|
|
||||||
until all transactions will release it.
|
|
||||||
Once the transaction commits, it can release online_alter_binlog
|
|
||||||
by decreasing ref_count.
|
|
||||||
|
|
||||||
online_alter_binlog->ref_count can be reached 0 only once.
|
|
||||||
Proof:
|
|
||||||
If share exists, we'll always have ref_count >= 1.
|
|
||||||
Once it reaches destroy(), nobody can acquire it again,
|
|
||||||
therefore, only release() is possible at this moment.
|
|
||||||
*/
|
|
||||||
from->s->tdc->flush_unused(1); // to free the binlog
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (error > 0 && !from->s->tmp_table)
|
if (error > 0 && !from->s->tmp_table)
|
||||||
@ -12400,6 +12382,26 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to,
|
|||||||
|
|
||||||
if (unlikely(mysql_trans_commit_alter_copy_data(thd)))
|
if (unlikely(mysql_trans_commit_alter_copy_data(thd)))
|
||||||
error= 1;
|
error= 1;
|
||||||
|
|
||||||
|
if (unlikely(error) && online)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
We can't free the resources properly now, as we can still be in
|
||||||
|
non-exclusive state. So this s->online_alter_binlog will be used
|
||||||
|
until all transactions will release it.
|
||||||
|
Once the transaction commits, it can release online_alter_binlog
|
||||||
|
by decreasing ref_count.
|
||||||
|
|
||||||
|
online_alter_binlog->ref_count can be reached 0 only once.
|
||||||
|
Proof:
|
||||||
|
If share exists, we'll always have ref_count >= 1.
|
||||||
|
Once it reaches destroy(), nobody can acquire it again,
|
||||||
|
therefore, only release() is possible at this moment.
|
||||||
|
|
||||||
|
Also, this will release the binlog.
|
||||||
|
*/
|
||||||
|
from->s->tdc->flush_unused(1);
|
||||||
|
}
|
||||||
|
|
||||||
err:
|
err:
|
||||||
if (bulk_insert_started)
|
if (bulk_insert_started)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user