@watarukuraです。 社内向け管理画面のMadrasのPHPバージョンを7.4から8.0へメジャーバージョンアップしました。 苦労話を共有させてください。
PHP7.4->8.0移行計画
まず、ドキュメントを書きました。
これにより、なんでこれをやってるんだっけ、ということを明確にします。
公式ドキュメントを熟読します。 下位互換性のない変更点
↓こんな移行作戦を立てました。
- ローカル開発用のDocker環境がPHP8.0で動くようにする
- ローカルでのテストが全部通るようにする
- CI用のDockerイメージをPHP8.0でも作って、手動でCIを回してテストが全部通るようにする
- 通常のCIはPHP7.4で動作させておき、本番deploy後にPHP8.0イメージでCIを回す
- stg環境にPHP8.0を導入して動作検証する
- 本番移行手順を作る
- 本番移行する
ポイント
PHP7.4とPHP8.0でcomposer.jsonを共通化する
"require": { "php": "^7.4||^8.0",
毎回composer.jsonを修正して検証用ブランチにpushしてGitHub Actionsのworkflow_dispatchで手動実行していたのです。(修正しないとcomposer validateで落ちる) 上記の修正後は検証用の専用ブランチではなくdefaultブランチで検証できるようになったので、本番へのdeploy後にGitHub Actionsのworkflow_run機能でPHP8.0用のCIを回すところが自動化できました。
@ 演算子は、致命的なエラー を隠さなくなりました
急にテストでHTTPステータス400エラーを期待している箇所で500エラーが出るようになりました。
ログを見ると、array_keys(): Argument #1 ($array) must be of type array, null given
というエラーが出ています。
$hasX = @count(array_keys($hash['x'])) !== 1;
なるほど。
- $hasX = @count(array_keys($hash['x'])) !== 1; + $hasX = count(array_keys($hash['x'] ?? [])) !== 1;
\Symfony\Component\Process\Processは生きてるのに\Swoole\Coroutineは死んでる
これが一番のハマりどころで、移行が1週間遅れました...。
バッチ処理の高速化のために\Symfony\Component\Process\Processを使ってマルチプロセスで実行している箇所があるのですが、$process->isRunning()
がtrueを返すのにSwooleのCoroutineがWARNING swoole_signalfd_event_callback(): read from signalfd failed, Error: Resource temporarily unavailable[11]
って警告を吐いて死んでしまっていました。
(余談ですが、signalfdってsignal用のファイルディスクリプタなんですね。初めて知りました Man page of SIGNALFD)
原因として推測したのは、PHP8.0ではexit()は例外と同じ挙動になるようで、この辺りはSwooleでもIssueがいくつか上がっていました。
移行を始めた時点はSwoole4.8.1を使用していたのですが、バグにハマっている間に4.8.2がでており、ReleaseNoteをみると、Fixed cannot exit directly when a fatal error occurs in PHP8 environment
との記述が!
しめしめとバージョンアップしてみたものの、残念ながら同じ警告がでました...。
SwooleのソースもPHPのソースも読んでも何もわからん(CとC++を読んで原因までたどり着ける力量がない)ので、ワークアラウンドとしてtimeoutを設定するようにしました。 これで、異常時は検知できるので、拾ってリトライすれば良さそうです。
$process = new Process(['ls', '-lsa']); + $process->setTimeout(3600); $process->start(); while ($process->isRunning()) { // waiting for process to finish + // check if the timeout is reached + $process->checkTimeout(); usleep(200000); } echo $process->getOutput();
まとめ
2021/10/01から開始したPHP8.0移行プロジェクトは、本日11/26の動作検証を以て完了となります。 PHP8.0に起因していない障害(cronの記述ミス...)こそあったものの、日次のバッチ処理は正常に稼働しており、まずは成功と言えるのではないでしょうか。
さて、昨日11/25に待望のPHP8.1が来ました! Fibersにより、いよいよPHPで非同期処理が公式サポートされるようになります。 PHP8.0移行プロジェクトの終了と同時に、PHP8.1移行プロジェクトの幕開けとなりました。 (その前にLaravel6->8移行プロジェクトもあるのですが...) では、次のプロジェクト完了報告でお会いしましょう...!