Sample Header Ad - 728x90

How to gracefully handle MySQL deadlocks involving SAVEPOINT?

0 votes
1 answer
1022 views
I have a concurrency problem where 2 (or more) processes are making inserts at the same time and are all locking the same index page (I guess) among with a on a SELECT FOR UPDATE clause Here is a reproducible sample: CREATE TABLE U ( -- user id int not null primary key, name varchar(222) ); CREATE TABLE O ( -- object id int not null primary key, name varchar(222) ); CREATE TABLE OU ( -- user object id int not null auto_increment primary key, object_id int, user_id int, -- (object_id, user_id) should be UNIQUE (not enforced by a constraint) CONSTRAINT fk_object_id FOREIGN KEY (object_id) REFERENCES O(id) ON DELETE CASCADE, CONSTRAINT fk_user_id FOREIGN KEY (user_id) REFERENCES U(id) ON DELETE CASCADE ); INSERT INTO U VALUES (1, 'foo'), (2, 'bar'); -- Start 2 concurrent transactions (A, B) BEGIN; -- A BEGIN; -- B INSERT INTO O VALUES (1, 'a'); -- A INSERT INTO O VALUES (2, 'b'); -- B SAVEPOINT s_a; -- A SAVEPOINT s_b; -- B SELECT * FROM OU WHERE object_id = 1 FOR UPDATE; -- A SELECT * FROM OU WHERE object_id = 2 FOR UPDATE; -- B INSERT INTO OU(object_id, user_id) VALUES (1, 1); -- A INSERT INTO OU(object_id, user_id) VALUES (2, 1); -- B one of the transactions will fail with a Deadlock found when trying to get lock; try restarting transaction error. The problem is that it rolls back the **whole** transaction without any way for me to gracefully handle the error. In that example, the object 2 is lost. I would **really** like for it to still persist and handle the second insert a few seconds / minutes after another transaction. The way the code is currently designed does not allow fine tuning of the transaction block (I cannot commit after the first insert then start another transaction) Is there any way to tell MySQL to not rollback on that deadlock error, or to rollback specifically to the savepoint? **edit** here is the innodb engine status *** (1) TRANSACTION: TRANSACTION 3727668996, ACTIVE 0 sec inserting mysql tables in use 1, locked 1 LOCK WAIT 3 lock struct(s), heap size 1184, 2 row lock(s), undo log entries 1 MySQL thread id 20145497, OS thread handle 0x7f4fdba2b700, query id 15334836096 xxxxxxxxxxxxx.xxxx.xxx.xxxx.xxx 10.150.15.33 user1 update INSERT INTO Tablename (product_id, product_type_id, contact_id, last_transaction_id, creation_date) VALUES (655988, 1, 26399, NULL, '2017-04-20 19:04:21') *** (1) WAITING FOR THIS LOCK TO BE GRANTED: RECORD LOCKS space id 201 page no 1012 n bits 840 index product_id of table dbname.Tablename trx id 3727668996 lock_mode X locks gap before rec insert intention waiting *** (2) TRANSACTION: TRANSACTION 3727668998, ACTIVE 0 sec inserting, thread declared inside InnoDB 5000 mysql tables in use 1, locked 1 3 lock struct(s), heap size 1184, 2 row lock(s), undo log entries 1 MySQL thread id 20145647, OS thread handle 0x7f5d6d253700, query id 15334836100 xxxxxxxxxxxxx.xxxx.xxx.xxxx.xxx 10.150.15.35 user1 update INSERT INTO Tablename (product_id, product_type_id, contact_id, last_transaction_id, creation_date) VALUES (655991, 1, 26399, NULL, '2017-04-20 19:04:21') *** (2) HOLDS THE LOCK(S): RECORD LOCKS space id 201 page no 1012 n bits 840 index product_id of table dbname.Tablename trx id 3727668998 lock_mode X locks gap before rec *** (2) WAITING FOR THIS LOCK TO BE GRANTED: RECORD LOCKS space id 201 page no 1012 n bits 840 index product_id of table dbname.Tablename trx id 3727668998 lock_mode X locks gap before rec insert intention waiting *** WE ROLL BACK TRANSACTION (2)
Asked by Romuald Brunet (101 rep)
Apr 20, 2017, 01:00 PM
Last activity: Aug 3, 2025, 10:04 AM