diff --git a/README.md b/README.md index 54c28d3..981a0dd 100644 --- a/README.md +++ b/README.md @@ -26,17 +26,17 @@ $callableThatMightFail = function (int $arg1, int $arg2): int { // Allows you to call the callable with parameters and retry its execution in case an exception is thrown. // You can access the return value of the callable (3 in this case). -$returnValue = Retry::call($callableThatMightFail, 1, 2); +$returnValue = Retry::configure()->call($callableThatMightFail, 1, 2); // By default: // - The callable is retried twice (i.e. max three executions). If it still fails, the last error is rethrown. -// - Retries have a 300 milliseconds delay between them. +// - Retries have a no delay between them. // - Every \Throwable will trigger the retry logic, i.e. both \Exception and \Error. // You can adjust the retry logic like this: $retryingCallable = Retry::configure() - ->setMaxRetries(5) - ->setDelayInMs(100) - ->setRetryableExceptions(\RuntimeException::class) // other failures like \TypeError will not be retried + ->maxRetries(5) + ->delayInMs(100) + ->retryOnSpecificExceptions(\RuntimeException::class) // other failures like \TypeError will not be retried ->decorate($callableThatMightFail) ; $returnValue = $retryingCallable(1, 2); diff --git a/src/Retry.php b/src/Retry.php index 5d3df9f..60ca610 100644 --- a/src/Retry.php +++ b/src/Retry.php @@ -2,6 +2,8 @@ namespace Tobion\Retry; +use Doctrine\DBAL\Exception\RetryableException; + /** * Simple retry entry point providing shortcuts. * @@ -18,23 +20,17 @@ public static function configure(): RetryConfigurator } /** - * Returns a callable that decorates the given operation that should be retried on failure. - */ - public static function decorate(callable $callable): RetryingCallable - { - return (new RetryConfigurator())->decorate($callable); - } - - /** - * Executes the passed callable and its arguments with the default retry behavior. - * - * @see RetryConfigurator + * Executes the passed callable and its arguments with the a preconfigured retry behavior suitable for Doctrine database transactions. * * @return mixed The return value of the passed callable */ - public static function call(callable $callable, ...$arguments) + public static function onDoctrineExceptionWith2Retries300MsDelay(callable $callable, ...$arguments) { - return (new RetryConfigurator())->call($callable, ...$arguments); + return self::configure() + ->maxRetries(2) + ->delayInMs(300) + ->retryOnSpecificExceptions(RetryableException::class) + ->call($callable, ...$arguments); } /** diff --git a/src/RetryConfigurator.php b/src/RetryConfigurator.php index 75b3e9c..5a61ec6 100644 --- a/src/RetryConfigurator.php +++ b/src/RetryConfigurator.php @@ -12,7 +12,7 @@ * * @author Tobias Schultze */ -class RetryConfigurator +final class RetryConfigurator { /** * @var string[] @@ -27,20 +27,18 @@ class RetryConfigurator /** * @var int */ - private $delayInMs; + private $delayInMs = 0; /** * Configures the retry logic. By default: * * - The callable is retried twice (i.e. max three executions). If it still fails, the last error is rethrown. - * - Retries have a 300 milliseconds delay between them. - * - Every \Throwable will trigger the retry logic, i.e. both exceptions and errors. + * - Retries have a no delay between them. + * - Every \Throwable will trigger the retry logic, i.e. both \Exception and \Error. */ - public function __construct(int $maxRetries = 2, int $delayInMs = 300, string $exceptionToRetry = \Throwable::class) + public function __construct(int $maxRetries = 2) { $this->maxRetries = $maxRetries; - $this->delayInMs = $delayInMs; - $this->setRetryableExceptions($exceptionToRetry); } /** @@ -53,7 +51,7 @@ public function __construct(int $maxRetries = 2, int $delayInMs = 300, string $e * * @return $this */ - public function setRetryableExceptions(string $exceptionClass, string ...$moreExceptionClasses): self + public function retryOnSpecificExceptions(string $exceptionClass, string ...$moreExceptionClasses): self { array_unshift($moreExceptionClasses, $exceptionClass); $this->retryableExceptionClasses = $moreExceptionClasses; @@ -66,7 +64,7 @@ public function setRetryableExceptions(string $exceptionClass, string ...$moreEx * * @return $this */ - public function setMaxRetries(int $maxRetries): self + public function maxRetries(int $maxRetries): self { $this->maxRetries = $maxRetries; @@ -80,41 +78,13 @@ public function setMaxRetries(int $maxRetries): self * * @return $this */ - public function setDelayInMs(int $milliseconds): self + public function delayInMs(int $milliseconds): self { $this->delayInMs = $milliseconds; return $this; } - /** - * Returns the exception classes/interfaces to catch and retry on. - * - * If empty, every exception will trigger the retry logic. - * - * @return string[] - */ - public function getRetryableExceptions(): array - { - return $this->retryableExceptionClasses; - } - - /** - * Returns the maximum number of retries. - */ - public function getMaxRetries(): int - { - return $this->maxRetries; - } - - /** - * Returns the delay between retries in milliseconds. - */ - public function getDelayInMs(): int - { - return $this->delayInMs; - } - /** * Returns a callable that decorates the given operation that should be retried on failure. */ @@ -123,7 +93,7 @@ public function decorate(callable $operation): RetryingCallable $handlers = []; // we can skip the handler in this default case - if ([\Throwable::class] !== $this->retryableExceptionClasses) { + if ([] !== $this->retryableExceptionClasses && [\Throwable::class] !== $this->retryableExceptionClasses) { $handlers[] = new RethrowNonRetryableExceptions(...$this->retryableExceptionClasses); } diff --git a/tests/RetryConfiguratorTest.php b/tests/RetryConfiguratorTest.php deleted file mode 100644 index dfaf2b4..0000000 --- a/tests/RetryConfiguratorTest.php +++ /dev/null @@ -1,41 +0,0 @@ -assertSame(2, $retryConfigurator->getMaxRetries()); - $this->assertSame(300, $retryConfigurator->getDelayInMs()); - $this->assertSame([\Throwable::class], $retryConfigurator->getRetryableExceptions()); - } - - public function testConfiguratorConstructor(): void - { - $retryConfigurator = new RetryConfigurator(1, 100, \Exception::class); - - $this->assertSame(1, $retryConfigurator->getMaxRetries()); - $this->assertSame(100, $retryConfigurator->getDelayInMs()); - $this->assertSame([\Exception::class], $retryConfigurator->getRetryableExceptions()); - } - - public function testConfiguratorSetters(): void - { - $retryConfigurator = new RetryConfigurator(); - $retryConfigurator - ->setMaxRetries(1) - ->setDelayInMs(100) - ->setRetryableExceptions(\Exception::class, \Error::class) - ; - - $this->assertSame(1, $retryConfigurator->getMaxRetries()); - $this->assertSame(100, $retryConfigurator->getDelayInMs()); - $this->assertSame([\Exception::class, \Error::class], $retryConfigurator->getRetryableExceptions()); - } -} diff --git a/tests/RetryFunctionalityTest.php b/tests/RetryFunctionalityTest.php index 32eddfb..bf119b1 100644 --- a/tests/RetryFunctionalityTest.php +++ b/tests/RetryFunctionalityTest.php @@ -42,7 +42,7 @@ public function testRetrySucceeds(): void public function testRetrySucceedsWithTwoRetryableExceptions(): void { $retry = (new RetryConfigurator(4)) - ->setRetryableExceptions(TestExceptionToRetry::class,TestDifferentException::class) + ->retryOnSpecificExceptions(TestExceptionToRetry::class,TestDifferentException::class) ->decorate([new TestExamplesToRetry(4), 'useTwoExceptions']) ; @@ -65,7 +65,7 @@ public function testRetryFailsAfterMaxRetries(): void public function testNoRetryWhenNotRetryableError(): void { - $retry = (new RetryConfigurator(10, 300, TestExceptionToRetry::class))->decorate( + $retry = (new RetryConfigurator(10))->retryOnSpecificExceptions(TestExceptionToRetry::class)->decorate( [new TestExamplesToRetry(), 'retryableErrorFollowedByOtherError'] ); @@ -82,7 +82,7 @@ public function testRetryDelays(): void { $start = microtime(true); - $returnValue = (new RetryConfigurator(2, 500))->call($this->getCallable(2)); + $returnValue = (new RetryConfigurator(2))->delayInMs(500)->call($this->getCallable(2)); $elapsedTimeInMs = (microtime(true) - $start) * 1000; diff --git a/tests/RetryTest.php b/tests/RetryTest.php index b0bee87..a8dcc1c 100644 --- a/tests/RetryTest.php +++ b/tests/RetryTest.php @@ -13,14 +13,4 @@ public function testConfigure(): void { $this->assertInstanceOf(RetryConfigurator::class, Retry::configure()); } - - public function testDecorate(): void - { - $this->assertInstanceOf(RetryingCallable::class, Retry::decorate(function () { return 42; })); - } - - public function testCallPassesOnArgumentsAndReturnsCallableReturnValue(): void - { - $this->assertSame(42, Retry::call(function (int $arg1, int $arg2) { return $arg1 + $arg2; }, 40, 2)); - } }