Skip to content

Commit

Permalink
allow to silence exceptions when exponential backoff disabled
Browse files Browse the repository at this point in the history
  • Loading branch information
deminy committed Mar 24, 2021
1 parent 26310e6 commit 29b63fb
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 37 deletions.
20 changes: 11 additions & 9 deletions src/ExponentialBackoff.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@
*/
class ExponentialBackoff
{
public const DEFAULT_MAX_ATTEMPTS = 4;

public const TYPE_MICROSECONDS = 1;
public const TYPE_SECONDS = 2;

Expand All @@ -54,7 +56,7 @@ class ExponentialBackoff
/**
* @var int
*/
protected $maxAttempts = 4;
protected $maxAttempts = self::DEFAULT_MAX_ATTEMPTS;

/**
* @var int
Expand Down Expand Up @@ -176,17 +178,17 @@ public function setRetryCondition(AbstractRetryCondition $retryCondition): self
*/
protected function retry($result, ?\Exception $e): bool
{
if ($this->getCurrentAttempts() < $this->getMaxAttempts()) {
if ($this->getRetryCondition()->met($result, $e)) {
return false;
}

$this->sleep();
if ($this->getRetryCondition()->met($result, $e)) {
return false;
}

return true;
if ($this->getCurrentAttempts() >= $this->getMaxAttempts()) {
return false;
}

return false;
$this->sleep();

return true;
}

/**
Expand Down
85 changes: 57 additions & 28 deletions tests/unit/CustomizedConditionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,32 +32,40 @@
*/
class CustomizedConditionTest extends TestCase
{
protected const MAX_ATTEMPTS = ExponentialBackoff::DEFAULT_MAX_ATTEMPTS;

public function dataBackoff(): array
{
return [
[
'maxAttempts' => 1,
'message' => 'Maximum # of attempts is 1 (exponential backoff disabled)',
],
[
'maxAttempts' => 2,
'message' => 'Maximum # of attempts is 2',
],
[
'maxAttempts' => self::MAX_ATTEMPTS,
'message' => 'Maximum # of attempts is 4',
],
];
}

/**
* The $backoff object in this test is the same as the one in the next method self::testUnthrowableException(),
* except that method $backoff->throwable() returns TRUE.
*
* @dataProvider dataBackoff
* @covers \CrowdStar\Backoff\AbstractRetryCondition::throwable()
* @covers \CrowdStar\Backoff\ExponentialBackoff::run()
*/
public function testThrowableException()
public function testThrowableException(int $maxAttempts)
{
$helper = (new Helper())->setException(Exception::class)->setExpectedFailedAttempts(4);
$backoff = (new ExponentialBackoff(
new class extends AbstractRetryCondition {
public function throwable(): bool
{
// This tells the caller to throw out the exception when finally failed.
return true;
}
public function met($result, ?Exception $e): bool
{
return (empty($e) || (!($e instanceof Exception)));
}
}
));
$helper = (new Helper())->setException(Exception::class)->setExpectedFailedAttempts(self::MAX_ATTEMPTS);

$this->expectException(Exception::class); // Next function call will through out an exception.
$backoff->run(
$this->getBackoff($maxAttempts, false)->run(
function () use ($helper) {
return $helper->getValueAfterExpectedNumberOfFailedAttemptsWithExceptionsThrownOut();
}
Expand All @@ -68,31 +76,52 @@ function () use ($helper) {
* The $backoff object in this test is the same as the one in the previous method self::testThrowableException(),
* except that method $backoff->throwable() returns FALSE.
*
* @dataProvider dataBackoff
* @covers \CrowdStar\Backoff\AbstractRetryCondition::throwable()
* @covers \CrowdStar\Backoff\ExponentialBackoff::run()
*/
public function testUnthrowableException()
public function testUnthrowableException(int $maxAttempts)
{
$helper = (new Helper())->setException(Exception::class)->setExpectedFailedAttempts(self::MAX_ATTEMPTS);
$this->getBackoff($maxAttempts, true)->run(
function () use ($helper) {
return $helper->getValueAfterExpectedNumberOfFailedAttemptsWithExceptionsThrownOut();
}
);

$this->addToAssertionCount(1); // Since there is no assertions in this test, we manually add the count by 1.
}

/**
* @param bool $silenceWhenFailed To hide or throw out the exception when finally failed.
*/
protected function getBackoff(int $maxAttempts, bool $silenceWhenFailed): ExponentialBackoff
{
$helper = (new Helper())->setException(Exception::class)->setExpectedFailedAttempts(4);
$backoff = (new ExponentialBackoff(
new class extends AbstractRetryCondition {
new class($silenceWhenFailed) extends AbstractRetryCondition {
protected $silenceWhenFailed;
protected $throwable = true;
public function __construct(bool $silenceWhenFailed)
{
$this->silenceWhenFailed = $silenceWhenFailed;
}
public function throwable(): bool
{
// This tells the caller NOT to throw out the exception when finally failed.
return false;
// This tells the caller to hide or throw out the exception when finally failed.
return $this->throwable;
}
public function met($result, ?Exception $e): bool
{
return (empty($e) || (!($e instanceof Exception)));
if (empty($e) || (!($e instanceof Exception))) {
return true;
}
$this->throwable = !$this->silenceWhenFailed;
return false;
}
}
));
$backoff->setMaxAttempts($maxAttempts);

$this->addToAssertionCount(1); // Since there is no assertions in this test, we manually add the count by 1.
$backoff->run(
function () use ($helper) {
return $helper->getValueAfterExpectedNumberOfFailedAttemptsWithExceptionsThrownOut();
}
);
return $backoff;
}
}

0 comments on commit 29b63fb

Please sign in to comment.