N+1問題の雪崩を防ぐ:LaravelのStrict Modeを強制する

N+1クエリ問題は、データベースのパフォーマンスを著しく低下させます。これは、レコードのリストを取得した後、それらをループしてリレーションにアクセスしようとした際に発生します。リレーションを事前にロードし忘れていることが原因です。

例えば、ダッシュボードに50件の請求書を表示するとします。クライアント名を表示するために、それらをループで回します。ここで with メソッドを使うのを忘れていました。Eloquentは請求書のために1つのクエリを実行します。その後、各クライアントに対して50個の個別のクエリを実行します。

ローカル開発環境では、51個のクエリはわずか10ミリ秒で終わります。そのため、気づくことはないでしょう。しかし、本番環境では、大量のトラフィックによってこれが雪崩(アバランチ)へと変わります。データベースのコネクションプールを使い果たし、サーバーをダウンさせてしまいます。

コードレビューだけに頼ることはできません。アーキテクチャ上のガードレールが必要です。

Laravelはこの問題を解決するために Strict Mode を提供しています。アプリケーションレベルで preventLazyLoading を使用できます。Laravelはデータベースのリレーションを監視します。ローカル開発中やテスト中に遅延ロード(lazy load)が検出されると、例外をスローします。これにより、即座にコードを修正することを強制できます。

設定方法:

AppServiceProvider で安全メカニズムを設定します。これはローカルおよびテスト環境に対して行ってください。本番環境では、アプリをクラッシュさせるのではなく、エラーをログに記録するようにすべきです。

boot メソッド内で以下の3つの設定を使用します:

  • Model::preventLazyLoading: N+1クエリを防止します。
  • Model::preventSilentlyDiscardingAttributes: Mass assignment(一括代入)の失敗を防止します。
  • Model::preventAccessingMissingAttributes: リレーションの欠落によるメモリリークを防止します。

本番環境では handleLazyLoadingViolationUsing を使用します。これにより、違反をSlackやロギングサービスに記録できます。ユーザー体験を損なうことなく、可視性を確保できます。

Strict Modeを強制することで、最適化のプロセスをパイプラインの初期段階へと移すことができます。N+1クエリはテストを通過できなくなります。その結果、コードベースは最適化された状態を保ち、データベースの高速性も維持されます。

出典: https://dev.to/iprajapatiparesh/stop-n1-avalanches-enforce-laravel-strict-mode-2oop