仕組みから学ぶ Web
発展読了目安 15 分#React#データ取得#ウォーターフォール#BFF#パフォーマンス

データをどこで取るか — 取得が一列に並ぶと、画面は足し算で遅くなる

注文の詳細画面が 2.4 秒かかっていた。API はどれも 0.8 秒で返っていた。部品がそれぞれ、自分が描かれてからデータを取りに行っていたので、3 回の待ちが順番に並んでいた。

この記事の進み方

What — 取得が一列に並ぶ仕組み

部品ごとにデータを取る書き方は、とても自然です。部品が自分の必要なものを自分で取るので、部品を別の画面に持っていっても、そのまま動きます。

ただしこの書き方には、見えない順番が入ります。

  1. 親の部品が描かれ、親のデータを取りに行く
  2. 親のデータが届くまで、子の部品は描かれない(まだ親が「読み込み中」を出している)
  3. 子が描かれて初めて、子のデータを取りに行く

こうして、部品の入れ子の深さだけ、取得が一列に並びます。この並びをウォーターフォール(滝)と呼びます。

Compare3 つのデータを、どう取るか

各部品が、自分が描かれてから取りに行く。

注文      ████████
購入者            ████████
配送状況                  ████████
        0     0.8    1.6    2.4 秒
表示まで
2.4 秒
部品の独立性
高い
通信の回数
3 回
  • –入れ子が深くなるほど、待ちが足し算で増える。
  • –どの部品も単体では正しく、コードを読んでも遅さの原因が見えない。

API の速さは同じ。取りに行く順番と場所だけが違う。

並ぶべきものと、並ばなくてよいもの

すべての取得を同時にできるわけではありません。前の結果を使って次を取るものは、順番が要ります。

  • 注文を取り、その中にある購入者の ID で購入者を取る → 順番が要る(ID が無いと呼べない)
  • 注文の番号で、注文・配送状況を取る → 同時にできる(どちらも番号だけで呼べる)

冒頭の画面は、3 つとも注文の番号だけで呼べたのに、部品の入れ子のせいで順番に並んでいました。データの依存ではなく、部品の形が順番を決めていたのが問題です。

確認 — ここまで読めたか

冒頭の画面で、取得が一列に並んだ原因はどれですか?

まず選ぶ(解答例は a〜d の記号で説明します)

一覧の中で 1 件ずつ取る

もう 1 つの典型は、一覧の各行が、自分のデータを取りに行く形です。

一覧を取る(20 件)
  → 行 1 がユーザーを取る
  → 行 2 がユーザーを取る
  → …
  → 行 20 がユーザーを取る   ← 20 回の通信

DB で言う N+1 問題が、ブラウザとサーバーの間で起きています。同時に出すので待ちは足し算になりませんが、通信の回数とサーバーの負荷は行の数だけ増えます。一覧を取るときに、必要なユーザーもまとめて取るか、ID をまとめて 1 回で取るようにします。

Why — なぜ気づきにくいのか

どの部品も、単体では正しい

部品ごとに取る書き方は、部品を独立させる良い習慣から来ています。どの部品を読んでも、おかしな所はありません。遅さは、部品を組み合わせたときに初めて現れます。コードを読むだけでは見つからず、通信の記録を時間の順に見て初めて分かります。

開発環境では待ちが短い

開発者の手元では、API がすぐ近くにあり、データも少なく、1 回の往復が数十ミリ秒で済みます。3 回並んでも 0.1 秒で、誰も気づきません。本番の、遠くて混んだ回線で、1 回の往復が数百ミリ秒になって初めて、足し算が効いてきます。

まとめすぎると、別の払うものが来る

同時にする、1 つにまとめる、はどちらも速くなりますが、払うものもあります。

  • いちばん遅いものを全員が待つ。 3 つを 1 つの API にまとめると、配送状況が遅いとき、注文も購入者も表示されない
  • 部品が独立しなくなる。 画面の入口やまとめた API が、部品が何を必要とするかを知る必要がある。部品を足すたびに、入口も直す

だから、速くするだけでなく、遅い部分を後から出せる形にしておくことが大事です。注文と購入者を先に表示し、配送状況は届いたら差し込む。描画をどこでするかで見た、部分ごとに分ける考え方と同じです。

確認 — ここまで読めたか

注文・購入者・配送状況を 1 つの API にまとめたところ、配送業者の API が遅い日に、画面全体が表示されなくなりました。どう直すのがよいですか?

まず選ぶ(解答例は a〜d の記号で説明します)

演習 — まず自分で判断する

解説を読む前に、まず自分で判断してみてください。ここで一度詰まっておくと、 次の節の判断軸が「なるほど」ではなく「そう来たか」に変わります。

演習 — 設計判断を問う

冒頭の注文の詳細画面を、どう直しますか?

与えられた条件
  • 注文・購入者・配送状況の 3 つの API は、どれも注文の番号だけで呼べる。それぞれ 0.8 秒
  • 配送状況は外部の配送業者に問い合わせていて、ときどき 3 秒以上かかる
  • 購入者の部品は、ほかの 4 つの画面でも使われている
  • 表示まで 1 秒以内にしたい
この軸で考える
  • · 並びを作っているのは、データの依存か、部品の形か
  • · 同時に取ることで、どの部品が何を知る必要が出るか
  • · 遅くなりうる取得を、ほかの表示から切り離せるか
まず選ぶ(解答例は a〜d の記号で説明します)

演習 — 説明できるか

「API はどれも 0.8 秒で速いのに、なぜ画面が 2.4 秒もかかるのか」と聞く、バックエンド担当の同僚に説明してください

与えられた条件
  • 相手は API の性能を測り、問題が無いことを確かめている
  • 相手はフロントエンドの部品の仕組みには詳しくない
  • 相手に協力を頼みたいこと(画面用の API など)があれば、それも伝えたい

読み終わりましたか?

読了にすると、これを前提とする記事がロードマップで開放されます。