[MDEV-17512] Deadlock after locking all rows in table Created: 2018-10-21 Updated: 2023-03-10 |
|
| Status: | Open |
| Project: | MariaDB Server |
| Component/s: | Locking, Storage Engine - InnoDB |
| Affects Version/s: | 10.2.18, 10.3.10 |
| Fix Version/s: | 10.2 |
| Type: | Bug | Priority: | Major |
| Reporter: | Simeon Maxein | Assignee: | Marko Mäkelä |
| Resolution: | Unresolved | Votes: | 1 |
| Labels: | performance | ||
| Environment: |
Windows 7 Pro x64, Ubuntu Xenial |
||
| Attachments: |
|
||||||||||||
| Issue Links: |
|
||||||||||||
| Description |
|
This is a surprisingly simple scenario leading to a (to me) surprising deadlock. Since I'm not an expert on InnoDB I have already tried other ways to find out whether it is a bug (see https://dba.stackexchange.com/questions/216450/is-this-surprising-deadlock-reasonable-or-a-mariadb-bug), but unfortunately didn't get any helpful responses, so I'm filing it now. Here is the setup. The isolation level is left at its default (REPEATABLE_READ): create database deadlock; Now we can start. Let's open two connections A and B to the database. -- Connection A -- Connection B At this point, the select issued by B is waiting. This is as expected because A is holding the relevant locks. Note that the StackExchange question used "delete from foo;" instead in both transactions - it makes no difference to the result, but a select seems easier to analyze. -- Connection A And at this point, mariadb kills the waiting query of B due to a deadlock. This is very surprising to me, because it would mean that B has already managed to acquire some locks. As an additional point of information, if you use the id 11 instead of 9 in the last statement, there is no deadlock. This test is a simplification of a production scenario where this deadlock kept occurring - there was a transaction that first deleted "invalid" records before inserting a new one. Whether a record was "invalid" was determined in a way that did not use an index, so the entire table had to be scanned. I did not understand how the deadlock could occur, since my mental model was that the delete would essentially get an exclusive lock on everything relevant in the table, so a second transaction running the same queries would have to wait before being able to acquire any locks, and thus could not possibly contribute to a deadlock. |
| Comments |
| Comment by Elena Stepanova [ 2018-12-29 ] | |||||||||||||||||||||||||||||||||||||||||||
|
Here is the last deadlock info for the test case that you described. It is basically the same as the one you attached, but the attachment is for DELETE, so I'll get another one, to avoid confusion.
Here Connection A is TRANSACTION(2), and Connection B is TRANSACTION(1) So, to my understanding, in your case Connection A, by running SELECT .. FOR UPDATE, acquired an exclusive lock on the index record, and also a shared gap lock.
Hence the deadlock. It's a bit inconvenient that there is no clear indication of gap lock holding in the LAST DEADLOCK info (or in TRANSACTIONS info, for that matter), so I'll assign it to marko to confirm that's what is happening here, and if so, clarify if it's possible to see acquired gap locks in innodb status (apart from counting rows and seeing that there is one too many in "row locks"). | |||||||||||||||||||||||||||||||||||||||||||
| Comment by Marko Mäkelä [ 2018-12-31 ] | |||||||||||||||||||||||||||||||||||||||||||
|
According to what I have written in MDEV-16406 half a year ago, SELECT…FOR UPDATE indeed acquires a shared gap lock. The test case is very similar to my test case in MDEV-16406. The ‘insert intention’ lock that will conflict with the SELECT * FOR UPDATE actually is an exclusive gap lock. It surely feels counter-intuitive that FOR UPDATE is only acquiring a shared gap lock, and not an exclusive one. The motivation could have been to minimize locking conflicts. I don’t think that this behaviour directly contradicts the FOR UPDATE request, because it is only INSERT that causes the deadlock. | |||||||||||||||||||||||||||||||||||||||||||
| Comment by Marko Mäkelä [ 2021-04-13 ] | |||||||||||||||||||||||||||||||||||||||||||
|
The terminology around gap locks is confusing. There are not actually "shared" or "exclusive" gap locks, but we could call them "read gap" locks and "write gap" locks (a.k.a. insert intention locks). I have tried to clarify this in MDEV-16406. MDEV-24813 has been filed for making a locking full table scan to lock the entire table. That should improve performance and avoid this type of deadlock. |