はじめに

最近、PHP 8.2.18とMySQL 8.0.36を使用したアプリケーションで「Packets out of order. Expected 1 received 0.」というエラーに遭遇しました。

このエラーの詳細とその対処法について記します。

環境

  • PHPバージョン: 8.2.18
  • MySQLバージョン: 8.0.36
  • サーバー設定: php-fpm

エラー発生時の状況

エラーが発生したのは、アプリケーションへのアクセスが一定期間途切れた以下のような状況。
1. 前日の夕方までアプリにアクセス。
2. 翌日朝、アプリにアクセスすると「Packets out of order. Expected 1 received 0.」のエラー。
3. その後、何度かアクセスを試みると正常にアクセスできるように。

エラーの内容

おそらくphp-fpmが古い接続を保持している一方で、MySQLサーバー側でその接続がタイムアウトにより切断されることで発生。

以下流れ

  1. php-fpmに古い接続が保持: 前日のアクセスで確立された接続がphp-fpmに保持される。
  2. MySQLの接続タイムアウト: 長時間アクセスがない場合、MySQL側で接続がタイムアウトされる(デフォルトでは8時間)。
  3. 翌日のアクセス時にエラー: 朝に再度アクセスすると、php-fpmが保持している古い接続を使用しようとするためエラーが発生。
  4. 新しい接続の確立: しばらくすると新しい接続が確立され、エラーが解消。

対処法

php-fpmのrequest_terminate_timeoutの設定を調整する

/etc/php-fpm.d/www.confファイル内のrequest_terminate_timeoutの値を、MySQLのwait_timeoutの値よりも短く設定する。

例えば、wait_timeoutがデフォルトで8時間である場合、request_terminate_timeoutを6時間に設定することで、php-fpmが古い接続を保持し続けることを防げる。

設定例

# /etc/php-fpm.d/www.conf
request_terminate_timeout = 21600s # 6時間

その他の対処法

その他、以下のようなやり方でも対処可能。

  • 永続接続の使用を避ける: 永続接続 (PDO::ATTR_PERSISTENT) を無効にすることで、永続接続を使用しない。
  • 接続の再試行: エラーが発生した場合に、接続を再試行するように実装を変更
  • 設定の調整: MySQLサーバーの設定で、wait_timeoutやinteractive_timeoutの値を可能な範囲で伸ばす。

まとめ

まだ修正が入っていないようなので、今現在では上記のようなやり方でエラー対処する必要がありそうです。

参考リンク:
PHP Bug #81335
GitHub Pull Request #13618