Commit a3a86aa5 authored by Steve Müller's avatar Steve Müller

Merge pull request #718 from Tobion/retry-logic

Identify retryable transaction errors
parents 3dcf0cc8 43f92753
......@@ -45,6 +45,10 @@ abstract class AbstractMySQLDriver implements Driver, ExceptionConverterDriver,
public function convertException($message, DriverException $exception)
{
switch ($exception->getErrorCode()) {
case '1213':
return new Exception\DeadlockException($message, $exception);
case '1205':
return new Exception\LockWaitTimeoutException($message, $exception);
case '1050':
return new Exception\TableExistsException($message, $exception);
......
......@@ -46,6 +46,9 @@ abstract class AbstractPostgreSQLDriver implements Driver, ExceptionConverterDri
public function convertException($message, DriverException $exception)
{
switch ($exception->getSQLState()) {
case '40001':
case '40P01':
return new Exception\DeadlockException($message, $exception);
case '0A000':
// Foreign key constraint violations during a TRUNCATE operation
// are considered "feature not supported" in PostgreSQL.
......
......@@ -46,6 +46,14 @@ abstract class AbstractSQLAnywhereDriver implements Driver, ExceptionConverterDr
public function convertException($message, DriverException $exception)
{
switch ($exception->getErrorCode()) {
case '-306':
case '-307':
case '-684':
return new Exception\DeadlockException($message, $exception);
case '-210':
case '-1175':
case '-1281':
return new Exception\LockWaitTimeoutException($message, $exception);
case '-100':
case '-103':
case '-832':
......
......@@ -40,6 +40,10 @@ abstract class AbstractSQLiteDriver implements Driver, ExceptionConverterDriver
*/
public function convertException($message, DriverException $exception)
{
if (strpos($exception->getMessage(), 'database is locked') !== false) {
return new Exception\LockWaitTimeoutException($message, $exception);
}
if (strpos($exception->getMessage(), 'must be unique') !== false ||
strpos($exception->getMessage(), 'is not unique') !== false ||
strpos($exception->getMessage(), 'are not unique') !== false ||
......
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\DBAL\Exception;
/**
* Exception for a deadlock error of a transaction detected in the driver.
*
* @author Tobias Schultze <http://tobion.de>
* @link www.doctrine-project.org
* @since 2.6
*/
class DeadlockException extends ServerException implements RetryableException
{
}
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\DBAL\Exception;
/**
* Exception for a lock wait timeout error of a transaction detected in the driver.
*
* @author Tobias Schultze <http://tobion.de>
* @link www.doctrine-project.org
* @since 2.6
*/
class LockWaitTimeoutException extends ServerException implements RetryableException
{
}
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\DBAL\Exception;
/**
* Marker interface for all exceptions where retrying the transaction makes sense.
*
* @author Tobias Schultze <http://tobion.de>
* @link www.doctrine-project.org
* @since 2.6
*/
interface RetryableException
{
}
......@@ -24,6 +24,8 @@ abstract class AbstractDriverTest extends DbalTestCase
const EXCEPTION_TABLE_EXISTS = 'Doctrine\DBAL\Exception\TableExistsException';
const EXCEPTION_TABLE_NOT_FOUND = 'Doctrine\DBAL\Exception\TableNotFoundException';
const EXCEPTION_UNIQUE_CONSTRAINT_VIOLATION = 'Doctrine\DBAL\Exception\UniqueConstraintViolationException';
const EXCEPTION_DEADLOCK = 'Doctrine\DBAL\Exception\DeadlockException';
const EXCEPTION_LOCK_WAIT_TIMEOUT = 'Doctrine\DBAL\Exception\LockWaitTimeoutException';
/**
* The driver mock under test.
......
......@@ -134,6 +134,12 @@ class AbstractMySQLDriverTest extends AbstractDriverTest
array('1569', null, null),
array('1586', null, null),
),
self::EXCEPTION_DEADLOCK => array(
array('1213', null, null),
),
self::EXCEPTION_LOCK_WAIT_TIMEOUT => array(
array('1205', null, null),
),
);
}
}
......@@ -101,6 +101,10 @@ class AbstractPostgreSQLDriverTest extends AbstractDriverTest
self::EXCEPTION_UNIQUE_CONSTRAINT_VIOLATION => array(
array(null, '23505', null),
),
self::EXCEPTION_DEADLOCK => array(
array(null, '40001', null),
array(null, '40P01', null),
),
);
}
}
......@@ -93,6 +93,16 @@ class AbstractSQLAnywhereDriverTest extends AbstractDriverTest
array('-193', null, null),
array('-196', null, null),
),
self::EXCEPTION_DEADLOCK => array(
array('-306', null, null),
array('-307', null, null),
array('-684', null, null),
),
self::EXCEPTION_LOCK_WAIT_TIMEOUT => array(
array('-210', null, null),
array('-1175', null, null),
array('-1281', null, null),
),
);
}
}
......@@ -73,6 +73,9 @@ class AbstractSQLiteDriverTest extends AbstractDriverTest
array(null, null, 'is not unique'),
array(null, null, 'are not unique'),
),
self::EXCEPTION_LOCK_WAIT_TIMEOUT => array(
array(null, null, 'database is locked'),
),
);
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment