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.

Comments

josh waihi’s picture

Status: Active » Needs review

Didn't set the status correctly.

Status: Needs review » Needs work

The last submitted patch, 7.x-atomic-node_save.patch, failed testing.

josh waihi’s picture

Status: Needs work » Needs review
StatusFileSize
new1.35 KB

Changes made to make node_save lock when updating only.

slashrsm’s picture

damien tournoud’s picture

Status: Needs review » Postponed (maintainer needs more info)

Josh, 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.

josh waihi’s picture

This 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.

damien tournoud’s picture

Status: Postponed (maintainer needs more info) » Closed (duplicate)

See #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:

binlog_format=row
innodb_locks_unsafe_for_binlog=1