PHPで例外が発生したらリトライするような処理をigorw/retryで簡単に書いてみる

Web APIを相手にするようなコードを書いていると、リトライ処理は必ず現れる課題です。

先日も似たようなことがあったので、ちょっとした処理を書いて対処したのですが、これはなんかライブラリがあるんじゃないかなと思って調べてみると、Packagistでよくダウンロードされていそうなライブラリがありました。

packagist.org

Githubのスターも結構付いているので、これを試してみます。

インストール

Packagistで公開されているので、もちろんcomposerでインストールしますが、開発版のようなのでdev-masterを指定してcomposer requireします。

$ composer require igorw/retry dev-master

サンプルコード

使い方は簡単で、retry関数にリトライ回数と実行したい処理を無名関数やcallableとして渡すだけです。

動作確認用に、2回目までは例外を投げて3回目に成功するメソッドを作って確認してみます。

<?php

require __DIR__.'/vendor/autoload.php';

use function igorw\retry;

class A
{
    private static $retries = 0;

    public static function hello()
    {
        if (self::$retries < 2) {
            ++self::$retries;
            echo 'retry'.PHP_EOL;
            throw new \Exception();
        }

        echo 'hello'.PHP_EOL;
    }
}

retry(2, function () {
    A::hello();
});

これを実行すると、以下のような結果になります。

$ php example.php
retry
retry
hello

2回までリトライするようになっているので、初回の実行と1回目のリトライでは例外が発生していますが、2回目のリトライ(通算3回目の実行)では処理が正常に終了していますので、想定通りの動作となっています。

また、指定された回数リトライしても例外が発生するとigorw\FailingTooHardExceptionが発生するようになっています。

ソースを読んでみる

すごくシンプルな処理なので、ソースを追うのも楽です。

retry/retry.php at master · igorw/retry · GitHub

無限ループにして指定回数を超えたらbreakするような実装を想像していましたが、まさかのgotoでした。こういう使い方もあるんですね。

このライブラリでできないこと

単純にリトライするだけなら、これを使えばとても簡単にリトライ処理が書けるような気がしますが、WebAPI相手だとExponential Backoffのようにリトライ時にスリープを入れたり、特定の例外だった場合のみリトライするようなことはできません。

リトライ時の処理をもっと柔軟に定義したい場合は、以下のライブラリもよさそうです。

github.com

この記事では紹介のみにとどめますが、このライブラリも試してみたいと思っています。