概要
- Lumenを使用したWebアプリを運用している中で、Queue(DB)を使用
- DBは、Azure SQL Databaseを使用
- Azure SQL Databaseでは一時的に負荷が上がると、強制的に接続を切断しDB側の再構成が走る
DB接続をキャッシュしたり、コネクションプーリングなどを使用している場合、古いDB接続が利用されるとDB接続エラーが発生する
docs.microsoft.com
- LumenのQueue(DB)では、DB接続をキャッシュして使用しているため、デフォルトのまま使用しているとDB負荷が上がったときにDB接続エラーが連続して発生するようになる
yasutomo.hatenablog.com
前提
- lumen:5.5.7
- PHP:7.1
- php artisan queue:work のコマンド実行時
詳細
Illuminate\Queue\DatabaseQueueを継承したクラスを作成
<?php
namespace App\Libs;
use Illuminate\Queue\DatabaseQueue;
class CustomDatabaseQueue extends DatabaseQueue
{
const RETRY_WAIT_SEC = 10;
const RETRY_SQL_STATE = ["08S01", "HY000"];
@see
@param
@return
@throws
public function pop($queue = null)
{
$queue = $this->getQueue($queue);
try {
try {
$this->database->beginTransaction();
} catch (\Throwable $e) {
\Log::info($e);
if (property_exists($e, 'errorInfo')) {
$sqlstate = $e->errorInfo[0];
$sqlerrcd = $e->errorInfo[1];
\Log::info("SQLSTATE::" . $sqlstate);
\Log::info("SQLERRCD::" . $sqlerrcd);
if (in_array($sqlstate, self::RETRY_SQL_STATE)) {
\Log::info('キュー管理テーブル参照時にDB接続エラーが発生したので、10秒後にDB再接続します。');
sleep(10);
$this->database->reconnect();
$this->database->beginTransaction();
\Log::info('キュー管理テーブルの再接続に成功しました。');
} else {
throw $e;
}
} else {
throw $e;
}
}
if ($job = $this->getNextAvailableJob($queue)) {
return $this->marshalJob($queue, $job);
}
$this->database->commit();
} catch (\Throwable $e) {
$this->database->rollBack();
throw $e;
}
}
}
Illuminate\Queue\Connectors\DatabaseConnectorを継承したクラスを作成
<?php
namespace App\Libs;
use Illuminate\Queue\Connectors\DatabaseConnector;
@package
class CustomDatabaseConnector extends DatabaseConnector
{
@see
@param
@return
public function connect(array $config)
{
return new CustomDatabaseQueue(
$this->connections->connection($config['connection'] ?? null),
$config['table'],
$config['queue'],
$config['retry_after'] ?? 60
);
}
}
App\Providers\AppServiceProviderの改修
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use App\Libs\CustomDatabaseConnector;
class AppServiceProvider extends ServiceProvider
{
@return
public function register()
{
$queueManager = $this->app['queue'];
$queueManager->addConnector('customdatabase', function () {
return new CustomDatabaseConnector($this->app['db']);
});
}
}
api/config/queue.php
<?php
return [
'default' => env('QUEUE_DRIVER', 'customdatabase'),
'connections' => [
'customdatabase' => [
'connection' => 'hoge',
'driver' => 'customdatabase',
'table' => 'QueueJobs',
'queue' => 'default',
'retry_after' => 60,
]
],
];
artisanコマンドを変更
- php artisan queue:work customdatabase