Problem/Motivation

During a call to a hook_update_N() function that is walking over a large number of nodes and updating each with a final call to node_save() we experienced the following error;

sample_config module : 
  7094 -   Enable colorbox load to allow links  to be opened in a colorbox. 
  7095 -   Update the year with new format 

Do you wish to run all pending updates? (y/n): y
Processing sample_config_update_7094                                               [success]
Processing sample_config_update_7095                                               [success]
Reverting all features.                                                                           [success]
Gathering customer_pricing_phase nodes.                                            [success]
Processing 1 customer_pricing_phase nodes.                                        [success]
Processed 1 of 166: Unison Generators - Confirm assets ASD2015/16  [success]
Processed 2 of 166: Wessampleer - Confirm assets ASD2015/16          [success]
WD node: PDOException: SQLSTATE[25P02]: In failed sql transaction: 7 ERROR:  current transaction is aborted, commands ignored until end of         [error]
transaction block: UPDATE url_alias SET source=:db_update_placeholder_0, alias=:db_update_placeholder_1, language=:db_update_placeholder_2
WHERE  (pid = :db_condition_placeholder_0) ; Array
(
    [target] => default
    [return] => 2
    [already_prepared] => 1
)
 in drupal_write_record() (line 7261 of /path/to/drupal/includes/common.inc).
SQLSTATE[25P02]: In failed sql transaction: 7 ERROR:  current transaction is aborted, commands ignored until end of transaction block              [error]

The node that the error occurs on is random and occasionally we would also have a successful run in our development environments. However, the staging and production environments always had the error at some point.

This was tracked down to path_save() and, more specifically, the use of drupal_write_record() within it. The first thing that drupal_write_record() says is;

Do not use drupal_write_record() within hook_update_N() functions, since the database schema cannot be relied upon when a user is running a series of updates. Instead, use db_insert() or db_update() to save the record.

Proposed resolution

The proposed resolution is to follow the advice in the docs for drupal_write_record() and use db_insert() and db_update() instead.

Since doing this I've not been able to replicate this issue at all.

Remaining tasks

  • Code review
  • Community testing
Support from Acquia helps fund testing for Drupal Acquia logo

Comments

Gold’s picture

I may have jumped the gun on this one. Second test run on staging still failed. I'm thinking this wasn't the actual source of the issue.

However, the reason for using this patch still stands. Based on what drupal_write_record() recommends and with path_save being used a lot in update hooks this change should probably still stand. I'm going to dig into db_update and db_insert a little and see if they experience the same issues as dwr. If so, there may be a second patch shortly using db_query and raw sql.

wiifm’s picture

Status: Active » Needs work

Quick review:

  1. +++ b/includes/path.inc
    @@ -438,13 +438,27 @@ function path_save(&$path) {
    +  // The docs for drupal_write_record() recommend that it never be used from
    

    Make this into a hyperlink so it renders correctly in api.drupal.org

  2. +++ b/includes/path.inc
    @@ -438,13 +438,27 @@ function path_save(&$path) {
    -    module_invoke_all('path_insert', $path);
    

    Do not remove this line

  3. +++ b/includes/path.inc
    @@ -438,13 +438,27 @@ function path_save(&$path) {
    +      'source'   => $path['source'],
    +      'alias'    => $path['alias'],
    +      'language' => $path['language'],
    

    Do not worry about aligning the '=>', this is not required (and creates hard to maintain code)

  4. +++ b/includes/path.inc
    @@ -438,13 +438,27 @@ function path_save(&$path) {
    -    module_invoke_all('path_update', $path);
    

    Again, this should not be removed

  5. +++ b/includes/path.inc
    @@ -438,13 +438,27 @@ function path_save(&$path) {
    +    ->condition('pid', $path['pid'], '=')
    

    The third argument to condition() defaults to '=', I would remove

Gold’s picture

It's been a long painful day.

  1. Sorted (if I'm reading the docs right)
  2. Totally missed the path_insert & path_update bits. Moved these out of the condition and then forgot to include in the diff.
  3. Sorted
  4. See #2
  5. Done.
Gold’s picture

Status: Needs work » Needs review
wiifm’s picture

Status: Needs review » Needs work

Hey, that is looking good, small nitpicks:

  1. +++ b/includes/path.inc
    @@ -429,6 +429,8 @@ function path_load($conditions) {
    + * @see drupal_write_record().
    

    Unsure of whether this is needed in the parent docblock, as this function is no longer used inside it correct?

  2. +++ b/includes/path.inc
    @@ -438,12 +440,29 @@ function path_save(&$path) {
    +  // recommend that it never be used from a hook_update_N() call.  This
    

    I think a single space is what is needed here. Also s/recommend/recommends/

Gold’s picture

I was wondering about the @see thing.

Removed that and fixed the comment too.

Gold’s picture

Status: Needs work » Needs review
wiifm’s picture

Looks good to me, keen to have more eyes from someone in core to look at this. Also keen to see what the test bot says here.

Gold’s picture

@wiifm, thanks for the critique.

The last submitted patch, 1: drupal-path-save-sans-dwr-2459719-1.patch, failed testing.

The last submitted patch, 3: drupal-path-save-sans-dwr-2459719-3.patch, failed testing.

Status: Needs review » Needs work

The last submitted patch, 6: drupal-path-save-sans-dwr-2459719-6.patch, failed testing.

Gold’s picture

running the tests on SimplyTest.me suggests it's something in the update hooks. @wiifm pointed out that drupal_write_record would populate the pid on an insert.

Let's try that.

Gold’s picture

Status: Needs work » Needs review

Really need to get into the habit up updating this on patch submission...

wiifm’s picture

Status: Needs review » Reviewed & tested by the community

Hey patch passes all the test, seems to fix an important bug. Marking RTBC to get a core maintainer to review.

David_Rothstein’s picture

Status: Reviewed & tested by the community » Postponed (maintainer needs more info)

I don't think we should change this. path_save(), like most API functions, is too high-level to recommend calling it from update functions at all. In addition to the drupal_write_record(), it also invokes hooks, etc.

I think if you're running into issues with it you should use low-level functions only, like direct db_insert() or db_update() queries... right?

David_Rothstein’s picture

Well I see the code comments refer to this being called indirectly via node_save(). I would assume calling node_save() from a generic update function has all sorts of other problems too...

It's fine in a site-specific update function, I think, but in generic code (e.g. contrib modules) it's probably not the best idea.

That said, "not recommended" is not the same as "causes PDOExceptions". I would really expect that if you're seeing PDOExceptions here, the bug is caused by something else.