Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
D
doctrine-dbal
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Tomáš Trávníček
doctrine-dbal
Commits
224b76e1
Commit
224b76e1
authored
Jun 08, 2015
by
Tobias Schultze
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
remove the retry logic which can be provided by another package
parent
af90b420
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
0 additions
and
278 deletions
+0
-278
RetryWrapper.php
lib/Doctrine/DBAL/RetryWrapper.php
+0
-134
RetryWrapperTest.php
tests/Doctrine/Tests/DBAL/RetryWrapperTest.php
+0
-144
No files found.
lib/Doctrine/DBAL/RetryWrapper.php
deleted
100644 → 0
View file @
af90b420
<?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
;
use
Doctrine\DBAL\Exception\RetryableException
;
/**
* Wraps a database operation, represented as a callable, in retry logic.
*
* It is best practice to retry transactions that are aborted because of deadlocks
* or timeouts. Such errors are caused by lock contention and you often can design
* your application to reduce the likeliness that such an error occurs. But it's
* impossible to guarantee that such error conditions will never occur. So when you
* have to ensure the application does not fail in edge cases or high load, retrying
* transactions in case of such errors is actually a must.
*
* The class implements the retry logic for you by re-executing your callable in
* case of temporary database errors where retrying the failed transaction after a
* short delay usually resolves the problem. Just wrap your database transaction
* or single query in this class and invoke it. You can also pass arguments when
* invoking the wrapper which will be passed through to the underlying callable.
*
* @author Tobias Schultze <http://tobion.de>
* @link www.doctrine-project.org
* @since 2.6
*/
class
RetryWrapper
{
/**
* The database operation to execute that can be retried on failure.
*
* @var callable
*/
private
$callable
;
/**
* Maximum number of retries.
*
* @var integer
*/
private
$maxRetries
;
/**
* Delay between retries in milliseconds.
*
* @var integer
*/
private
$retryDelay
;
/**
* Actual number of retries.
*
* @var integer|null
*/
private
$retries
;
/**
* Constructor to wrap a callable.
*
* @param callable $callable The database operation to execute that can be retried on failure.
* @param integer $maxRetries Maximum number of retries.
* @param integer $retryDelay Delay between retries in milliseconds to give the blocking
* transaction time to finish.
*/
public
function
__construct
(
$callable
,
$maxRetries
=
3
,
$retryDelay
=
100
)
{
$this
->
callable
=
$callable
;
$this
->
maxRetries
=
$maxRetries
;
$this
->
retryDelay
=
$retryDelay
;
}
/**
* Returns the number of retries used.
*
* @return integer|null The number of retries used or null if wrapper has not been invoked yet
*/
public
function
getRetries
()
{
return
$this
->
retries
;
}
/**
* Executes the callable and retries it in case of a temporary database error.
*
* The callable is only re-executed for temporary database errors where retrying
* the failed transaction after a short delay usually resolves the problem. Such
* errors are for example deadlocks and lock wait timeouts. Internally the raised
* exception must implement the marker interface RetryableException. Other exceptions
* like syntax errors or constraint violations will not cause the callable to be
* re-executed.
*
* All arguments given will be passed through to the wrapped callable.
*
* @return mixed The return value of the wrapped callable
*
* @throws \Exception If an exception has been raised where retrying makes no sense
* or a RetryableException after max retries has been reached.
*/
public
function
__invoke
()
{
$this
->
retries
=
0
;
$args
=
func_get_args
();
do
{
try
{
return
call_user_func_array
(
$this
->
callable
,
$args
);
}
catch
(
RetryableException
$e
)
{
if
(
$this
->
retries
<
$this
->
maxRetries
)
{
$this
->
retries
++
;
usleep
(
$this
->
retryDelay
*
1000
);
}
else
{
throw
$e
;
}
}
}
while
(
true
);
}
}
tests/Doctrine/Tests/DBAL/RetryWrapperTest.php
deleted
100644 → 0
View file @
af90b420
<?php
namespace
Doctrine\Tests\DBAL
;
use
Doctrine\DBAL\DBALException
;
use
Doctrine\DBAL\Driver\DriverException
;
use
Doctrine\DBAL\Exception\DeadlockException
;
use
Doctrine\DBAL\Exception\RetryableException
;
use
Doctrine\DBAL\RetryWrapper
;
class
RetryWrapperTest
extends
\Doctrine\Tests\DbalTestCase
{
public
function
testConstructor
()
{
$retryWrapper
=
new
RetryWrapper
(
$this
->
getRetryCallable
());
$this
->
assertNull
(
$retryWrapper
->
getRetries
());
}
public
function
testWithoutRetry
()
{
$retryWrapper
=
new
RetryWrapper
(
$this
->
getRetryCallable
(
0
));
$this
->
assertSame
(
'return-value'
,
$retryWrapper
());
$this
->
assertSame
(
0
,
$retryWrapper
->
getRetries
());
}
public
function
testExecuteOnceWithZeroMaxRetries
()
{
$retryWrapper
=
new
RetryWrapper
(
$this
->
getRetryCallable
(
0
),
0
);
$this
->
assertSame
(
'return-value'
,
$retryWrapper
(),
'Callable must be executed when max retries is 0'
);
$this
->
assertSame
(
0
,
$retryWrapper
->
getRetries
());
}
public
function
testExecuteOnceWithNegativeMaxRetries
()
{
$retryWrapper
=
new
RetryWrapper
(
$this
->
getRetryCallable
(
0
),
-
3
);
$this
->
assertSame
(
'return-value'
,
$retryWrapper
(),
'Callable must be executed when max retries is negative'
);
$this
->
assertSame
(
0
,
$retryWrapper
->
getRetries
());
}
public
function
testRetrySucceedsWithDefaultMaxRetries
()
{
$retryWrapper
=
new
RetryWrapper
(
$this
->
getRetryCallable
(
2
));
$this
->
assertSame
(
'return-value'
,
$retryWrapper
());
$this
->
assertSame
(
2
,
$retryWrapper
->
getRetries
());
}
public
function
testRetryFailsAfterMaxRetries
()
{
$retryWrapper
=
new
RetryWrapper
(
$this
->
getRetryCallable
(
2
),
1
);
try
{
$retryWrapper
();
$this
->
fail
(
'Wrapper should rethrow exception when max retries has been reached.'
);
}
catch
(
RetryableException
$e
)
{
$this
->
assertSame
(
'Deadlock'
,
$e
->
getMessage
());
$this
->
assertSame
(
1
,
$retryWrapper
->
getRetries
());
}
}
public
function
testNoRetryOnGenericError
()
{
$retryWrapper
=
new
RetryWrapper
(
array
(
new
RetryCallableExample
(),
'retryableErrorFollowedByGenericError'
),
5
);
try
{
$retryWrapper
();
$this
->
fail
(
'Wrapper should rethrow exception when not retryable.'
);
}
catch
(
DBALException
$e
)
{
$this
->
assertSame
(
'Constraint violation'
,
$e
->
getMessage
());
$this
->
assertSame
(
1
,
$retryWrapper
->
getRetries
(),
'One retry, then abort'
);
}
}
public
function
testInvokeWithParams
()
{
$retryWrapper
=
new
RetryWrapper
(
__NAMESPACE__
.
'\RetryCallableExample::staticMethodWithParams'
);
$this
->
assertSame
(
'foobar'
,
$retryWrapper
(
'foo'
,
'bar'
));
}
private
function
getRetryCallable
(
$succeedAfterCalls
=
0
)
{
$callableObject
=
new
RetryCallableExample
(
$succeedAfterCalls
);
return
array
(
$callableObject
,
'succeedAsConfigured'
);
}
}
class
RetryCallableExample
{
private
$succeedAfterCalls
;
private
$executionCount
=
0
;
public
function
__construct
(
$succeedAfterCalls
=
0
)
{
$this
->
succeedAfterCalls
=
$succeedAfterCalls
;
}
public
function
succeedAsConfigured
()
{
$this
->
executionCount
++
;
if
(
$this
->
executionCount
>
$this
->
succeedAfterCalls
)
{
return
'return-value'
;
}
throw
new
DeadlockException
(
'Deadlock'
,
new
DummyDriverException
()
);
}
public
function
retryableErrorFollowedByGenericError
()
{
$this
->
executionCount
++
;
if
(
$this
->
executionCount
>
1
)
{
throw
new
DBALException
(
'Constraint violation'
);
}
throw
new
DeadlockException
(
'Deadlock'
,
new
DummyDriverException
()
);
}
public
static
function
staticMethodWithParams
(
$param1
,
$param2
)
{
return
$param1
.
$param2
;
}
}
class
DummyDriverException
extends
\Exception
implements
DriverException
{
public
function
getErrorCode
()
{
return
'code'
;
}
public
function
getSQLState
()
{
return
'state'
;
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment