diff --git a/src/Backoff/ArrayBackoff.php b/src/Backoff/ArrayBackoff.php new file mode 100644 index 0000000..bd73395 --- /dev/null +++ b/src/Backoff/ArrayBackoff.php @@ -0,0 +1,57 @@ +lastMillisecond = (int) array_pop($this->milliseconds); + } + + /** + * Sleep until the next execution. + */ + public function sleep(): void + { + $ms = (int) (array_shift($this->milliseconds) ?? $this->lastMillisecond); + + if ($ms === 0) { + return; + } + + usleep($ms * 1000); + } + + /** + * Get the next backoff for logging, etc. + * @return int next backoff + */ + public function nextBackoff(): int + { + return (int) Arr::first($this->milliseconds, default: $this->lastMillisecond); + } +} diff --git a/src/Functions.php b/src/Functions.php index 713b392..23f9ec1 100644 --- a/src/Functions.php +++ b/src/Functions.php @@ -15,6 +15,7 @@ use Hyperf\Collection\Arr; use Hyperf\Context\ApplicationContext; use Hyperf\Stringable\StrCache; +use Hyperf\Support\Backoff\ArrayBackoff; use Throwable; /** @@ -60,14 +61,19 @@ function env($key, $default = null) /** * Retry an operation a given number of times. * - * @param float|int $times + * @param float|int|int[] $times * @param int $sleep millisecond * @throws Throwable */ function retry($times, callable $callback, int $sleep = 0) { $attempts = 0; - $backoff = new Backoff($sleep); + if (is_array($times)) { + $backoff = new ArrayBackoff($times); + $times = count($times); + } else { + $backoff = new Backoff($sleep); + } beginning: try { diff --git a/tests/BackoffTest.php b/tests/BackoffTest.php index 67d8159..4b7ef91 100644 --- a/tests/BackoffTest.php +++ b/tests/BackoffTest.php @@ -33,4 +33,15 @@ public function testBackoff() $this->assertGreaterThanOrEqual(1, $secondTick); $this->assertLessThanOrEqual(3 * $firstTick, $secondTick); } + + public function testCustomBackoff() + { + $backoff = new Backoff\ArrayBackoff([1, 200]); + $backoff->sleep(); + $this->assertSame(200, $backoff->nextBackoff()); + + $backoff = new Backoff\ArrayBackoff([1, 2.2]); + $backoff->sleep(); + $this->assertSame(2, $backoff->nextBackoff()); + } } diff --git a/tests/FunctionTest.php b/tests/FunctionTest.php index a861251..85da16f 100644 --- a/tests/FunctionTest.php +++ b/tests/FunctionTest.php @@ -61,6 +61,24 @@ public function testRetry() } } + public function testRetryTimesAsArray() + { + $this->expectException(RetryException::class); + $result = 0; + $milliSeconds[] = microtime(true); + try { + retry([100, 200], function () use (&$result, &$milliSeconds) { + ++$result; + $milliSeconds[] = microtime(true); + throw new RetryException('Retry Test'); + }); + } finally { + $this->assertSame(3, $result); + $this->assertGreaterThanOrEqual(0.1, $milliSeconds[2] - $milliSeconds[1]); + $this->assertGreaterThanOrEqual(0.2, $milliSeconds[3] - $milliSeconds[2]); + } + } + public function testOneTimesRetry() { $this->expectException(RetryException::class);