[MDEV-27992] DELETE fails to delete record after blocking is released Created: 2022-03-03 Updated: 2023-03-10 Resolved: 2022-03-07 |
|
| Status: | Closed |
| Project: | MariaDB Server |
| Component/s: | Data Manipulation - Delete, Locking, Storage Engine - InnoDB |
| Affects Version/s: | 10.5.14, 10.6.6, 10.7.3 |
| Fix Version/s: | 10.3.35, 10.4.25, 10.5.16, 10.6.8, 10.7.4, 10.8.3 |
| Type: | Bug | Priority: | Critical |
| Reporter: | John Jove | Assignee: | Vladislav Lesin |
| Resolution: | Fixed | Votes: | 0 |
| Labels: | transactions | ||
| Environment: |
OS: Ubuntu-20.04 LTS |
||
| Issue Links: |
|
||||||||||||||||||||||||
| Description |
|
Isolation Level: Read Committed & Read Uncommitted
|
| Comments |
| Comment by Marko Mäkelä [ 2022-03-03 ] | |||||||||||||||||||||||||||||||
|
Here is a simpler, executable version of the test, again (like in
I tested some versions of MariaDB where no wrong result was returned. In those cases, DELETE reported an error:
I verified the wrong result (no error reported for DELETE) also for 10.5.14 and 10.6.6. If I revert the I debugged this with a version of the server that includes the At the time lock_wait() was invoked, the table contained the record (5,5) and the delete-marked record (8,NULL). So, the problem appears to be that after lock_wait() is resumed and the current record is committed as delete-marked, we fail to notice that the record had been replaced with one with a smaller PRIMARY KEY value (3,5). That is why the record (3,5) will survive the execution of the DELETE statement. In this special case where DELETE is operating on the entire table, escalating to table-level locking (MDEV-24813) would prevent this wrong result. vlad.lesin, please check if we could do anything else to prevent this at a lower level. | |||||||||||||||||||||||||||||||
| Comment by Marko Mäkelä [ 2022-03-03 ] | |||||||||||||||||||||||||||||||
|
The DELETE did acquire a gap lock on the gap before the record (5,5), but that did not conflict with the preceding UPDATE. Even if I add SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE before the DELETE, the record (5,3) will survive the DELETE. | |||||||||||||||||||||||||||||||
| Comment by Marko Mäkelä [ 2022-03-04 ] | |||||||||||||||||||||||||||||||
|
Even if both transactions report SELECT @@tx_isolation as SERIALIZABLE, the DELETE will still fail to remove the record. Note: If there was a WHERE condition on the DELETE that is supposed to match all rows, then a work-around provided MDEV-24813 would not work. I was thinking a bit more, and I currently hope that this problem is only related to a corner case: When the PRIMARY KEY of a record is being updated to a smaller value, a concurrent DELETE or UPDATE may have trouble. I found two source code comments that refers to a related problem, the Halloween problem "allowing the row to be visited more than once during the operation". Here, a matching row is not being visited at all.
I understand that the comment is a description of a solution that has not been implemented. At a higher level, there is another comment about this:
For fixing this bug, I can offer a wild idea that I do not think can be implemented easily: Once the lock has been granted and the record is committed as delete-marked, then we could try to look up the record that replaced it. If the new key is smaller, we should somehow rewind to that record (without running into the Halloween problem). Theoretically we could look up the replacement record if our transaction holds a read view (this is not necessarily the case for locking reads!) that allows us to access the undo log of the initially conflicting and now committed transaction. I do not think that the current undo log format allows us to distinguish the UPDATE of a PRIMARY KEY from a DELETE and INSERT. | |||||||||||||||||||||||||||||||
| Comment by Vladislav Lesin [ 2022-03-04 ] | |||||||||||||||||||||||||||||||
|
I simplified the test a bit:
Here is the sequence of actions which cause the error: | |||||||||||||||||||||||||||||||
| Comment by Vladislav Lesin [ 2022-03-07 ] | |||||||||||||||||||||||||||||||
|
As there is no better solution at the moment, |