Bug #50413 | insert on duplicate key update sometimes writes binlog position incorrectly | ||
---|---|---|---|
Submitted: | 18 Jan 2010 13:54 | Modified: | 12 Nov 2013 12:50 |
Reporter: | justin dahlke | Email Updates: | |
Status: | Closed | Impact on me: | |
Category: | MySQL Server: InnoDB storage engine | Severity: | S1 (Critical) |
Version: | 5.0.91,5.1.50,5.6.0 | OS: | Any |
Assigned to: | Sunny Bains | CPU Architecture: | Any |
[18 Jan 2010 13:54]
justin dahlke
[19 Jan 2010 15:57]
Susanne Ebrecht
Many thanks for writing a bug report. MySQL 5.0 is out of support since a while. We need to know if you have same problems with by using MySQL 5.1.
[2 Sep 2010 22:05]
Mark Callaghan
MySQL 5.0 is in support for some customers. I think I have been been encountering this frequently over the past few weeks. I wish this would have been investigated as we have lost a lot of time from this.
[2 Sep 2010 22:54]
justin dahlke
Hi Mark, We never tried it on 5.1 b/c upgrading was going to be a big deal. What we ended up doing was a software hack. We realized that the block of code that was in a transaction didn't absolutely have to be in a transaction so long as we retried the queries when a deadlock occurred. It's a total hack which opened us up to some potential edge cases, but it also solved the replication problem which was more critical at the time. Hope that helps
[3 Sep 2010 14:54]
MySQL Verification Team
point the attached testcase to the master server and run. 5.0 slave breaks quickly.
[3 Sep 2010 14:55]
MySQL Verification Team
testcase.
Attachment: bug50413.c (text/plain), 6.86 KiB.
[3 Sep 2010 15:14]
MySQL Verification Team
repeatable on 5.1.50 when master/slave have --innodb_autoinc_lock_mode=0
[3 Sep 2010 15:55]
MySQL Verification Team
http://dev.mysql.com/doc/refman/5.1/en/innodb-auto-increment-handling.html says "Therefore, if you are using statement-based replication, you must either avoid INSERT ... ON DUPLICATE KEY UPDATE or use innodb_autoinc_lock_mode = 0 (“traditional” lock mode)." in this case, binlogs are broken and slave breaks in 5.1/5.6
[3 Sep 2010 16:42]
Mark Callaghan
Awesome, so SBR is broken by default for MySQL 5.1 given the default value of innodb_autoinc_lock_mode.
[3 Sep 2010 21:26]
Mark Callaghan
Actually, I don't understand the docs at -- http://dev.mysql.com/doc/refman/5.1/en/innodb-auto-increment-handling.html. Paraphrasing some of what has been written there: >>> INSERT ... ON DUPLICATE KEY UPDATE is a "mixed-mode" insert >>> According to this, I don't have to use innodb_autoinc_lock_mode=0 with SBR. >>> innodb_autoinc_lock_mode = 0 (“traditional” lock mode) ... This lock mode is provided only for backward compatibility and performance testing. There is little reason to use this lock mode unless you use “mixed-mode inserts” and care about the important difference in semantics described later. >>> The claim here is that I must use innodb_autoinc_lock_mode=0 for INSERT ...ON DUP KEY UPDATE. That is a bit stronger then the text in the previous section. >>> innodb_autoinc_lock_mode = 1 (“consecutive” lock mode) A similar situation exists if you use INSERT ... ON DUPLICATE KEY UPDATE. This statement is also classified as a “mixed-mode insert” since an auto-increment value is not necessarily generated for each row. Because InnoDB allocates the auto-increment value before the insert is actually attempted, it cannot know whether an inserted value will be a duplicate of an existing value and thus cannot know whether the auto-increment value it generates will be used for a new row. Therefore, if you are using statement-based replication, you must either avoid INSERT ... ON DUPLICATE KEY UPDATE or use innodb_autoinc_lock_mode = 0 (“traditional” lock mode). >>> Can someone explain this?
[6 Sep 2010 18:01]
Mark Callaghan
Shane - your testcase shows corruption when innodb_autoinc_lock_mode=0 is used. But the docs state that innodb_autoinc_lock_mode=0 should be used to avoid problems when SBR is also used. I care about this because of corruption that occurs when innodb_autoinc_lock_mode=1. Am I stuck?
[8 Sep 2010 23:19]
Mark Callaghan
I must restate my last comment. Switching to 5.1 has fixed my corruption problem that occurs for INSERT ... ON DUPLICATE KEY UPDATE statements when the binlog has incorrect SET INSERT_ID=... values. And the switch was done using innodb_autoinc_lock_mode=1, so now I want to know why the docs warn that innodb_autoinc_lock_mode=0 should be used with SBR.
[1 Dec 2010 11:50]
Sven Sandberg
Here is a deterministic test case that reproduces the problem. ======== BEGIN rpl_bug-master.opt ======== --innodb_autoinc_lock_mode=2 ======== END rpl_bug-master.opt ======== ======== BEGIN rpl_bug.test ======== --source include/master-slave.inc --source include/have_binlog_format_statement.inc --source include/have_innodb.inc CREATE TABLE t1 (a INT AUTO_INCREMENT PRIMARY KEY, b INT UNIQUE KEY, c INT) ENGINE=InnoDB; # Transaction 1 on connection 1 BEGIN; INSERT INTO t1 SET b=1, c=1 ON DUPLICATE KEY UPDATE b=VALUES(b), c=VALUES(c); INSERT INTO t1 SET b=1, c=2 ON DUPLICATE KEY UPDATE b=VALUES(b), c=VALUES(c); # Transaction 2 on connection 2 --connection master1 BEGIN; INSERT INTO t1 SET b=2, c=3 ON DUPLICATE KEY UPDATE b=VALUES(b), c=VALUES(c); COMMIT; --connection master COMMIT; SELECT * FROM t1; --sync_slave_with_master SELECT * FROM t1; ======== END rpl_bug.test ======== The test case causes slave to stop with the following error message: Error 'Duplicate entry '1' for key 'b'' on query. Default database: 'test'. Query: 'INSERT INTO t1 SET b=1, c=2 ON DUPLICATE KEY UPDATE b=VALUES(b), c=VALUES(c)' The test case generates the following binlog: BEGIN SET INSERT_ID=2; INSERT INTO t1 SET b=2, c=3 ON DUPLICATE KEY UPDATE b=VALUES(b), c=VALUES(c) COMMIT; BEGIN SET INSERT_ID=1; INSERT INTO t1 SET b=1, c=1 ON DUPLICATE KEY UPDATE b=VALUES(b), c=VALUES(c) SET INSERT_ID=2; INSERT INTO t1 SET b=1, c=2 ON DUPLICATE KEY UPDATE b=VALUES(b), c=VALUES(c) COMMIT; This is what happens on master: M1. Transaction 1 allocates autoinc value 1 and inserts the first row (1,1,1). M2. Transaction 1 allocates autoinc value 2 for the second INSERT and tries to insert (2,1,2). Since there is a UK on b, the existing row is updated and becomes (1,1,2). Note that the allocated autoinc value was not used, so the next autoinc value is still 2. M3. Transaction 2 allocates autoinc value 2 and inserts row (2,2,3). M4. Transaction 2 commits and is written to the binlog. M5. Transaction 1 commits, hence is written to the binlog after Transaction 1. When the slave replays this binlog, the following happens: S1. Transaction 2 inserts (2,2,3). S2. Transaction 1 inserts row (1,1,1). S3. Transaction 1 tries to insert row (2,1,2). Now this row clashes with *both* the PK on a and the UK on b. Which one it chooses to update is nondeterministic, but it happens to choose (2,2,3). Thus, it tries to replace (2,2,3) by (2,1,2). This fails because of the UK on b.
[1 Dec 2010 12:05]
Sven Sandberg
Correction: rpl_bug-master.opt should contain --innodb_autoinc_lock_mode=0 Also, the test case can be simplified: it's enough to have one single-statement transaction and a concurrent autocommited statement, as follows: --source include/master-slave.inc --source include/have_binlog_format_statement.inc --source include/have_innodb.inc CREATE TABLE t1 (a INT AUTO_INCREMENT PRIMARY KEY, b INT UNIQUE KEY, c INT) ENGINE=InnoDB; INSERT INTO t1 SET b=1, c=1; BEGIN; INSERT INTO t1 SET b=1, c=2 ON DUPLICATE KEY UPDATE b=VALUES(b), c=VALUES(c); --connection master1 INSERT INTO t1 SET b=2, c=3 ON DUPLICATE KEY UPDATE b=VALUES(b), c=VALUES(c); --connection master COMMIT; SELECT * FROM t1; --sync_slave_with_master SELECT * FROM t1;
[1 Dec 2010 12:41]
Sven Sandberg
In fact, it seems the reason for this bug is not innodb or autoincrement: it happens with concurrent transactions and INSERT ON DUPLICATE KEY UPDATE statements. Here is an even simpler test case (independent of innodb_autoinc_lock_mode): --source include/master-slave.inc --source include/have_binlog_format_statement.inc --source include/have_innodb.inc CREATE TABLE t1 (a INT PRIMARY KEY, b INT UNIQUE KEY, c INT) ENGINE = InnoDB; INSERT INTO t1 VALUES (1, 1, 1); BEGIN; INSERT INTO t1 VALUES (2, 1, 2) ON DUPLICATE KEY UPDATE b=VALUES(b), c=VALUES(c); --connection master1 INSERT INTO t1 VALUES(2, 2, 3) ON DUPLICATE KEY UPDATE b=VALUES(b), c=VALUES(c); --connection master COMMIT; SELECT * FROM t1; --sync_slave_with_master SELECT * FROM t1; I don't think there is any way to solve this with statement-based replication. So the solution is to mark INSERT..ON DUPLICATE KEY IGNORE unsafe. That will cause a warning if binlog_format=statement and switch to row logging when binlog_format=row. Maybe it is enough to mark INSERT..ON DUPLICATE KEY IGNORE unsafe when it is inside a transaction. I'm not 100% sure about that, it would need to be investigated more carefully.
[1 Dec 2010 19:53]
Sven Sandberg
Actually, there are two bugs here: 1. The primary problem is an innodb bug (contrary to what I said in my last comment). When the following statement failed to find an entry for 2 in column a, it should have taken a gap lock preventing concurrent clients from adding a row with value 2: INSERT INTO t1 VALUES (2, 1, 2) ON DUPLICATE KEY UPDATE b=VALUES(b) c=VALUES(c) In general, if there is more than one unique or primary key, and INSERT ... ON DUPLICATE KEY UPDATE does not find any value for one of the keys, it should take a gap lock on that key and hold it for the duration of the transaction. This is not only a replication problem, but also a transaction isolation problem. 2. There is also a related replication bug. I reported a separate bug for that: see BUG#58637. The original bug would be solved by either of these fixes (if the reporter switched to binlog_format=mixed). However, the reason that it failed in the original report was problem 1. So we will keep this bug for problem 1 (the innodb problem), and use BUG#58637 to track problem 2.
[1 Dec 2010 19:57]
Sven Sandberg
The following test case shows that the interleaved transactions execute in a non-serializable order. --source include/have_innodb.inc --connect (other,127.0.0.1,root,,test,$MASTER_MYPORT,) --connection default CREATE TABLE t1 (a INT UNIQUE KEY, b INT UNIQUE KEY, c INT) ENGINE = InnoDB; --echo # Interleaved transactions INSERT INTO t1 VALUES (1, 1, 1); BEGIN; INSERT INTO t1 VALUES (2, 1, 2) ON DUPLICATE KEY UPDATE c=VALUES(c); --connection other BEGIN; INSERT INTO t1 VALUES (2, 2, 3) ON DUPLICATE KEY UPDATE c=VALUES(c); COMMIT; --connection default INSERT INTO t1 VALUES (2, 3, 4) ON DUPLICATE KEY UPDATE c=VALUES(c); COMMIT; SELECT * FROM t1; --echo # Serialized execution order 1 DELETE FROM t1; INSERT INTO t1 VALUES (1, 1, 1); BEGIN; INSERT INTO t1 VALUES (2, 1, 2) ON DUPLICATE KEY UPDATE c=VALUES(c); INSERT INTO t1 VALUES (2, 3, 4) ON DUPLICATE KEY UPDATE c=VALUES(c); COMMIT; BEGIN; INSERT INTO t1 VALUES (2, 2, 3) ON DUPLICATE KEY UPDATE c=VALUES(c); COMMIT; SELECT * FROM t1; --echo # Serialized execution order 2 DELETE FROM t1; INSERT INTO t1 VALUES (1, 1, 1); BEGIN; INSERT INTO t1 VALUES (2, 2, 3) ON DUPLICATE KEY UPDATE c=VALUES(c); COMMIT; BEGIN; INSERT INTO t1 VALUES (2, 1, 2) ON DUPLICATE KEY UPDATE c=VALUES(c); INSERT INTO t1 VALUES (2, 3, 4) ON DUPLICATE KEY UPDATE c=VALUES(c); COMMIT; SELECT * FROM t1; DROP TABLE t1;
[1 Dec 2010 20:13]
Sven Sandberg
Btw, Mark is right: there is no replication problem specific to innodb_autoinc_lock_mode=1. It is as safe as innodb_autoinc_lock_mode=0. We should remove that warning from the documentation.
[1 Dec 2010 20:13]
Sven Sandberg
Btw, Mark is right: there is no replication problem specific to innodb_autoinc_lock_mode=1. It is as safe as innodb_autoinc_lock_mode=0. We should remove that warning from the documentation.
[29 Dec 2010 14:05]
Mark Callaghan
Given that this bug occurs with innodb_autoinc_lock_mode=0 but not when set to 1, I think that 1 is safer than 0.
[24 Mar 2011 14:18]
Brian Parris
It was added to the documentation due to this: http://bugs.mysql.com/bug.php?id=28781 people were complaining about gaps in autoincrement values which is normal behavior when innodb_autoinc_lock_mode=1, setting it to 0 uses the old lock mode and avoids gaps when using ON DUPLICATE KEY UPDATE. MyISAM version of this issue http://bugs.mysql.com/bug.php?id=24432 for 5.0.24 which they apparently fixed in 5.0.38 so autoinc gaps no longer occur, of course this COMPLETELY breaks replication between 5.0.24 and anything newer(so much for minor release changes).
[6 Mar 2013 8:49]
Ricardo Oliveira
Any plan to fix thi? this has been finger pointed by bug #52020 and im getting deadlocks on a simple insert on duplicate key update left and right.
[4 Nov 2013 13:17]
Daniel Price
Fixed as of 5.7.3, and here's the changelog entry: An "INSERT ...ON DUPLICATE KEY UPDATE" statement run on a table with multiple unique indexes would sometimes cause events to be written to the binary log incorrectly. Thank you for the bug report.
[4 Nov 2013 13:44]
Daniel Price
Revision to changelog entry: "Concurrent "INSERT ...ON DUPLICATE KEY UPDATE" statements run on a table with multiple unique indexes would sometimes cause events to be written to the binary log incorrectly"
[6 Nov 2013 12:40]
Daniel Price
Reopening this bug. The patch has been reverted due to regressions. The 5.7.3 changelog entry has been removed.
[12 Nov 2013 12:50]
Daniel Price
The changelog entry noted previously has been added to the 5.7.4 changelog. Thank you for the bug report.
[30 Nov 2018 13:16]
Daniel Price
Posted by developer: The fix for this bug was reverted by by the fix for Bug #25966845 INSERT ON DUPLICATE KEY GENERATE A DEADLOCK in 5,7,26 and 8.0.15.