Skip to content

Commit 0331f1f

Browse files
committed
MDEV-36227 Race condition between ALTER TABLE…EXCHANGE PARTITION and SELECT
In commit 6e6a1b3 (MDEV-35000) a race condition was exposed. ha_innobase::check_if_incompatible_data(): If the statistics have already been initialized for the table, skip the invocation of innobase_copy_frm_flags_from_create_info() in order to avoid unexpectedly ruining things for other threads that are concurrently accessing the table. dict_stats_save(): Add debug instrumentation that is necessary for reproducing the interlocking of the failure scenario.
1 parent 6e6a1b3 commit 0331f1f

File tree

4 files changed

+77
-5
lines changed

4 files changed

+77
-5
lines changed

mysql-test/suite/innodb/r/alter_partitioned_debug.result

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
CREATE TABLE t1 (a INT, b VARCHAR(10)) ENGINE=InnoDB
2+
STATS_PERSISTENT=1 STATS_AUTO_RECALC=0
23
PARTITION BY RANGE(a)
34
(PARTITION pa VALUES LESS THAN (3),
45
PARTITION pb VALUES LESS THAN (5));
@@ -19,9 +20,30 @@ connection ddl;
1920
ERROR 23000: Duplicate entry '2-two' for key 'a'
2021
connection default;
2122
DELETE FROM t1;
22-
disconnect ddl;
2323
SET DEBUG_SYNC = 'RESET';
2424
CHECK TABLE t1;
2525
Table Op Msg_type Msg_text
2626
test.t1 check status OK
27-
DROP TABLE t1;
27+
CREATE TABLE t(a INT, b VARCHAR(10)) ENGINE=InnoDB
28+
STATS_PERSISTENT=1 STATS_AUTO_RECALC=1;
29+
RENAME TABLE t TO u;
30+
DELETE FROM mysql.innodb_table_stats WHERE table_name='u';
31+
DELETE FROM mysql.innodb_index_stats WHERE table_name='u';
32+
SET STATEMENT debug_dbug='+d,dict_stats_save_exit_notify_and_wait' FOR
33+
SELECT * FROM u;
34+
connection ddl;
35+
SET DEBUG_SYNC='open_tables_after_open_and_process_table
36+
WAIT_FOR dict_stats_save_finished';
37+
ALTER TABLE t1 EXCHANGE PARTITION pb WITH TABLE u;
38+
connect sync,localhost,root;
39+
SET DEBUG_SYNC='now SIGNAL dict_stats_save_unblock';
40+
disconnect sync;
41+
connection default;
42+
a b
43+
connection ddl;
44+
disconnect ddl;
45+
connection default;
46+
SELECT * FROM u;
47+
a b
48+
SET DEBUG_SYNC = 'RESET';
49+
DROP TABLE t1,u;

mysql-test/suite/innodb/t/alter_partitioned_debug.test

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
--source include/have_debug_sync.inc
55

66
CREATE TABLE t1 (a INT, b VARCHAR(10)) ENGINE=InnoDB
7+
STATS_PERSISTENT=1 STATS_AUTO_RECALC=0
78
PARTITION BY RANGE(a)
89
(PARTITION pa VALUES LESS THAN (3),
910
PARTITION pb VALUES LESS THAN (5));
@@ -26,9 +27,46 @@ reap;
2627

2728
connection default;
2829
DELETE FROM t1;
29-
disconnect ddl;
3030

3131
SET DEBUG_SYNC = 'RESET';
3232

3333
CHECK TABLE t1;
34-
DROP TABLE t1;
34+
35+
CREATE TABLE t(a INT, b VARCHAR(10)) ENGINE=InnoDB
36+
STATS_PERSISTENT=1 STATS_AUTO_RECALC=1;
37+
RENAME TABLE t TO u;
38+
DELETE FROM mysql.innodb_table_stats WHERE table_name='u';
39+
DELETE FROM mysql.innodb_index_stats WHERE table_name='u';
40+
41+
send SET STATEMENT debug_dbug='+d,dict_stats_save_exit_notify_and_wait' FOR
42+
SELECT * FROM u;
43+
44+
connection ddl;
45+
SET DEBUG_SYNC='open_tables_after_open_and_process_table
46+
WAIT_FOR dict_stats_save_finished';
47+
send ALTER TABLE t1 EXCHANGE PARTITION pb WITH TABLE u;
48+
49+
connect sync,localhost,root;
50+
let $wait_condition=
51+
select count(*) = 1 from information_schema.processlist
52+
where state = 'debug sync point: now'
53+
and info like 'SET STATEMENT debug_dbug%SELECT * FROM u';
54+
--source include/wait_condition.inc
55+
let $wait_condition=
56+
select count(*) = 1 from information_schema.processlist
57+
where state = 'Waiting for table metadata lock'
58+
and info like 'ALTER TABLE t1 EXCHANGE PARTITION pb WITH TABLE u';
59+
--source include/wait_condition.inc
60+
SET DEBUG_SYNC='now SIGNAL dict_stats_save_unblock';
61+
disconnect sync;
62+
63+
connection default;
64+
reap;
65+
connection ddl;
66+
reap;
67+
disconnect ddl;
68+
connection default;
69+
SELECT * FROM u;
70+
SET DEBUG_SYNC = 'RESET';
71+
72+
DROP TABLE t1,u;

storage/innobase/dict/dict0stats.cc

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2887,6 +2887,13 @@ dberr_t dict_stats_save(dict_table_t* table, index_id_t index_id)
28872887
STRING_WITH_LEN("now SIGNAL dict_stats_save_finished"));
28882888
});
28892889
);
2890+
DBUG_EXECUTE_IF("dict_stats_save_exit_notify_and_wait",
2891+
SCOPE_EXIT([] {
2892+
debug_sync_set_action(current_thd,
2893+
STRING_WITH_LEN("now SIGNAL dict_stats_save_finished"
2894+
" WAIT_FOR dict_stats_save_unblock"));
2895+
});
2896+
);
28902897
#endif /* ENABLED_DEBUG_SYNC */
28912898

28922899
if (high_level_read_only) {

storage/innobase/handler/ha_innodb.cc

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17424,7 +17424,12 @@ ha_innobase::check_if_incompatible_data(
1742417424
param_new = info->option_struct;
1742517425
param_old = table->s->option_struct;
1742617426

17427-
innobase_copy_frm_flags_from_create_info(m_prebuilt->table, info);
17427+
m_prebuilt->table->stats_mutex_lock();
17428+
if (!m_prebuilt->table->stat_initialized()) {
17429+
innobase_copy_frm_flags_from_create_info(
17430+
m_prebuilt->table, info);
17431+
}
17432+
m_prebuilt->table->stats_mutex_unlock();
1742817433

1742917434
if (table_changes != IS_EQUAL_YES) {
1743017435

0 commit comments

Comments
 (0)