mariadb/mysql-test/suite/innodb/t/lock_isolation.test
Marko Mäkelä ddd7d5d8e3 MDEV-24035 Failing assertion: UT_LIST_GET_LEN(lock.trx_locks) == 0 causing disruption and replication failure
Under unknown circumstances, the SQL layer may wrongly disregard an
invocation of thd_mark_transaction_to_rollback() when an InnoDB
transaction had been aborted (rolled back) due to one of the following errors:
* HA_ERR_LOCK_DEADLOCK
* HA_ERR_RECORD_CHANGED (if innodb_snapshot_isolation=ON)
* HA_ERR_LOCK_WAIT_TIMEOUT (if innodb_rollback_on_timeout=ON)

Such an error used to cause a crash of InnoDB during transaction commit.
These changes aim to catch and report the error earlier, so that not only
this crash can be avoided but also the original root cause be found and
fixed more easily later.

The idea of this fix is from Michael 'Monty' Widenius.

HA_ERR_ROLLBACK: A new error code that will be translated into
ER_ROLLBACK_ONLY, signalling that the current transaction
has been aborted and the only allowed action is ROLLBACK.

trx_t::state: Add TRX_STATE_ABORTED that is like
TRX_STATE_NOT_STARTED, but noting that the transaction had been
rolled back and aborted.

trx_t::is_started(): Replaces trx_is_started().

ha_innobase: Check the transaction state in various places.
Simplify the logic around SAVEPOINT.

ha_innobase::is_valid_trx(): Replaces ha_innobase::is_read_only().

The InnoDB logic around transaction savepoints, commit, and rollback
was unnecessarily complex and might have contributed to this
inconsistency. So, we are simplifying that logic as well.

trx_savept_t: Replace with const undo_no_t*. When we rollback to
a savepoint, all we need to know is the number of undo log records
that must survive.

trx_named_savept_t, DB_NO_SAVEPOINT: Remove. We can store undo_no_t
directly in the space allocated at innobase_hton->savepoint_offset.

fts_trx_create(): Do not copy previous savepoints.

fts_savepoint_rollback(): If a savepoint was not found, roll back
everything after the default savepoint of fts_trx_create().
The test innodb_fts.savepoint is extended to cover this code.

Reviewed by: Vladislav Lesin
Tested by: Matthias Leich
2024-12-12 18:02:00 +02:00

191 lines
4.5 KiB
Text

