Bug #21293 Deadlock detection prefers to kill long running FOR UPDATE queries
Submitted: 26 Jul 2006 12:19 Modified: 30 Jul 2007 18:23
Reporter: Domas Mituzas
Status: Closed
Category:Server: InnoDB Severity:S4 (Feature request)
Version:4.1,5.0-bk,5.1-bk OS:Any
Assigned to: Vasil Dimov Target Version:
Triage: D5 (Feature request)

[26 Jul 2006 12:19] Domas Mituzas
Description:
Long SELECT .. FOR UPDATE or INSERT/REPLACE ... SELECT queries, which establish locks on
data reads, may be killed by deadlock detector, if there're any other transactions, which
edit source data.

In concurrent environments this may make INSERT ... SELECT unusable, as locks not obtained
atomically may have other transaction locks interrupted.

How to repeat:
#
# Test of Locking
#
--disable_warnings
drop table if exists t1,t2;
--enable_warnings
create table t1 (
        a int primary key
) engine=InnoDB;

create table t2 (
        a int
) engine=MyISAM;

insert into t1 values (1),(2),(3);

#send insert into t2 select * from t1 where a > sleep(1);
send select * from t1 where a > sleep(1) for update;

--connect (othertransaction,,,,)
begin;
delete from t1 where a = 3;
--sleep 2
send delete from t1 where a = 1;

connection default;
reap;

Suggested fix:
roll back the other transactions instead the jumbo ones?
[26 Jul 2006 17:21] Heikki Tuuri
Domas,

InnoDB tries to save the transaction that has updated, deleted, or inserted the biggest
number of rows. That unfortunately means that pure SELECT ... FOR UPDATE transactions will
end up as the victim.

To adequately solve this, we should introduce a settable priority for a transaction.

In the meantime, a clumsy workaround is to run some big dummy insert + delete in your
transaction first, and only after that proceed to do the actual work. The dummy operation
will raise the priority of your transaction if a deadlock is encountered.

Regards,

Heikki

lock0lock.c:

                                if (ut_dulint_cmp(wait_lock->trx->undo_no,
                                                        start->undo_no) >= 0) {
                                        /* Our recursion starting point
                                        transaction is 'smaller', let us
                                        choose 'start' as the victim and roll
                                        back it */

                                        return(LOCK_VICTIM_IS_START);
                                }
[26 Jul 2006 17:25] Domas Mituzas
One of serious problems with this is that in case of 

INSERT INTO ... SELECT
or
REPLACE INTO ... SELECT

if the target table is MyISAM, InnoDB interrupts the SELECT query, and partially-executed
statement is written into binary log with error code attached.
[4 Jan 2007 15:35] Heikki Tuuri
Domas,

I think Guilhem implemented a retry to replication in a deadlock situation. That was about
2 years ago.

Regards,

Heikki
[4 Jan 2007 15:40] Domas Mituzas
The problem is not a retry, but InnoDB interrupting MyISAM operation in the middle,
because of lack of rows edited. 

There were discussions about using ha::extra() to fetch such information somehow. 
It could make sense to check if a query has edited non-transactional tables before killing
it.

It is a bug, as it causes data inconsistencies in replication.
[4 Jan 2007 15:42] Heikki Tuuri
Domas,

ok, I have now raised this to S2.

--Heikki
[16 Apr 2007 13:51] Heikki Tuuri
This is a feature request.
[12 Jul 2007 19:57] Timothy Smith
Queued to 5.1-maint team tree(s)
[19 Jul 2007 17:48] Bugs System
Pushed into 5.1.21-beta
[30 Jul 2007 18:23] Paul DuBois
Noted in 5.1.21 changelog.

When determining which transaction to kill after deadlock has been
detected, InnoDB now adds the number of locks to a transaction's
weight, and avoids killing transactions that mave modified
non-transactional tables. This should reduce the likelihood of
killing long-running transactions containing SELECT ... FOR UPDATE or
INSERT/REPLACE INTO ... SELECT statements, and of causing partial
updates if the target is a MyISAM table.