From e7e52f8e692e765c68458b79222ac6160c12d781 Mon Sep 17 00:00:00 2001 From: Marco Pfeiffer Date: Sun, 15 Mar 2020 15:43:51 +0100 Subject: [PATCH] change error codes for proxy errors --- README.md | 8 ++--- src/RdsDataDriver.php | 14 +++++++++ src/RdsDataException.php | 4 +-- tests/RdsDataExceptionTest.php | 56 +++++++++++++++++++++++++++++----- 4 files changed, 68 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index beefcf8..ffcb646 100644 --- a/README.md +++ b/README.md @@ -221,9 +221,9 @@ If an [Aurora Serverless] is paused, you'll get this error message: > Communications link failure The last packet sent successfully to the server was 0 milliseconds ago. > The driver has not received any packets from the server. -I mapped this error message to [error code `2002`] which is normally a socket connection error -but this will cause the dbal's `Doctrine\DBAL\Driver\AbstractMySQLDriver::convertException` to map this error -to an `Doctrine\DBAL\Exception\ConnectionException` which existing application might already handle gracefully. +I mapped this error message to error code `6000` (server errors are 1xxx and client errors 2xxx). +It'll also be converted to dbal's `Doctrine\DBAL\Exception\ConnectionException` +which existing application might already handle gracefully. But the most important thing is that you can catch and handle it in your application to better tell your user that the database is paused and will probably be available soon. @@ -243,4 +243,4 @@ to better tell your user that the database is paused and will probably be availa [timeout setting of guzzle]: http://docs.guzzlephp.org/en/stable/request-options.html#timeout [mysql error documentation]: https://dev.mysql.com/doc/refman/5.6/en/server-error-reference.html [amazon developer forum]: https://forums.aws.amazon.com/thread.jspa?threadID=317595 -[error code `2002`]: https://dev.mysql.com/doc/refman/5.6/en/client-error-reference.html#error_cr_connection_error +[error code `2002`]: https://dv.mysql.com/doc/refman/5.6/en/client-error-reference.html#error_cr_connection_error diff --git a/src/RdsDataDriver.php b/src/RdsDataDriver.php index 8cac172..07b62ef 100644 --- a/src/RdsDataDriver.php +++ b/src/RdsDataDriver.php @@ -6,6 +6,8 @@ use Aws\Handler\GuzzleV6\GuzzleHandler; use Aws\RDSDataService\RDSDataServiceClient; use Doctrine\DBAL\Driver; +use Doctrine\DBAL\Driver\DriverException; +use Doctrine\DBAL\Exception AS DBALException; use GuzzleHttp\Client; class RdsDataDriver extends Driver\AbstractMySQLDriver @@ -49,4 +51,16 @@ public function getName(): string { return 'rds-data'; } + + public function convertException($message, DriverException $exception) + { + switch ($exception->getErrorCode()) { + case '6000': + return new DBALException\ConnectionException($message, $exception); + + default: + return parent::convertException($message, $exception); + } + } + } diff --git a/src/RdsDataException.php b/src/RdsDataException.php index 2c64aa8..5f64d54 100644 --- a/src/RdsDataException.php +++ b/src/RdsDataException.php @@ -99,8 +99,8 @@ class RdsDataException extends AbstractDriverException . "|(*:1701,ER_TRUNCATE_ILLEGAL_FK)Cannot truncate a table referenced in a foreign key constraint \\(.*\\)" // this error is custom and specific to aurora serverless proxies - // i fake it to indicate a socket connection error so implementations will correctly identify a connection error - . "|(*:2002,CR_CONNECTION_ERROR)Communications link failure.*" + // I decided to use 6xxx error codes for proxy errors since server errors are 1xxx and client errors 2xxx + . "|(*:6000,PR_CONNECTION_ERROR)Communications link failure.*" . ")$#s"; // note the PCRE_DOTALL modifier diff --git a/tests/RdsDataExceptionTest.php b/tests/RdsDataExceptionTest.php index b821ab7..0dae4c4 100644 --- a/tests/RdsDataExceptionTest.php +++ b/tests/RdsDataExceptionTest.php @@ -3,6 +3,13 @@ namespace Nemo64\DbalRdsData\Tests; +use Doctrine\DBAL\Exception\ConnectionException; +use Doctrine\DBAL\Exception\DriverException; +use Doctrine\DBAL\Exception\ForeignKeyConstraintViolationException; +use Doctrine\DBAL\Exception\SyntaxErrorException; +use Doctrine\DBAL\Exception\TableNotFoundException; +use Doctrine\DBAL\Exception\UniqueConstraintViolationException; +use Nemo64\DbalRdsData\RdsDataDriver; use Nemo64\DbalRdsData\RdsDataException; use PHPUnit\Framework\TestCase; @@ -11,23 +18,56 @@ class RdsDataExceptionTest extends TestCase public static function messages() { return [ - ["Table 'foo.bar' doesn't exist", 1146, 'ER_NO_SUCH_TABLE'], - ["Duplicate entry 'foo' for key bar", 1062, 'ER_DUP_ENTRY'], - ["You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use", 1149, 'ER_SYNTAX_ERROR'], - ["Cannot truncate a table referenced in a foreign key constraint (foobar)", 1701, 'ER_TRUNCATE_ILLEGAL_FK'], - // this is a specific rds proxy error - ["Communications link failure\n\nThe last packet sent successfully to the server was 0 milliseconds ago. The driver has not received any packets from the server.", 2002, 'CR_CONNECTION_ERROR'], - ["Some never before seen of error", null, null], + [ + "Table 'foo.bar' doesn't exist", + 1146, + 'ER_NO_SUCH_TABLE', + TableNotFoundException::class, + ], + [ + "Duplicate entry 'foo' for key bar", + 1062, + 'ER_DUP_ENTRY', + UniqueConstraintViolationException::class, + ], + [ + "You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use", + 1149, + 'ER_SYNTAX_ERROR', + SyntaxErrorException::class, + ], + [ + "Cannot truncate a table referenced in a foreign key constraint (foobar)", + 1701, + 'ER_TRUNCATE_ILLEGAL_FK', + ForeignKeyConstraintViolationException::class, + ], + [ + "Communications link failure\n\n" + . "The last packet sent successfully to the server was 0 milliseconds ago. The driver has not received any packets from the server.", + 6000, + 'PR_CONNECTION_ERROR', + ConnectionException::class, + ], + [ + "Some never before seen of error", + null, + null, + DriverException::class, + ], ]; } /** * @dataProvider messages */ - public function testMessageParsing($message, $expectedCode, $expectedState) + public function testMessageParsing($message, $expectedCode, $expectedState, $convertedExceptionInstance) { $exception = new RdsDataException($message); $this->assertEquals($expectedCode, $exception->getErrorCode()); $this->assertEquals($expectedState, $exception->getSQLState()); + + $driver = new RdsDataDriver(); + $this->assertInstanceOf($convertedExceptionInstance, $driver->convertException($exception->getMessage(), $exception)); } }