--source include/have_innodb.inc
--disable_query_log
call mtr.add_suppression("InnoDB: Transaction was aborted due to ");
--enable_query_log
--echo #
--echo # MDEV-26642 Weird SELECT view when a record is
--echo # modified to the same value by two transactions
--echo # MDEV-32898 Phantom rows caused by updates of PRIMARY KEY
--echo #
CREATE TABLE t(a INT PRIMARY KEY, b INT) ENGINE=InnoDB;
INSERT INTO t VALUES (1,1),(2,2);
BEGIN; SELECT * FROM t LOCK IN SHARE MODE;
--connect con_weird,localhost,root
BEGIN;
SELECT * FROM t;
--connect consistent,localhost,root
SET innodb_snapshot_isolation=ON;
BEGIN;
SELECT * FROM t;
--connection default
UPDATE t SET a=3 WHERE b=2;
COMMIT;
--connection consistent
--error ER_CHECKREAD
UPDATE t SET b=3;
SELECT * FROM t;
COMMIT;
--connection con_weird
UPDATE t SET b=3;
SELECT * FROM t;
COMMIT;
--connection default
SELECT * FROM t;
DROP TABLE t;
--echo #
--echo # MDEV-26643 Inconsistent behaviors of UPDATE under
--echo # READ UNCOMMITTED and READ COMMITTED isolation level
--echo #
CREATE TABLE t(a INT, b INT) ENGINE=InnoDB;
INSERT INTO t VALUES(NULL, 1), (2, 2);
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
BEGIN; UPDATE t SET a = 10;
--connection consistent
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
--send UPDATE t SET b = 20 WHERE a
--connection default
let $wait_condition=
select count(*) = 1 from information_schema.processlist
where state = 'Updating'
and info = 'UPDATE t SET b = 20 WHERE a';
--source include/wait_condition.inc
COMMIT;
--connection consistent
--reap
SELECT * FROM t;
--connection default
TRUNCATE TABLE t;
INSERT INTO t VALUES(NULL, 1), (2, 2);
BEGIN; UPDATE t SET a = 10;
--connection consistent
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
--send UPDATE t SET b = 20 WHERE a
--connection default
let $wait_condition=
select count(*) = 1 from information_schema.processlist
where info = 'UPDATE t SET b = 20 WHERE a';
--source include/wait_condition.inc
COMMIT;
--connection consistent
--reap
SELECT * FROM t;
--connection default
TRUNCATE TABLE t;
INSERT INTO t VALUES(NULL, 1), (2, 2);
BEGIN; UPDATE t SET a = 10;
--connection con_weird
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
send UPDATE t SET b = 20 WHERE a;
--connection default
let $wait_condition=
select count(*) = 1 from information_schema.processlist
where state = 'Updating'
and info = 'UPDATE t SET b = 20 WHERE a';
--source include/wait_condition.inc
SELECT * FROM t;
COMMIT;
--connection con_weird
--reap
COMMIT;
--connection default
SELECT * FROM t;
TRUNCATE TABLE t;
--echo #
--echo # MDEV-34108 Inappropriate semi-consistent read in snapshot isolation
--echo #
INSERT INTO t VALUES(NULL, 1), (1, 1);
BEGIN;
UPDATE t SET b = 3;
--connection consistent
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
BEGIN;
# As semi-consistent read is disabled for innodb_snapshot_isolation=ON, the
# following UPDATE must be blocked on the first record.
--send UPDATE t SET b = 2 WHERE a
--connection default
let $wait_condition=
select count(*) = 1 from information_schema.processlist
where state = 'Updating' and info = 'UPDATE t SET b = 2 WHERE a';
--source include/wait_condition.inc
UPDATE t SET a = 1;
COMMIT;
--connection consistent
# If the bug wouldn't be fixed, the result would be (1,3),(1,2), because
# "UPDATE t SET b = 2 WHERE a" would be blocked on the second (1,3) record,
# as semi-consistent read would filter out the first (null,3) record without
# blocking.
--reap
COMMIT;
--connection default
SELECT * FROM t;
DROP TABLE t;
--echo #
--echo # MDEV-33802 Weird read view after ROLLBACK of other transactions
--echo #
CREATE TABLE t(a INT PRIMARY KEY, b INT UNIQUE) ENGINE=InnoDB;
INSERT INTO t SET a=1;
BEGIN; INSERT INTO t SET a=2;
--connection consistent
START TRANSACTION WITH CONSISTENT SNAPSHOT;
SAVEPOINT sp1;
--disable_ps2_protocol
--error ER_CHECKREAD
SELECT * FROM t FORCE INDEX (b) FOR UPDATE;
--enable_ps2_protocol
SAVEPOINT sp1;
--connection con_weird
START TRANSACTION WITH CONSISTENT SNAPSHOT;
send
SELECT * FROM t FORCE INDEX (b) FOR UPDATE;
--connection default
let $wait_condition=
select count(*) = 1 from information_schema.processlist
where state = 'Sending data'
and info LIKE 'SELECT * FROM t %';
--source include/wait_condition.inc
ROLLBACK;
--connection con_weird
--reap
SELECT * FROM t FORCE INDEX (b) FOR UPDATE;
--disconnect con_weird
--connection consistent
SELECT * FROM t FORCE INDEX (b) FOR UPDATE;
--disconnect consistent
--connection default
DROP TABLE t;
--echo # End of 10.6 tests