Daisuke's TechBlog

日々の仕事で得た技術的なことを書いてきます

Laravelでデータベース接続のリトライ

2017/12/26追記

業務で組み込んでみましたが、他と組み合わせるとうまく動かず。。 もし、解決策が分かったら更新します。。


久々の投稿です。あまり情報が載ってなかったので。。

目的

LaravelでDB接続リトライを指定回数行えるようにしたい

背景

今度お仕事でLaravelというPHPフレームワークを使うことになりました。
要望として、DB接続ができなかった場合に何回かリトライして欲しいとのことでした。

本題

いろいろ試行錯誤はしましたが、結論だけ書きます。
Laravelにはファサードという仕組みがあります。
ファサード 5.5 Laravel

公式サイトによると、「db」という文字列をファサードを利用して設定することにより、
DatabaseManagerクラスの差し替えが可能らしい。
ファサード 5.5 Laravel

さらに、実際にDB接続を失敗させてみたExceptionのスタックトレースを見ると、
DatabaseManager#__call というメソッドがreadのときもwriteのときも呼ばれている模様。

overrideしてこのメソッドでリトライしてあげれば良さげ。
(ここはちょっと自信ありません。試してみた結果だし、アンスコ2個付いてるメソッド名だし。。(でもpublicだし))

で書いたclassがこちら。

<?php

namespace App;

use Exception;
use Illuminate\Database\DatabaseManager;
use Illuminate\Support\Facades\Log;

class DBManager extends DatabaseManager
{

    function __call($method, $parameters)
    {
        $i = 0;
        while (true) {
            try {
                $i++;
                return parent::__call($method, $parameters);
            } catch (Exception $e) {
                Log::warning("Connection failed...");
                if ( $i >= 3 ) {
                    throw $e;
                }
                Log::warning("Retry!");
            }
        }
        return null;
    }
}

(3ってマジックナンバーだけどお仕事の時はちゃんとconfigから読むようにしますよ。)

まだクラスを作っただけなのでLaravelとは関連付けられてません。
今回は AppServiceProvider#register にて関連付け(差し替え)を行いました。

    public function register()
    {
        $this->app->singleton('db', function ($app) {
            return new DBManager($app, $app['db.factory']);
        });

    }

singletonというメソッド指定はLaravel内部でそういう指定の仕方をしているため。
(ここも公式ドキュメントに載せてほしいなあ。。) github.com

DB接続を失敗させてみます。 ※今回はDebugBarで確認しています。 f:id:hiromitsu-daisuke:20171125202559p:plain

ちゃんとリトライするようになりました。

まとめ

本当にこれで正しいのか分からないのでツッコミ待ちですが、
一旦はこれで要件は満たせそうです。