Skip to content

Commit

Permalink
Added a BackOffRunner that runs code with a back off strategy.
Browse files Browse the repository at this point in the history
  • Loading branch information
frankdejonge committed Sep 17, 2023
1 parent 1fe47e1 commit c203818
Show file tree
Hide file tree
Showing 2 changed files with 109 additions and 0 deletions.
53 changes: 53 additions & 0 deletions src/BackOffRunner.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<?php

declare(strict_types=1);

namespace EventSauce\BackOff;

use Throwable;

/**
* # This one is for the lazy people.
*
* This class is introduced for extremely lazy developers that can't even be bothered
* to write the tiniest amount of bootstrapping code, ever. LAZY! YOU'RE LAZY! BOOOO!
*/
class BackOffRunner
{
private BackOffStrategy $strategy;

/**
* @var class-string<Throwable>
*/
private string $retryOn;

/**
* @param class-string<Throwable> $retryOn
*/
public function __construct(BackOffStrategy $strategy, string $retryOn = Throwable::class)
{
$this->strategy = $strategy;
$this->retryOn = $retryOn;
}

/**
* @return mixed
* @throws Throwable
*/
public function run(callable $task)
{
$tries = 0;

while(true) {
try {
return $task();
} catch (Throwable $exception) {
if ( ! $exception instanceof $this->retryOn) {
throw $exception;
}

$this->strategy->backOff(++$tries, $exception);
}
}
}
}
56 changes: 56 additions & 0 deletions src/BackOffRunnerTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<?php

declare(strict_types=1);

namespace EventSauce\BackOff;

use Exception;
use LogicException;
use PHPUnit\Framework\TestCase;
use Throwable;

class BackOffRunnerTest extends TestCase
{
/**
* @test
*/
public function running_a_callable_with_backoff(): void
{
$runner = new BackOffRunner(new NoWaitingBackOffStrategy(5));
$tries = 0;
$exception = null;

try {
$runner->run(function () use (&$tries) {
$tries++;
throw new Exception('oh no, this is terrible');
});
} catch (Throwable $throwable) {
$exception = $throwable;
}

self::assertEquals(6, $tries); // retry 5 failures + 1 final fail
self::assertEquals(new Exception('oh no, this is terrible'), $exception);
}
/**
* @test
*/
public function not_matching_the_exception_type(): void
{
$runner = new BackOffRunner(new NoWaitingBackOffStrategy(5), LogicException::class);
$tries = 0;
$exception = null;

try {
$runner->run(function () use (&$tries) {
$tries++;
throw new Exception('oh no, this is terrible');
});
} catch (Throwable $throwable) {
$exception = $throwable;
}

self::assertEquals(1, $tries); // retry 5 failures + 1 final fail
self::assertEquals(new Exception('oh no, this is terrible'), $exception);
}
}

0 comments on commit c203818

Please sign in to comment.