LayerX エンジニアブログ

LayerX の エンジニアブログです。

パフォーマンス改善事例: クライアントストレージとデータのオーバーフェッチ問題 #LayerXテックアドカレ

この記事は、LayerX Tech Advent Calendar 2024 の 17 日目の記事です。

tech.layerx.co.jp

こんにちは、バクラク事業部エンジニアの ktr です。特定のお客さま環境で発生した珍しいエラーと、その調査・解決までの一連の流れをご紹介します。本記事を通じて、同様の問題やパフォーマンス異常に遭遇した際のヒントや知見になれば幸いです。

特定テナントからのお問い合わせ

あるお客さまから、「月末月初になると、画面の操作ができなくなる」とのご報告をいただきました。ピーク期間となる月末月初以外では特に問題なく利用できている点が特徴的で、当初はサーバー側の負荷増大やネットワーク環境の影響を疑いました。しかし、バックエンドのアクセスログや HAR ファイル (お客さま環境での HTTP 通信の記録) を確認する限り、レスポンスやネットワーク速度には大きな遅延や異常は見つかりませんでした。また、バクラクの推奨利用環境も満たしており、サーバー側の問題は疑いにくい状況でした。

フロントエンドの調査

バックエンドに問題がなさそうであることから、次にフロントエンドの調査に移りました。コンソールログを確認すると、アクセスのピーク期間に Uncaught QuotaExceededError: Failed to execute 'setItem' on 'Storage': Setting the value of 'xxx' exceeded the quota. というエラーがいくつか記録されていることに気づきました。これは、ウェブストレージ API にデータを書き込もうとした際、容量制限により書き込みが失敗したことを示すエラーです。今回のケースではセッションストレージの容量制限に引っかかっているようでした。

一般的にセッションストレージは約 5MB 前後の容量制限があり、これを超えるデータを書き込むと例外が発生します。プロダクト機能の一部にセッションストレージを使っているため、データの書き込みに失敗するとこの機能が使えなくなったり、不安定になる可能性がありました。

developer.mozilla.org

また、当該お客さま以外のテナントでは発生していなかったことから、何らかの理由で当該テナントだけ極端に大きなデータがセッションストレージに保存されている可能性が浮上しました。

問題箇所の特定

さらなるコードリーディングおよび検証を行ったところ、以下の問題点が判明しました。

  1. セッションストレージへの過剰なデータ格納
    機能の実装において、API のレスポンスをそのままセッションストレージへ保存していました。必要なデータはレスポンスのごく一部のみでしたが、余剰なデータをすべて保存していたため、データ量が膨大になっていました。
  2. GraphQL クエリでのオーバーフェッチ
    GraphQL を利用している箇所で、本来不要なリソースまで冗長に取得してしまっていました。そのためレスポンスそのものが大きくなり、月末月初といったデータ処理件数がピークとなるタイミングでレスポンスサイズが肥大化、結果として 5MB 超のデータを保存しようとして、Quota Exceeded Exception を引き起こしていました。今回のケースでは、一度の API 呼び出して実に 6.5MB のデータがレスポンスとして返っており、そのほとんどが不要なデータでした。

これらの事実をもとに、開発環境での再現を行ったところ、期待どおり一部の機能が正常に動作しなくなることが確認できました。たとえばボタンを押しても反応がない、画面遷移が行われないといった症状が表面化していました。

また、今回問題が起きていたストレージはローカルストレージではなくセッションストレージであったため、ブラウザやタブを閉じることで保存されたデータが消えるという特性がありました。

これらにより、ピーク期間にはレスポンスデータが膨大になり、セッションストレージに保存されることで容量制限を超えてしまい、一部機能が利用できなくなるものの、ピークを過ぎると次第にレスポンスデータが小さくなり、かつブラウザを閉じることでセッションストレージのデータが消えるため、問題が一時的に解消されるという現象が起きていたことがわかりました。

解決策

問題特定後、以下の 2 点の改善を行いました。

  1. セッションストレージに必要十分なデータのみ保存する
    「何が本当に必要なデータなのか」を精査した結果、実際には特定リソースの ID 情報さえあれば後続の UI 描画やロジック実行が可能でした。これにより、セッションストレージに格納するデータ量を大幅に削減できました。
  2. GraphQL クエリの最適化 (オーバーフェッチの解消)
    本当に必要なフィールドのみを取得するようにクエリを見直しました。不要なフィールドを取り除くことで、レスポンスサイズを劇的に軽減し、結果としてセッションストレージへの保存データ量も削減されました。また、副次的な効果としてネットワーク通信量の削減、バックエンドの負荷軽減にもつながりました。

結果と学び

上記の改善により、セッションストレージが利用できなくなる問題が解消され、ピーク期間でも機能の劣化を防ぐことができるようになりました。 この問題を通じて得られた知見は次のとおりです。

  • クライアントサイドストレージの容量制限を念頭に置く
    開発時に容易に見落としがちな点として、ブラウザ側のストレージ容量制限があります。特にセッションストレージやローカルストレージを利用する場合は、保存するデータ量に注意が必要です。もし補助的な機能のみにセッションストレージを使っている場合、ストレージの逼迫によりコア機能を壊さないようにします。
  • 不要なオーバーフェッチを避ける
    必要なデータだけが取得されるようにします。必要なデータはプロダクトの成長により変わることがあるため、定期的な見直しを行い、不要なデータを取得しないようにします。また、監視などを通じてオーバーフェッチが発生していないかを確認することも重要です。GraphQL では柔軟なデータフェッチが強みである反面、簡単にオーバーフェッチできてしまうため、特に注意が必要です。

改善としては非常にシンプルで、基本的なことでもあるのですが、全ての箇所で徹底することの難しさを改めて思い知りました…。

まとめ

今回の事例は、単なるバックエンド処理やネットワーク遅延が原因ではなく、フロントエンド実装上の問題に起因していた点で興味深いケースでした。ストレージ容量制限とオーバーフェッチの組み合わせが最終的に機能面での問題を引き起こしていたことは、今後の実装やメンテナンスにおいて大きな学びとなりました。

今後もお客さまにとって圧倒的に使いやすいプロダクトを提供するため、機能開発だけでなくパフォーマンス改善をはじめとした安定性を高める取り組みを継続していきます。もしこういった領域に興味をお持ちの方がいらっしゃいましたら、ぜひ一度お話ししましょう!

明日の記事もお楽しみに!