Problem/Motivation
When one of my sites had a few content editors working on content, they were reporting occasional 500 error pages that I couldn't later replicate. From reviewing the logs I noticed that deadlocks had occured in the database. However, the deadlock did not always occur in the same place. Sometimes it was the taxonomy_term_data table, other times, apachesolr_index_entities_node and so on. If the issue was a deadlock, then the problem had to be occurring when one process attempted to perform the same operation as another process at the same time.
I wrote a quick script to replicate the issue and ran several threads of it:
while ($node = node_load(1234)) {
try {
$node->title = substr($node->title, 5) . substr($node->title, 0, 5);
node_save($node);
}
catch (Exception $e) {
watchdog_exception('debug', $e);
}
}
While core's node edit forms are atomic, contrib modules like views bulk operations are not. node_save is a well known and used API function but locking operations to use it is not widely adopted nor is it something that would work if ever module tried to implement its own version of it.
Proposed resolution
The solution for an atomic node_save means that node_save may fail. At the moment, node_save never returns a value so its not obvious how to tell if the operation failed or not. However, node_save does throw exceptions. My proposed solution is to lock the node_save operation by nid or throw an exception if the lock cannot be obtained. As this is what is likely to happen further down the chain if a deadlock should occur, the only difference will be that (A) the error message is more meaning full to the application and (B) a standard Exception will be thrown instead of a PDOException.
| Comment | File | Size | Author |
|---|---|---|---|
| #3 | 1576716-7.x-atomic-node_save.patch | 1.35 KB | josh waihi |
| 7.x-atomic-node_save.patch | 1.18 KB | josh waihi |
Comments
Comment #1
josh waihi commentedDidn't set the status correctly.
Comment #3
josh waihi commentedChanges made to make node_save lock when updating only.
Comment #4
slashrsm commentedThis could be related #230029: Remove race condition in node_save.
Comment #5
damien tournoud commentedJosh, we need to dig deeper into what type of DEADLOCK you are seeing. Is that on PostgreSQL?
On MySQL we do have a known issue that is easy to fix by changing the configuration of the server.
On PostgreSQL, I assumed we would not have the same type of deadlock as the default transaction isolation on PostgreSQL should require less locking then on MySQL.
Comment #6
josh waihi commentedThis is in MySQL that this problem occurs. Damian, what is the issue what suggests the server configuration change?
I've been running this patch for awhile and seems to be working really well.
Comment #7
damien tournoud commentedSee #937284: DEADLOCK errors on MergeQuery INSERT due to InnoDB gap locking when condition in SELECT ... FOR UPDATE results in 0 rows.
We are not going to add application-level locking to node_save() when the database is already providing us with this semantic. That would be slow and painful.
You need this in your MySQL configuration: