Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix EZP-27814: Support MySQL error codes in DB exceptions #1315

Merged
merged 1 commit into from Oct 17, 2017

Conversation

peterkeung
Copy link
Contributor

@peterkeung peterkeung commented Aug 15, 2017

Bug report: https://jira.ez.no/browse/EZP-27814

Since eZ Publish 4.5, we can catch DB exceptions: https://github.com/ezsystems/ezpublish-legacy/blob/master/doc/features/4.5/ezdb_exception_based_error_handling.txt

However, the error code in the DB exception is always 0. This pull request logs the MySQL error code in the exception code so that you can specifically catch error codes (such as deadlocks) and act accordingly (such as retrying the transaction).

One of the reasons this is important is that the legacy API is prone to DB deadlock errors on concurrent object creation.

You can reproduce this quite easily by running multiple versions of this script at the same time:

<?php
for( $i = 0; $i < 100; ++$i )
{
    $params = array();
    $params['class_identifier'] = 'folder';
    $params['parent_node_id'] = 62;
    $params['attributes'] = array( 'name' => 'Test folder script 1' );
 
    $contentObject = eZContentFunctions::createAndPublishObject( $params );
}

By adding the DB exception code, we can catch a deadlock error and retry the creation rather than having the script crash.

@@ -15,7 +15,31 @@
* @version //autogentag//
* @package kernel
*/
class eZDBException extends ezcBaseException
class eZDBException extends Exception
Copy link
Contributor

@andrerom andrerom Aug 25, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

break, could this be done another way?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's here now is essentially a copy of ezcBaseException, but with support for the error code / number. Thus, the intention is that there is no BC break at all.

Copy link
Collaborator

@emodric emodric Sep 5, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a break if someone typehinted against ezcBaseExtension.

Can you not implement a custom base exception (e.g. eZBaseException) extending from ezcBaseExtension with added features?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's unlikely somebody would typehint against 'ezcBaseException'. It's much more likely that somebody would typehint against 'eZDBException'

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, but that doesn't change the fact that it's still a BC break which IMO should not be made lightly.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, I've rewritten it so that we continue to extend ezcBaseException but just set the $message and $code properties, since the ezcBaseException constructor removed $code support.

@peterkeung peterkeung changed the title Support MySQL error codes in DB exceptions Fix EZP-27814: Support MySQL error codes in DB exceptions Aug 29, 2017
@peterkeung peterkeung force-pushed the support_db_exception_code branch 2 times, most recently from 88998b0 to d2a9b2f Compare September 12, 2017 22:48
}
else
{
$this->message = htmlspecialchars( $message );
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should probably be htmlspecialchars($message, ENT_QUOTES, 'UTF-8') to properly encode quotes and since UTF-8 is the default charset only since PHP 5.4.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was originally just a copy of ezcBaseException, but I've made the change now :)

@emodric
Copy link
Collaborator

emodric commented Sep 13, 2017

Apart from the comment about htmlspecialchars, this looks good 👍

Copy link
Member

@adamwojs adamwojs left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1

By adding the DB exception code, we can catch a deadlock error and retry the creation rather than having the script crash.

Maybe one sugestion for future improvment: I think that introduce const representing error codes, will allow to avoid hard coding values in error code checking. Maybe those const are already defined somewhere ?

@peterkeung
Copy link
Contributor Author

About the constants, in the context of MySQL (https://dev.mysql.com/doc/refman/5.7/en/error-messages-server.html), I don't think there is such a list of them in PHP.

@moismailzai
Copy link

+1

1 similar comment
@dfearnley
Copy link

+1

@ernestob
Copy link

This looks good 👍

@andrerom andrerom added this to the 2017.10 milestone Oct 17, 2017
@andrerom andrerom merged commit 7d92d36 into ezsystems:master Oct 17, 2017
@andrerom
Copy link
Contributor

andrerom commented Oct 17, 2017

@peterkeung Didn't see this before after merge, but it might be a minor issue not to call parent::__construct( $message ) as Exception::__construct() then won't get called.

Would you be able to do a followup fix?

@peterkeung
Copy link
Contributor Author

peterkeung commented Oct 17, 2017

@andrerom That was the entire challenge of preserving the use of ezcBaseException (because with ezcBaseException as the parent, the $code cannot be set through the constructor), but I think we are good here, because:

  • The exception is thrown
  • You can catch eZDBException, ezcBaseException, or Exception
  • By setting the $this->message and $this->code properties, we are making the necessary information available to be acted upon

Thus, we don't need to call parent::__construct( $message) unless I'm missing something?

@andrerom
Copy link
Contributor

andrerom commented Oct 17, 2017

@peterkeung I was thinking about these parts:

		$this->file = __FILE__; // of throw clause
		$this->line = __LINE__; // of throw clause
		$this->trace = debug_backtrace();
		$this->string = StringFormat($this);

@peterkeung
Copy link
Contributor Author

@andrerom (Disclaimer: this is beyond my exception knowledge, so I apologize if I have a fundamental misunderstanding.) In my tests, I still got the expected results with $e->getFile(), $e->getLine(), $e->getTrace(), $e->getTraceAsString() in the caught exception object. We couldn't just move those lines into eZDBException or we'd get wrong info.

@andrerom
Copy link
Contributor

In my tests, I still got the expected results with $e->getFile(), $e->getLine(), $e->getTrace(), $e->getTraceAsString() in the caught exception object.

ok then we are good, my doubt was if these would be wrong now.

We couldn't just move those lines into eZDBException or we'd get wrong info.

was not implying that, only that we'd somehow call Exception::__construct().
But if the info is right, then no need. done.

}
else
{
$this->message = htmlspecialchars( $message, ENT_QUOTES, 'UTF-8' );
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick: it is bad practice to html-encode data on data-creation. Data should be escaped when it is displayed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
9 participants