Exponential back-offs prevent overloading an unavailable service by doubling the timeout each iteration. This class uses an exponential back-off algorithm to calculate the timeout for the next request.
This library allows doing exponential backoff in non-blocking mode in Swoole.
composer require crowdstar/exponential-backoff:~3.0.0
In following code pieces, we assume that you want to store return value of method MyClass::fetchData() in variable $result, and you want to do exponential backoff on that because something unexpected could happen when running method MyClass::fetchData().
Following code is to try to fetch some non-empty data back with method MyClass::fetchData(). This piece of code will try a few more times (by default 4) until either we get some non-empty data back, or we have reached maximum numbers of retries.
<?php
use CrowdStar\Backoff\EmptyValueCondition;
use CrowdStar\Backoff\ExponentialBackoff;
$result = (new ExponentialBackoff(new EmptyValueCondition()))->run(
function () {
return MyClass::fetchData();
}
);
?>
Following code is to try to fetch some data back with method MyClass::fetchData(), which may throw out exceptions. This piece of code will try a few more times (by default 4) until either we get some data back, or we have reached maximum numbers of retries.
NOTE: Internal PHP errors (class Error) won't trigger exponential backoff. They should be fixed manually.
<?php
use CrowdStar\Backoff\ExceptionBasedCondition;
use CrowdStar\Backoff\ExponentialBackoff;
// Allow to catch multiple types of exceptions and throwable objects.
$backoff = new ExponentialBackoff(new ExceptionBasedCondition(Exception::class, Throwable::class));
try {
$result = $backoff->run(
function () {
return MyClass::fetchData();
}
);
} catch (Throwable $t) {
// Handle the errors here.
}
?>
When method call MyClass::fetchData() finally fails with an exception caught, we can silence the exception without throwing it out by overriding method AbstractRetryCondition::throwable():
<?php
use CrowdStar\Backoff\AbstractRetryCondition;
use CrowdStar\Backoff\ExponentialBackoff;
$backoff = new ExponentialBackoff(
new class extends AbstractRetryCondition {
public function throwable(): bool
{
return false;
}
public function met($result, ?Exception $e): bool
{
return (empty($e) || (!($e instanceof Exception)));
}
}
);
$backoff->run(
function () {
return MyClass::fetchData();
}
);
?>
If needed, you can have more complex logic defined when overriding method AbstractRetryCondition::throwable().
Following code is to try to fetch some non-empty data back with method MyClass::fetchData(). This piece of code works the same as the first example, except that here it's implemented with a customized condition class instead of class \CrowdStar\Backoff\EmptyValueCondition.
<?php
use CrowdStar\Backoff\AbstractRetryCondition;
use CrowdStar\Backoff\ExponentialBackoff;
$backoff = new ExponentialBackoff(
new class extends AbstractRetryCondition {
public function met($result, ?Exception $e): bool
{
return !empty($result);
}
}
);
$result = $backoff->run(
function () {
return MyClass::fetchData();
}
);
?>
Following code is to try to fetch some data back with method MyClass::fetchData(). This piece of code works the same as the second example, except that here it's implemented with a customized condition class instead of class \CrowdStar\Backoff\ExceptionBasedCondition.
In this piece of code, we also show what options are available when doing exponential backoff with the package.
<?php
use CrowdStar\Backoff\AbstractRetryCondition;
use CrowdStar\Backoff\EmptyValueCondition;
use CrowdStar\Backoff\ExceptionBasedCondition;
use CrowdStar\Backoff\ExponentialBackoff;
$backoff = new ExponentialBackoff(new EmptyValueCondition());
$backoff = new ExponentialBackoff(new ExceptionBasedCondition());
$backoff = new ExponentialBackoff(new ExceptionBasedCondition(Exception::class, Throwable::class));
$backoff = new ExponentialBackoff(
new class extends AbstractRetryCondition {
public function met($result, ?Exception $e): bool
{
return (empty($e) || (!($e instanceof Exception)));
}
}
);
$backoff
->setType(ExponentialBackoff::TYPE_SECONDS)
->setType(ExponentialBackoff::TYPE_MICROSECONDS)
->setMaxAttempts(3)
->setMaxAttempts(4);
$result = $backoff->run(
function () {
return MyClass::fetchData();
}
);
?>
There are two ways to disable exponential backoff temporarily for code piece like following:
<?php
$result = MyClass::fetchData();
?>
First, you may disable exponential backoff temporarily by calling method \CrowdStar\Backoff\ExponentialBackoff::disable(). For example:
<?php
use CrowdStar\Backoff\EmptyValueCondition;
use CrowdStar\Backoff\ExponentialBackoff;
$backoff = new ExponentialBackoff(new EmptyValueCondition());
$backoff->disable();
$result = $backoff->run(function () {return MyClass::fetchData();});
?>
You may also disable exponential backoff temporarily by using class \CrowdStar\Backoff\NullCondition:
<?php
use CrowdStar\Backoff\ExponentialBackoff;
use CrowdStar\Backoff\NullCondition;
$result = (new ExponentialBackoff(new NullCondition()))
->setRetryCondition(new NullCondition()) // The method here is for demonstration purpose.
->run(function () {return MyClass::fetchData();});
?>
All these 3 code piece work the same, having return value of method call MyClass::fetchData() assigned to variable $result.
Sample scripts can be found under folder examples/. Before running them under CLI, please do a composer update first:
composer update -n