mirror of
https://github.com/MariaDB/server.git
synced 2026-01-05 11:05:42 +01:00
The scenario of the bug is the following. Before killing the server some
transaction A starts undo log writing in some undo segment U of rseg R.
It writes its trx_id into the undo log header. Then new trx_id is assigned
to transaction B, but undo log hasn't been started yet. Then transaction
A commits and writes trx_no into its undo log header. Transaction B
starts writing undo log into the undo segment U. So we have the
following undo logs in the undo segments U:
... undo log 1...
... undo log 2...
...
undo log A, trx_id: L, trx_no: M, ...
undo log B, trx_id: N, trx_no: 0, ...
Where L < N < M.
Then server is killed.
On recovery the maximum trx_no is extracted from each rseg, and the
maximum trx_no among all rsegs plus one is considered as a new value
for server-wide transaction id/no counter.
For each undo segment of each rseg we read the last undo log header. If
the last undo log is committed, then we read trx_no from the header,
otherwise we treat trx_id as trx_no. The maximum trx_no from all undo
log segments of the current rseg is treated as the maximum trx_no of the
rseg.
For the above case the undo log of transaction B is not committed and
its trx_no is 0. So we read trx_id and treat it as trx_no. But M < N. If
U is the last modified undo segment in rseg R, and trx_(id/no) N is the
maximum trx_no among all rsegs, then there can be the case when after
recovery some transaction with trx_no_C, such as N < trx_no_C <= M, is
committed.
During a purging we store trx_no of the last parsed undo log of a
committed transaction in purge_sys.tail.trx_no. So if the last parsed
undo log is the undo log of transaction A(transaction B was rolled back
on recovery and its undo log was also removed from the undo segment U),
then purse_sys.tail.trx_no = M. Than if some other transaction C with
trx_no_C <= M is being committed and purged, we hit
"tail.trx_no <= last_trx_no" assertion failure in
purge_sys_t::choose_next_log(), because purge queue is min-heap of
(trx_no, trx_sys.rseg_array index) pairs, where the key is trx_no, and it
must not be that trx_no of the last parsed undo log of a committed
transaction is greater than the last trx_no of the rseg at the top of
the queue.
The fix is to read the trx_no of the previous to last undo log in undo
segment, if the last undo log in that undo segment is not committed, and
set trx_no=max(trx_id of the last undo log, trx_no of the previous to
last undo log) during recovery.
We can do this because we need to extract the maximum
value of trx_no or trx_id of the undo log segment, and the maximum value
is either trx_id of the last undo log or trx_no of the previous to
last undo log, because undo segment can be assigned only to the one
transaction at time, and undo logs in the undo segment are ordered by
trx_id.
Reviewed by Marko Mäkelä.
114 lines
3.9 KiB
Text
114 lines
3.9 KiB
Text
CREATE TABLE t_2_9(a INT primary key) Engine=InnoDB STATS_PERSISTENT=0;
|
|
INSERT INTO t_2_9 SET a = 10;
|
|
CREATE TABLE t_2_8(a INT primary key) Engine=InnoDB STATS_PERSISTENT=0;
|
|
INSERT INTO t_2_8 SET a = 10;
|
|
CREATE TABLE t_2_7(a INT primary key) Engine=InnoDB STATS_PERSISTENT=0;
|
|
INSERT INTO t_2_7 SET a = 10;
|
|
CREATE TABLE t_2_6(a INT primary key) Engine=InnoDB STATS_PERSISTENT=0;
|
|
INSERT INTO t_2_6 SET a = 10;
|
|
CREATE TABLE t_2_5(a INT primary key) Engine=InnoDB STATS_PERSISTENT=0;
|
|
INSERT INTO t_2_5 SET a = 10;
|
|
CREATE TABLE t_2_4(a INT primary key) Engine=InnoDB STATS_PERSISTENT=0;
|
|
INSERT INTO t_2_4 SET a = 10;
|
|
CREATE TABLE t_2_3(a INT primary key) Engine=InnoDB STATS_PERSISTENT=0;
|
|
INSERT INTO t_2_3 SET a = 10;
|
|
CREATE TABLE t_2_2(a INT primary key) Engine=InnoDB STATS_PERSISTENT=0;
|
|
INSERT INTO t_2_2 SET a = 10;
|
|
CREATE TABLE t_2_1(a INT primary key) Engine=InnoDB STATS_PERSISTENT=0;
|
|
INSERT INTO t_2_1 SET a = 10;
|
|
CREATE TABLE t_1_0(a INT primary key) Engine=InnoDB STATS_PERSISTENT=0;
|
|
INSERT INTO t_1_0 SET a = 10;
|
|
CREATE TABLE t_2_0(a INT primary key) Engine=InnoDB STATS_PERSISTENT=0;
|
|
INSERT INTO t_2_0 SET a = 10;
|
|
InnoDB 0 transactions not purged
|
|
connect prevent_purge,localhost,root,,;
|
|
START TRANSACTION WITH CONSISTENT SNAPSHOT;
|
|
SET GLOBAL DEBUG_DBUG ="+d,assign_same_rseg";
|
|
connection default;
|
|
UPDATE t_2_1 SET a=150;
|
|
connect con_1_0, localhost,root,,;
|
|
BEGIN;
|
|
UPDATE t_1_0 SET a=15;
|
|
connect con_2_0, localhost,root,,;
|
|
BEGIN;
|
|
SET DEBUG_SYNC="before_undo_log_trx_id_write SIGNAL before_0 WAIT_FOR cont_0";
|
|
SET DEBUG_SYNC="after_undo_log_trx_id_write SIGNAL after_0";
|
|
UPDATE t_2_0 SET a=15;
|
|
connection con_1_0;
|
|
SET DEBUG_SYNC="now WAIT_FOR before_0";
|
|
connect con_2_1, localhost,root,,;
|
|
BEGIN;
|
|
SET DEBUG_SYNC="before_undo_log_trx_id_write SIGNAL before_1 WAIT_FOR cont_1";
|
|
UPDATE t_2_1 SET a=15;
|
|
connection default;
|
|
SET DEBUG_SYNC="now WAIT_FOR before_1";
|
|
connect con_2_2, localhost,root,,;
|
|
BEGIN;
|
|
SET DEBUG_SYNC="before_undo_log_trx_id_write SIGNAL before_2 WAIT_FOR cont_2";
|
|
UPDATE t_2_2 SET a=15;
|
|
connection default;
|
|
SET DEBUG_SYNC="now WAIT_FOR before_2";
|
|
connect con_2_3, localhost,root,,;
|
|
BEGIN;
|
|
SET DEBUG_SYNC="before_undo_log_trx_id_write SIGNAL before_3 WAIT_FOR cont_3";
|
|
UPDATE t_2_3 SET a=15;
|
|
connection default;
|
|
SET DEBUG_SYNC="now WAIT_FOR before_3";
|
|
connect con_2_4, localhost,root,,;
|
|
BEGIN;
|
|
SET DEBUG_SYNC="before_undo_log_trx_id_write SIGNAL before_4 WAIT_FOR cont_4";
|
|
UPDATE t_2_4 SET a=15;
|
|
connection default;
|
|
SET DEBUG_SYNC="now WAIT_FOR before_4";
|
|
connect con_2_5, localhost,root,,;
|
|
BEGIN;
|
|
SET DEBUG_SYNC="before_undo_log_trx_id_write SIGNAL before_5 WAIT_FOR cont_5";
|
|
UPDATE t_2_5 SET a=15;
|
|
connection default;
|
|
SET DEBUG_SYNC="now WAIT_FOR before_5";
|
|
connect con_2_6, localhost,root,,;
|
|
BEGIN;
|
|
SET DEBUG_SYNC="before_undo_log_trx_id_write SIGNAL before_6 WAIT_FOR cont_6";
|
|
UPDATE t_2_6 SET a=15;
|
|
connection default;
|
|
SET DEBUG_SYNC="now WAIT_FOR before_6";
|
|
connect con_2_7, localhost,root,,;
|
|
BEGIN;
|
|
SET DEBUG_SYNC="before_undo_log_trx_id_write SIGNAL before_7 WAIT_FOR cont_7";
|
|
UPDATE t_2_7 SET a=15;
|
|
connection default;
|
|
SET DEBUG_SYNC="now WAIT_FOR before_7";
|
|
connect con_2_8, localhost,root,,;
|
|
BEGIN;
|
|
SET DEBUG_SYNC="before_undo_log_trx_id_write SIGNAL before_8 WAIT_FOR cont_8";
|
|
UPDATE t_2_8 SET a=15;
|
|
connection default;
|
|
SET DEBUG_SYNC="now WAIT_FOR before_8";
|
|
connect con_2_9, localhost,root,,;
|
|
BEGIN;
|
|
SET DEBUG_SYNC="before_undo_log_trx_id_write SIGNAL before_9 WAIT_FOR cont_9";
|
|
UPDATE t_2_9 SET a=15;
|
|
connection default;
|
|
SET DEBUG_SYNC="now WAIT_FOR before_9";
|
|
connection con_1_0;
|
|
COMMIT;
|
|
SET DEBUG_SYNC="now SIGNAL cont_0";
|
|
SET DEBUG_SYNC="now WAIT_FOR after_0";
|
|
# Kill and restart
|
|
INSERT INTO t_2_1 SET a = 20;
|
|
INSERT INTO t_2_1 SET a = 30;
|
|
INSERT INTO t_2_1 SET a = 40;
|
|
INSERT INTO t_2_1 SET a = 50;
|
|
INSERT INTO t_2_1 SET a = 60;
|
|
SET GLOBAL innodb_max_purge_lag_wait=0;
|
|
DROP TABLE t_2_9;
|
|
DROP TABLE t_2_8;
|
|
DROP TABLE t_2_7;
|
|
DROP TABLE t_2_6;
|
|
DROP TABLE t_2_5;
|
|
DROP TABLE t_2_4;
|
|
DROP TABLE t_2_3;
|
|
DROP TABLE t_2_2;
|
|
DROP TABLE t_2_1;
|
|
DROP TABLE t_1_0;
|
|
DROP TABLE t_2_0;
|