retry

The retry function attempts to execute the given callback until the given maximum attempt threshold is met. If the callback does not throw an exception, its return value will be returned. If the callback throws an exception, it will automatically be retried. If the maximum attempt count is exceeded, the exception will be thrown:
<?php return retry(5, function () {
}, 100);
<?php     protected function repositoryExists()

    {

        return retry(2, fn () => $this->migrator->repositoryExists(), 0, function ($e) {

            try {

                if ($e->getPrevious() instanceof SQLiteDatabaseDoesNotExistException) {

                    return $this->createMissingSqliteDatabase($e->getPrevious()->path);

                }



                $connection = $this->migrator->resolveConnection($this->option('database'));



                if (

                    $e->getPrevious() instanceof PDOException &&

                    $e->getPrevious()->getCode() === 1049 &&

                    in_array($connection->getDriverName(), ['mysql', 'mariadb'])) {

                    return $this->createMissingMysqlDatabase($connection);

                }



                return false;

            } catch (Throwable) {

                return false;

            }

        });

    }
<?php         $shouldRetry = null;



        return retry($this->tries ?? 1, function ($attempt) use ($method, $url, $options, &$shouldRetry) {

            try {

                return tap($this->newResponse($this->sendRequest($method, $url, $options)), function ($response) use ($attempt, &$shouldRetry) {

                    $this->populateResponse($response);



                    $this->dispatchResponseReceivedEvent($response);



                    if (! $response->successful()) {

                        try {

                            $shouldRetry = $this->retryWhenCallback ? call_user_func($this->retryWhenCallback, $response->toException(), $this) : true;

                        } catch (Exception $exception) {

                            $shouldRetry = false;



                            throw $exception;

                        }



                        if ($this->throwCallback &&

                            ($this->throwIfCallback === null ||

                             call_user_func($this->throwIfCallback, $response))) {

                            $response->throw($this->throwCallback);

                        }



                        if ($attempt < $this->tries && $shouldRetry) {

                            $response->throw();

                        }



                        if ($this->tries > 1 && $this->retryThrow) {

                            $response->throw();

                        }

                    }

                });

            } catch (ConnectException $e) {

                $this->dispatchConnectionFailedEvent(new Request($e->getRequest()));



                throw new ConnectionException($e->getMessage(), 0, $e);

            }

        }, $this->retryDelay ?? 100, function ($exception) use (&$shouldRetry) {

            $result = $shouldRetry ?? ($this->retryWhenCallback ? call_user_func($this->retryWhenCallback, $exception, $this) : true);



            $shouldRetry = null;



            return $result;

        });

    }
<?php     public function testItCanExceedThresholdWhenSpecifyingDurationAsDateTime()

    {

        retry(2, function () {

            Carbon::setTestNow(Carbon::now());



            $input = new StringInput('foo');

            $called = false;



            $kernel = $this->app[Kernel::class];

            $kernel->command('foo', fn () => null);

            $kernel->whenCommandLifecycleIsLongerThan(Carbon::now()->addSecond()->addMillisecond(), function () use (&$called) {

                $called = true;

            });



            $kernel->handle($input, new ConsoleOutput);



            $this->assertFalse($called);



            Carbon::setTestNow(Carbon::now()->addSeconds(1)->addMillisecond());



            $kernel->terminate($input, 21);



            $this->assertTrue($called);

        }, 500);

    }



    public function testItCanStayUnderThresholdWhenSpecifyingDurationAsDateTime()
<?php     {

        Sleep::fake();



        $attempts = retry(2, function ($attempts) {

            if ($attempts > 1) {

                return $attempts;

            }



            throw new RuntimeException;

        }, 100);




        $this->assertEquals(2, $attempts);
<?php     {

        Sleep::fake();



        $attempts = retry(3, function ($attempts) {

            if ($attempts > 2) {

                return $attempts;

            }



            throw new RuntimeException;

        }, function ($attempt, $exception) {

            $this->assertInstanceOf(RuntimeException::class, $exception);



            return $attempt * 100;

        });




        $this->assertEquals(3, $attempts);
<?php     {

        Sleep::fake();



        $attempts = retry(2, function ($attempts) {

            if ($attempts > 1) {

                return $attempts;

            }



            throw new RuntimeException;

        }, 100, function ($ex) {

            return true;

        });




        $this->assertEquals(2, $attempts);
<?php     {

        $this->expectException(RuntimeException::class);



        retry(2, function ($attempts) {

            if ($attempts > 1) {

                return $attempts;

            }



            throw new RuntimeException;

        }, 100, function ($ex) {

            return false;

        });

    }



    public function testRetryWithBackoff()
<?php     {

        Sleep::fake();



        $attempts = retry([50, 100, 200], function ($attempts) {

            if ($attempts > 3) {

                return $attempts;

            }



            throw new RuntimeException;

        });




        $this->assertEquals(4, $attempts);
<?php     {

        Sleep::fake();



        $attempts = retry(2, function ($attempts) {

            if ($attempts > 1) {

                return $attempts;

            }



            throw new Error('This is an error');

        }, 100);




        $this->assertEquals(2, $attempts);