While messing with D7 and Views3, I noticed that errors during updates are not logged, even though update.php?op=results says:

Updates were attempted. If you see no failures below, you may proceed happily to the administration pages. Otherwise, you may need to update your database manually. All errors have been logged.

Here, 'logged' links to admin/reports/dblog.

For testing, I created an empty module with a .install file. After enabling the module, I added the following contents to the .install file:

function d7_update_error_update_1() {
  $ret = array();
  db_add_unique_key('non_existing_table', 'id', array('id'));
  return $ret;
}

As expected, this resulted in:

The following updates returned messages
d7_update_error module
Update #1

* Failed: Cannot add unique key id to table non_existing_table: table doesn't exist.

However this error was not logged; no new row was created in the watchdog table.

Comments

marcvangend’s picture

StatusFileSize
new898 bytes

I accidentally left some D6 code in the .install file. Now using this:

/**
 * This so-called update will produce an error.
 */
function d7_update_error_update_1() {
  db_add_unique_key('non_existing_table', 'id', array('id'));
}

However this does not solve the issue.

I'm attaching a zip with my test module, so anyone can try it out. Steps to reproduce: install the module, run UPDATE system SET schema_version = 0 WHERE name = 'd7_update_error' on the database and run update.php.

marcvangend’s picture

Bumping to critical; I think D7 should not be released with this bug.

marcvangend’s picture

Priority: Normal » Critical

Bumping to critical; I think D7 should not be released with this bug.

tom_o_t’s picture

Status: Active » Needs work

I was able to replicate this bug following the steps outlined in #1

catch’s picture

Status: Needs work » Active

No patch here.

marcvangend’s picture

Unfortunately, still no patch here. I've been trying to figure out what's going on, but a lot has changed since D6 in the error handling, update process and of course the db abstraction layer... I just couldn't wrap my head around it yet.

One thing I did notice: in D6, it seems as if update.php is responsible for the "All errors have been logged" message, yet it does not log the errors itself; it simply assumes that the db layer has done so. Wild guess: If that assumption is still present in D7, but the db layer does not write errors to the log, that could be the cause of this bug.

crotown’s picture

Assigned: Unassigned » crotown

I'm taking a look at it at the code sprint.

McGo’s picture

Status: Active » Closed (won't fix)

I can reproduce the error described in #1 too. Anyway, as http://api.drupal.org/api/function/hook_update_N/7 says, the update hook itself has to throw an update exception in case of an error. So i think this is not a bug, you are implementing the hook_update_N hook not correctly.

crotown’s picture

Assigned: crotown » Unassigned

I wasn't able to fix this (or determine if a fix is really needed) before I had to leave the code sprint. (now)

[Incidently, I was able to reproduce the behavior in #1, and tracked the throwing of the exception in this fake case to schema.inc line 395, in the funtion addUniqueKey()]

marcvangend’s picture

Status: Closed (won't fix) » Active

@McGo: OK, I didn't see that yet. However, we can't just set this to won't fix and be done with it.

If my implementation of hook_update_n is incorrect, then why are all core modules doing the same? Have a look at this one, for instance: http://api.drupal.org/api/function/node_update_7001/7. I couldn't find any update function that tries to catch a PDOException and throws a DrupalUpdateException itself.

To me, requiring hook_update_N functions handle errors feels like regression: Errors that used to be logged automatically, now need to caught and logged by the update function. IHMO, there should be some kind of default error logging, otherwise we're going to see a lot of duplicated code (and a lot of developers saying 'WTF').

For the sake of argument, lets assume that the documentation on http://api.drupal.org/api/function/hook_update_N/7 is correct. That would mean that all core modules have implemented hook_update_N incorrectly. It would also mean that update.php should not state "All errors have been logged" without checking if that's true.

One way or the other, something needs to be fixed.

@crotown: when you say "incidently", do you mean that in some cases the errors are logged in the Drupal dblog?

berdir’s picture

schema failure *do* throw exceptions, so #8 is wrong. You wouldn't see a failure in the summary in the first place if there would be no exception.

puregin’s picture

update_do_one() is not logging to the database. Will roll a patch tomorrow

puregin’s picture

Assigned: Unassigned » puregin
Status: Active » Needs review
StatusFileSize
new835 bytes

The exception was being caught and the message returned in the $res array, but the exception is otherwise never logged and $res is only used to print summary results to the screen.

I've added a watchdog() call to log the exception.

damien tournoud’s picture

update_do_one() is quite a mess currently. The $ret variable doesn't seem to be used at all. I guess all those should be converted to the new $context, and the problem will just go away.

puregin’s picture

Looks like $ret gets merged into $context a little later on - I suppose we could just update $context directly...

crotown’s picture

@marcvangend: No, I just meant that I saw the behavior in #1 (no logging) too.

Is the documentation at http://api.drupal.org/api/function/hook_update_N/7 incorrect, then?

marcvangend’s picture

The patch looks good, at least for now. One small thing: the new comment line is longer than 80 characters.

@crotown: Yes, as far as I can see it's incorrect. Maybe the person who wrote it meant to say something else, so it's at least very confusing.

puregin’s picture

StatusFileSize
new991 bytes

Comment line reformatted to fit within 80 characters.

puregin’s picture

The exception is thrown - it's just that it is in fact caught, not logged, and not re-thrown. Rethrowing does in fact log a DrupalUpdateException, but the way things are currently set up, this breaks other things.

puregin’s picture

StatusFileSize
new991 bytes
puregin’s picture

Re-attaching patch, the previous one was almost 24 hours in the queue

marcvangend’s picture

Off topic: The test bots can (should!) be busy these days. On http://qa.drupal.org/pifr/log/recent you can see if your test request was received; http://qa.drupal.org/pifr/status shows the length of the test queue. If you check before re-submitting, that can avoid unnecessary workload.

puregin’s picture

Off topic: thanks, @marcvangend. I did have a quick look at the queue - looks like there was something funky going on. However, the first instance of the patch looks like it got tested finally, so we're good :)

jpmckinney’s picture

Status: Needs review » Reviewed & tested by the community

Looks good to me. Can we also fix the docs at http://api.drupal.org/api/function/hook_update_N/7?

puregin’s picture

Issue tags: +Drupal update

Let's make the doc change, if it is necessary, a separate issue.

marcvangend’s picture

Issue tags: -Drupal update

I also tested the patch and it works as expected.

The documentation at http://api.drupal.org/api/function/update_do_one/7 should also be improved I think.

marcvangend’s picture

Issue tags: +Drupal update

(accidentally removed the tag)

puregin’s picture

StatusFileSize
new174.71 KB

Here's what I see if I just throw a DrupalUpdateException wrapping the DatabaseSchemaObjectDoesNotExistException.

puregin’s picture

StatusFileSize
new137.1 KB

And here's the corresponding entry in watchdog, generated (I believe) by the generic Drupal exception handler.

puregin’s picture

Conclusion - if we do as the documentation for update_do_one() suggests and throw a DrupalUpdateException, this will generate error logs with lots of stack trace information, which is potentially helpful.

However, it's not clear to me where this exception should be handled so that it doesn't get in the way of batch processing.

puregin’s picture

Status: Reviewed & tested by the community » Needs work

I'm thinking this should probably throw the exception, like the documentation says it's supposed to. The challenge now is to determine where the exception should be caught, so that exception handling and batch processing co-exist nicely.

berdir’s picture

If #711108: Richer exception reporting for watchdog() gets commited, watchdog() will be able to decode exceptions on its own, so you could just pass $e to watchdog as the $message and it would log it properly (including file, location and more details in case of pdo exceptions), similiar to what we currently display.

I think the update manager should handle exceptions during datebase updates and not re-throw them as we don't want to break batch runs because of failing update functions.

If for example the database goes down and watchdog() throws an exception too, I think we can safely let that exception bubble up since batch api won't work without the database anyway.

puregin’s picture

@Berdir - is there a way that we can handle exceptions higher up without breaking the batch processing? I've had a first look and I don't understand the way batch processing works well enough to see how to do this, though it may be clear to someone who understands this better than I do.

If we can't find an 'obvious' way to implement this (which I gather was the solution intended/implied by the documentation), I'm willing to set the issue status back to rtbc.

berdir’s picture

I don't think we should handle them higher, why do you?

The only that should be "handled" higher up is when logging to watchdog fails and something is *really* broken then anyway (database crashed, went down, ...) in which case we can't continue with batch processing anyway.

puregin’s picture

Status: Needs work » Needs review

#20: 774882-01.patch queued for re-testing.

puregin’s picture

Status: Needs review » Reviewed & tested by the community

Thanks @Berdir. At this point, I think you're right and handling the exception here makes sense.

rfay’s picture

Status: Reviewed & tested by the community » Needs work

Thanks for the excellent and important work on this patch.

I note that the watchdog error gives no context at all. Shouldn't it at least give a statement of which hook_update_N() was running? It would be Oh, so much better if it would give the actual query that ran (reference #711108: Richer exception reporting for watchdog() which doesn't seem to add this)

For example, the current hook_update_N() error described in #410636-23: D6->D7 update does not create required menus results in the following when update.php is run:

The following updates returned messages
system module
Update #7053

    * Failed: SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry 'user-menu' for key 'PRIMARY'

which is probably not enough, but it at least gives context: You know that it was system_update_7053() where the problem happened.

However, with this patch (#20), the watchdog entry provides not context (except that it's the update system). The output to watchdog is only:

Type	update
Date	Monday, May 17, 2010 - 22:11
User	rfay
Location	http://d7git.l/update.php?id=25&op=do
Referrer	http://d7git.l/update.php?op=start&id=25
Message	SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry 'user-menu' for key 'PRIMARY'
Severity	error
Hostname	127.0.0.1
Operations	

What I'd like to see would be

EDIT: #711108: Richer exception reporting for watchdog() does provide great information for watchdog. I think we can provide that here too.

rfay’s picture

#711108: Richer exception reporting for watchdog() has been committed. That may help us get lots of the information we need to actually report what goes on when something goes wrong.

berdir’s picture

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

Updated the patch to use watchdog_exception() and also use drupal_decode_exception() to display the exception details.

i did not add any additional information because you can see the function name and that tells you which update step it was :)

rfay’s picture

StatusFileSize
new1.06 KB
+++ includes/update.inc
@@ -733,10 +733,15 @@ function update_do_one($module, $number, $dependency_map, &$context) {
+      $variables += _drupal_decode_exception($exception);

$variables = _drupal_decode_exception($e);

And there's no need for the += is there?

Attached patch makes those changes. With that, it seems to work very nicely - way, way better. Complete explanation of what's going on, and logged to watchdog. Yay!

Powered by Dreditor.

carlos8f’s picture

Status: Needs review » Reviewed & tested by the community

Very useful patch. The += was unnecessary since $variables isn't previously defined.

webchick’s picture

Status: Reviewed & tested by the community » Fixed

Nice sleuthing on this, folks! Committed to HEAD. Thanks!

Status: Fixed » Closed (fixed)
Issue tags: -Drupal update

Automatically closed -- issue fixed for 2 weeks with no activity.