From 6c23728c85d570d5fa1efcd785a0c3c542ac0f82 Mon Sep 17 00:00:00 2001 From: Ondrej Vlach Date: Thu, 18 Jan 2024 20:49:33 +0100 Subject: [PATCH] feat: add retry client trait for api requests --- .../Remote/RetryingFailClientTrait.php | 59 +++++++++++++++++++ .../Remote/RetryingFailClientTraitTest.php | 56 ++++++++++++++++++ 2 files changed, 115 insertions(+) create mode 100644 src/Service/Remote/RetryingFailClientTrait.php create mode 100644 tests/Service/Remote/RetryingFailClientTraitTest.php diff --git a/src/Service/Remote/RetryingFailClientTrait.php b/src/Service/Remote/RetryingFailClientTrait.php new file mode 100644 index 0000000..dce7653 --- /dev/null +++ b/src/Service/Remote/RetryingFailClientTrait.php @@ -0,0 +1,59 @@ + $catchableExceptions + * @param LoggerInterface $logger + * @param callable $callback + * @return mixed + * @throws Exception + */ + protected function retryingFailRequest( + int $count, + float $sleep, + array $catchableExceptions, + LoggerInterface $logger, + callable $callback + ): mixed { + for ($i = 0; ; $i++) { + try { + return $callback(); + } catch (Exception $e) { + foreach ($catchableExceptions as $exceptionClass) { + if ($e instanceof $exceptionClass) { + $logger->error("transport: fail request retrying... got catchable exception", [ + 'exception' => $e, + 'try' => $i + ]); + + usleep((int) ($sleep * 1_000_000)); + + if ($i == $count) { + throw $e; + } + + continue 2; + } + } + + throw $e; + } + } + + // phpstan fail + return null; + } +} diff --git a/tests/Service/Remote/RetryingFailClientTraitTest.php b/tests/Service/Remote/RetryingFailClientTraitTest.php new file mode 100644 index 0000000..e945864 --- /dev/null +++ b/tests/Service/Remote/RetryingFailClientTraitTest.php @@ -0,0 +1,56 @@ +callCount = 0; + } + + public function testSuccess() { + $trait = new class { + use RetryingFailClientTrait { + retryingFailRequest as public; // make the method public + } + }; + + + $result = $trait->retryingFailRequest(2, 0, [\RuntimeException::class], $this->getLogger(), function () { + $this->callCount = $this->callCount + 1; + return 'foo'; + }); + + $this->assertEquals(1, $this->callCount); + $this->assertEquals('foo', $result); + } + public function testRetyingFail() { + $trait = new class { + use RetryingFailClientTrait { + retryingFailRequest as public; // make the method public + } + }; + + + try { + $trait->retryingFailRequest(2, 0, [\RuntimeException::class], $this->getLogger(), function () { + $this->callCount = $this->callCount + 1; + throw new \RuntimeException("test"); + }); + } catch (\RuntimeException) { + // do nothing + } + + $this->assertEquals(3, $this->callCount); + } +}