1
0
mirror of https://github.com/MariaDB/server.git synced 2026-10-23 18:49:29 +03:00

MDEV-39056 : sporadic test failures with galera.galera_toi_ddl_nonconflicting

The test sent

  ALTER TABLE t1 ADD COLUMN f3 INTEGER; INSERT INTO t1 VALUES (NULL, 10000, 10000);

from node_2 as a single --send. Nothing ordered that INSERT against the
CREATE UNIQUE INDEX issued from node_1, so the second DDL could reach node_2
while the INSERT was still executing there and BF-abort it. The automatic
autocommit retry then replicated with last_seen_seqno behind the seqno of the
DDL and failed certification against its exclusive key on test.t1. As
wsrep_retry_autocommit defaults to 1 there was no second retry and the client
got ER_LOCK_DEADLOCK.

Send only the ALTER TABLE and execute the INSERT after both DDL statements
have completed on both nodes.

Waiting for the second DDL by polling INFORMATION_SCHEMA.PROCESSLIST did not
prove that it had been ordered either: wsrep_TOI_begin() sets the
"Waiting to execute in isolation" stage before it replicates the writeset, so
that state can be observed before the DDL has reached total order. Wait on
node_2 for the new index to appear instead, which happens only once the DDL
has been ordered behind the parked ALTER TABLE, and record on node_1 that
neither DDL has been applied there while the first one is still parked.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Jan Lindström
2026-09-10 07:34:36 +03:00
committed by Jan Lindström
parent 75fe7a5118
commit 7ba0776692
2 changed files with 47 additions and 13 deletions

View File

@@ -13,7 +13,7 @@ SET SESSION wsrep_sync_wait=0;
SET GLOBAL wsrep_provider_options = 'dbug=d,apply_monitor_slave_enter_sync';
connection node_2;
# DDL 1
ALTER TABLE t1 ADD COLUMN f3 INTEGER; INSERT INTO t1 VALUES (NULL, 10000, 10000);;
ALTER TABLE t1 ADD COLUMN f3 INTEGER;
connection node_1a;
SET SESSION wsrep_on = 0;
SET SESSION wsrep_on = 1;
@@ -21,14 +21,22 @@ SET GLOBAL wsrep_provider_options = 'dbug=';
# This will block on acquiring total order isolation
connection node_1;
# DDL 2
CREATE UNIQUE INDEX i1 ON t1(f2);;
CREATE UNIQUE INDEX i1 ON t1(f2);
connection node_2a;
# Neither DDL has been applied on node_1 yet
connection node_1a;
SELECT COUNT(*) AS EXPECT_2 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 't1';
EXPECT_2
2
SELECT COUNT(*) AS EXPECT_1 FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_NAME = 't1';
EXPECT_1
1
# Signal DDL 1
SET GLOBAL wsrep_provider_options = 'dbug=';
SET GLOBAL wsrep_provider_options = 'signal=apply_monitor_slave_enter_sync';
connection node_2;
connection node_1;
connection node_2;
INSERT INTO t1 VALUES (NULL, 10000, 10000);
SELECT COUNT(*) AS EXPECT_3 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 't1';
EXPECT_3
3

View File

@@ -28,32 +28,49 @@ SET SESSION wsrep_sync_wait=0;
--let $galera_sync_point = apply_monitor_slave_enter_sync
--source include/galera_set_sync_point.inc
#
# DDL 1 is a TOI operation, it completes on node_2 as soon as it has been
# replicated, it does not wait for node_1. On node_1 its writeset is parked
# in the apply monitor by the sync point above, so node_1 cannot advance
# past it.
#
--connection node_2
--echo # DDL 1
--send ALTER TABLE t1 ADD COLUMN f3 INTEGER; INSERT INTO t1 VALUES (NULL, 10000, 10000);
--send ALTER TABLE t1 ADD COLUMN f3 INTEGER
--connection node_1a
--source include/galera_wait_sync_point.inc
--source include/galera_clear_sync_point.inc
#
# DDL 2 is issued on node_1 while DDL 1 is still parked there. It is therefore
# assigned a later seqno and has to wait for DDL 1 to be applied before it can
# acquire total order isolation.
#
--echo # This will block on acquiring total order isolation
--connection node_1
--echo # DDL 2
--send CREATE UNIQUE INDEX i1 ON t1(f2);
--send CREATE UNIQUE INDEX i1 ON t1(f2)
--connection node_1a
#
# "acquiring total order isolation" is not listed here: wsrep_TOI_begin()
# sets it with thd_proc_info() and THD_STAGE_INFO() overwrites it two lines
# later, so it never reaches the process list. "Waiting for TOI DDL" is the
# stage restored when a statement that already holds total order isolation
# leaves one of the one second metadata lock wait slices.
# Wait until DDL 2 has been replicated and applied on node_2. That proves that
# DDL 2 has reached total order after DDL 1 and is now waiting on node_1.
#
--let $wait_condition = SELECT COUNT(*) = 1 FROM INFORMATION_SCHEMA.PROCESSLIST WHERE STATE LIKE 'Waiting for table metadata lock%' OR STATE LIKE 'Waiting for TOI DDL%' OR STATE LIKE 'Waiting to execute in isolation%';
# Polling INFORMATION_SCHEMA.PROCESSLIST on node_1 for the state of the DDL 2
# thread is not a reliable substitute: wsrep_TOI_begin() sets the
# "Waiting to execute in isolation" stage before it replicates the writeset,
# so that state can be observed before DDL 2 has been ordered at all.
#
--connection node_2a
--let $wait_condition = SELECT COUNT(*) = 2 FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_NAME = 't1';
--source include/wait_condition.inc
--echo # Neither DDL has been applied on node_1 yet
--connection node_1a
SELECT COUNT(*) AS EXPECT_2 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 't1';
SELECT COUNT(*) AS EXPECT_1 FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_NAME = 't1';
--echo # Signal DDL 1
--source include/galera_clear_sync_point.inc
--let $galera_sync_point = apply_monitor_slave_enter_sync
--source include/galera_signal_sync_point.inc
@@ -63,7 +80,16 @@ SET SESSION wsrep_sync_wait=0;
--connection node_1
--reap
#
# The DML is executed only after both DDLs are done on both nodes. When it was
# sent together with DDL 1 in the same --send, its writeset raced with DDL 2:
# the DDL 2 TOI could reach node_2 while the INSERT was still executing there
# and BF-abort it, and the automatic autocommit retry then failed certification
# against the DDL 2 writeset, so the client got ER_LOCK_DEADLOCK.
#
--connection node_2
INSERT INTO t1 VALUES (NULL, 10000, 10000);
--let $wait_condition = SELECT COUNT(*) = 3 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 't1';
--source include/wait_condition.inc
--let $wait_condition = SELECT COUNT(*) = 2 FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_NAME = 't1';