Laravelの設定・キャッシュ構造を整理する
このメモ書きは、projectの本番環境調査で判明したLaravelのキャッシュ周りの仕組みを、メモ書きとして整理したものである。
きっかけは以下のコマンド結果。全サイトでconfig.phpが存在しないことが確認された。
for site in siteA siteB siteC siteD siteE siteF staging; do
echo "── $site"
ls -la /var/www/html/$site/bootstrap/cache/config.php 2>&1
done
── siteA
ls: cannot access '.../siteA/bootstrap/cache/config.php': No such file or directory
── siteB
ls: cannot access '.../siteB/bootstrap/cache/config.php': No such file or directory
(以下同様)
つまりどのサイトもconfig:cacheが一度も実行されていない状態であることが確認できる。
Laravelには3種類の別物がある
「静的/動的」という軸で並んでいるわけではなく、そもそも別の仕組みが3つ存在している。
| 種類 | 何をキャッシュするか | 誰が指示するか |
|---|---|---|
| config:cache | .env由来の設定値 | artisanコマンド(全設定を丸ごと) |
| route:cache | ルート定義 | artisanコマンド(全ルートを丸ごと) |
| Cache::(ファサード) | 開発者が選んだ任意のデータ(DB集計、API結果等) | コードで個別に指定 |
config:cacheとroute:cacheは「アプリの骨格」を固める、全部か無かの一括操作である。中間の粒度は存在しない。
Cache::は「このデータだけ」「この時間だけ」と開発者が個別に選ぶ方式である。書かなければ発動しない。デフォルトの動的データは、明示的にCache::で囲わない限りキャッシュを完全に無視して毎回DBを叩く。
「準静的」という表現よりも、「一括固定(config/route)」と「個別選択式(Cache::)」という区分で覚えたほうが、後で説明するときにブレない。
Laravelのキャッシュ構造(設定値の流れ)
一括固定系(骨格キャッシュ)
| コマンド | 固めるもの | 効果 |
|---|---|---|
| config:cache | config/*.php全部 | env()直読みが死ぬ。config()経由は生きる |
| route:cache | routes/*.php全部 | ルート追加してもキャッシュ再生成まで反映されない |
| optimize:clear | 上記を削除 | 生成はしない。次のリクエストからまた直読みに戻る |
projectの現状は、デプロイのたびにoptimize:clearを打つ運用になっている。常にキャッシュ無し、configもroute直読みの状態である。速いか遅いかは別として、事故りにくい状態と言える。
config:cacheを実行した状態でenv()を直接呼ぶと、値がnullになる、もしくは意図しない値になる。config:cache運用に切り替える際は、コード中にenv()の直読みが残っていないか事前に洗い出しておく必要がある。
個別選択系(データキャッシュ)
Cache::remember()などをコードに明示的に書いた場所だけ発動する。書いていなければDBに毎回聞きに行く仕組みになっている。config:cacheとは無関係の仕組みである。
キャッシュが生成される瞬間
意図的にコマンドを打った瞬間だけ生成される。放っておいて勝手に生成されることは無い。
存在するコマンドは以下の通り。
| コマンド | やること |
|---|---|
| config:cache | config生成(すでにあれば上書き) |
| route:cache | route生成 |
| view:cache | Bladeテンプレートのコンパイル済みキャッシュ生成 |
| optimize | 上記3つ(+event:cache)をまとめて生成 |
| config:clear | config削除のみ |
| route:clear | route削除のみ |
| optimize:clear | 上記の削除版まとめ(現状projectが使っているもの) |
optimize:cacheというコマンドは存在しない。生成のまとめコマンドはoptimizeであり、cacheという語は付かない。紛らわしいので注意が必要。
生成タイミングは以下の3パターンしかない。
1. 誰かがSSHで手打ちする
2. デプロイスクリプトに記述されていて自動実行される(project環境には記述無し)
3. php artisan serveではなく本番のnginx+php-fpm構成の場合、.envが読める状態であればキャッシュが無くてもアプリは動作する。何らかのタイミングで自動的にキャッシュが生成される仕組みはLaravelには存在していない
現状のprojectは、誰もconfig:cacheを実行していないためキャッシュが存在しない状態である。今後意図的に実行しない限り、この状態が継続する。
今回の作業への適用
- 外部サービスのトークンは.envに記述する
- config/services.phpに該当項目を追加し、env()の呼び出しをそこに閉じ込める
- コントローラやジョブからはconfig('services.example.token')の形で読み出す。理由は「今動くから」ではなく、将来config:cache運用に切り替わっても壊れないようにするためである
- DBの件数表示や反響データの表示は今回の話とは別の論点である。表示にラグがある場合は、Cache::の使用箇所を別途洗い出す必要がある
まとめ
| 論点 | 結論 |
|---|---|
| config:cache / route:cacheとは何か | アプリの骨格を固める一括操作。中間の粒度は無い |
| Cache::ファサードとは何か | 開発者が個別に指定するデータキャッシュ。書かなければ発動しない |
| デフォルトの挙動 | Cache::を書かない限り常にDBを直接叩く(キャッシュignore) |
| キャッシュはいつ生成されるか | 誰かが明示的にコマンドを打った瞬間のみ。自動生成の仕組みは無い |
| projectの現状 | config:cacheが一度も実行されておらず、キャッシュ無しの状態が続いている |
| 今回の対応方針 | トークンは.env→config/services.php→config()経由で読む構成にする |
あとがき
Laravelの設定周りは「.envを読む場所がconfig/*.phpに限定される」という前提を押さえていないと、config:cacheを導入した瞬間に思わぬ箇所で値がnullになるという事故につながる。今回のprojectのように、そもそもキャッシュを使わない運用であれば事故は起きにくいが、将来的にconfig:cacheを導入する可能性があるなら、env()の直読みを今のうちにconfig()経由へ寄せておくのが安全である。