Skip to content

Commit da46a89

Browse files
author
Libing Song
committed
BUG#25379659 MASTER MAY BINLOG A WRONG LAST_COMMITED
DESCRIPTION =========== Transactions could binlog a last_commited smaller than expected. With the wrong last_commited values, the transactions which sould be applied sequentially could be applied parallel. That caused applier errors or data inconsistency. ANALYSIS ======== When committing a transaction, its last_commited is set to the value of max_committed_transaction. max_committed_transaction is the maximum sequence_number of committed transactions. It is maintained just before committing each transaction to engine. If its sequence_number is not SEQ_UNINIT then updates max_committed_transaction accordingly. However, it checked wrong sequence_number(the sequence_number of the leader thread's transaction instead of the sequence_number of the transaction). That caused that max_committed_transaction was only updated in time for leader thread's transaction. The update for following transactions were delay to finish_commit() which was after the transaction commited to engine. FIX === Just checks its own sequence_number of each transaction instead of the sequence_number of the leader thransaction.
1 parent d90e5f1 commit da46a89

File tree

4 files changed

+96
-2
lines changed

4 files changed

+96
-2
lines changed

mysql-test/include/assert_logical_timestamps.inc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ if ($binlog_position)
4545

4646
# Check for match.
4747
--let $assert_select= last_committed=.*sequence_number=.*
48-
--let $assert_match= `SELECT CONCAT('^[^\n]*last_committed=', REPLACE(REPLACE('$logical_timestamps', ';', '.*\n[^\n]*last_committed='), ' ', '\tsequence_number='))`
48+
--let $assert_match= `SELECT CONCAT('last_committed=', REPLACE(REPLACE('$logical_timestamps', ';', '.*\n[^\n]*last_committed='), ' ', '\tsequence_number='))`
4949
--let $assert_text= assert_logical_timestamps: $logical_timestamps
5050
--let $extra_debug_info= Expected logical timestamps: $logical_timestamps
5151
--source include/assert_grep.inc
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
RESET MASTER;
2+
CREATE TABLE t1(c1 INT PRIMARY KEY, c2 INT) ENGINE = InnoDB;
3+
SET DEBUG_SYNC = "bgc_after_enrolling_for_commit_stage
4+
SIGNAL insert1_ready WAIT_FOR continue_commit_stage";
5+
INSERT INTO t1 VALUES(1, 1);
6+
SET DEBUG_SYNC = "now WAIT_FOR insert1_ready";
7+
SET DEBUG_SYNC = "reached_finish_commit WAIT_FOR insert2_finish";
8+
INSERT INTO t1 VALUES(2, 1);
9+
SET DEBUG_SYNC = "now SIGNAL continue_commit_stage";
10+
UPDATE t1 SET c2 = 2 WHERE c1 = 2;
11+
SET DEBUG_SYNC = "now SIGNAL insert2_finish";
12+
include/include/assert_logical_timestamps.inc [3 4]
13+
DROP TABLE t1;
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
################################################################################
2+
# BUG#25379659 MASTER MAY BINLOG A WRONG LAST_COMMITED
3+
#
4+
# Transactions could binlog a last_commited smaller than expected. With the
5+
# wrong last_commited values, the transactions which sould be applied
6+
# sequentially could be applied parallel. That caused applier errors or data
7+
# inconsistency.
8+
#
9+
# When committing a transaction, its last_commited is set to the value of
10+
# max_committed_transaction. max_committed_transaction is the maximum
11+
# sequence_number of committed transactions. It is maintained just before
12+
# committing each transaction to engine. If its sequence_number is not
13+
# SEQ_UNINIT then updates max_committed_transaction accordingly.
14+
#
15+
# However, it checked wrong sequence_number(the sequence_number of the
16+
# leader thread's transaction instead of the sequence_number of the transaction).
17+
# That caused that max_committed_transaction was only updated in time for leader
18+
# thread's transaction. The update for following transactions were delay to
19+
# finish_commit() which was after the transaction commited to engine.
20+
#
21+
# The test verifys last_committed is corret in the bug situation.
22+
#
23+
# Step 1. Use debug sync to gurantee that commit queue has two transactions.
24+
# Step 2. Use debug sync to pause the second transaction when it enters
25+
# finshi_commit()
26+
# Step 3. Execute a transaction and check if its last_committed is correct.
27+
################################################################################
28+
--source include/have_binlog_format_statement.inc
29+
--source include/have_debug_sync.inc
30+
31+
# Reset sequence_number and last_committed, so we can check the exact number
32+
# Make sure it starts from master-bin.000001
33+
RESET MASTER;
34+
35+
CREATE TABLE t1(c1 INT PRIMARY KEY, c2 INT) ENGINE = InnoDB;
36+
37+
# Make the INSERT to wait for another INSERT into the flush queue
38+
SET DEBUG_SYNC = "bgc_after_enrolling_for_commit_stage
39+
SIGNAL insert1_ready WAIT_FOR continue_commit_stage";
40+
--send INSERT INTO t1 VALUES(1, 1)
41+
42+
--connect(conn1, localhost, root)
43+
--connect(conn2, localhost, root)
44+
--connection conn1
45+
46+
# Make sure above INSERT is the leader
47+
SET DEBUG_SYNC = "now WAIT_FOR insert1_ready";
48+
# Record the INSERT's binlog position
49+
--let $binlog_pos= query_get_value(SHOW MASTER STATUS, Position, 1)
50+
51+
# Pause the INSERT when it enters finishi_commit()
52+
SET DEBUG_SYNC = "reached_finish_commit WAIT_FOR insert2_finish";
53+
--send INSERT INTO t1 VALUES(2, 1)
54+
55+
# Wait until above INSERT is binlogged
56+
--connection conn2
57+
--let $show_statement= SHOW MASTER STATUS
58+
--let $field= Position
59+
--let $condition= != '$binlog_pos'
60+
--source include/wait_show_condition.inc
61+
62+
# Signal insert1 to finish the commit group
63+
SET DEBUG_SYNC = "now SIGNAL continue_commit_stage";
64+
--connection default
65+
--reap
66+
67+
UPDATE t1 SET c2 = 2 WHERE c1 = 2;
68+
69+
SET DEBUG_SYNC = "now SIGNAL insert2_finish";
70+
--connection conn1
71+
--reap
72+
73+
--connection default
74+
# INSERT(2,1): sequence_number = 3
75+
# UPDATE: sequence_number = 4, last_committed = 3
76+
77+
--let $binlog_file= master-bin.000001
78+
--let $logical_timestamps= 3 4
79+
--source include/assert_logical_timestamps.inc
80+
81+
DROP TABLE t1;

sql/binlog.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8644,7 +8644,7 @@ MYSQL_BIN_LOG::process_commit_stage_queue(THD *thd, THD *first)
86448644
#ifndef DBUG_OFF
86458645
stage_manager.clear_preempt_status(head);
86468646
#endif
8647-
if (thd->get_transaction()->sequence_number != SEQ_UNINIT)
8647+
if (head->get_transaction()->sequence_number != SEQ_UNINIT)
86488648
update_max_committed(head);
86498649
/*
86508650
Flush/Sync error should be ignored and continue

0 commit comments

Comments
 (0)