【ITニュース解説】One Loop Made Four Hundred Round Trips
2026年09月23日に「Dev.to」が公開したITニュース「One Loop Made Four Hundred Round Trips」について初心者にもわかりやすく解説しています。
ITニュース概要
ループ内でデータベースへのクエリを繰り返すと、通信が増え、ページ表示は遅くなる。少量のテストでは見落としがちだ。ループ前にデータを一括取得し、外部アクセスは明確に記述すべきだ。
ITニュース解説
あるWebサイトのリストページが表示されるまでに9秒もかかってしまうという状況があった。開発者のコンピューター上では同じ処理がわずか0.25秒で完了していたにもかかわらず、本番環境では大幅に遅延していたのだ。開発環境ではデータが12件だったのに対し、本番環境では400件のデータがあった。開発者は自分の書いたコードに問題はないと考えていた。
問題はプログラムのループ処理の中に隠されていた。このプログラムは、複数の注文データを順に処理していく中で、それぞれの注文に紐づく顧客の名前を読み出す処理を含んでいた。一見すると、この顧客名はプログラムが既に持っているオブジェクトの単なる「フィールド」(データの一部)のように見えた。メモリ上にあるデータを読み出すのと同じような記述だったため、開発者は特に意識しなかったのである。
しかし、この「顧客名を読み出す」という処理は、実際にはデータベースへの「クエリ」、つまり問い合わせだった。プログラムが、既にメモリ上にあるデータから値を読み出すのではなく、外部にあるデータベースサーバーに対して「この注文の顧客名を教えてほしい」と一つずつ問い合わせていたのである。
この問い合わせは、プログラムが動作しているコンピューターから「プロセス」を離れ、ネットワークを通じてデータベースサーバーまでデータを送り、サーバーが処理をして、その結果を再びネットワークを通じてプログラムに送り返すという一連の通信を伴う。この一連の通信を「ラウンドトリップ」と呼ぶ。問題の本質は、このラウンドトリップが注文データの数だけ、つまり400回も繰り返されていたことにある。個々のラウンドトリップ自体は非常に高速だったかもしれない。しかし、それが400回繰り返されることで、積もり積もって合計9秒という長い待ち時間が発生していたのだ。
この種の問題は、開発者が発見しにくいという特徴がある。まず、コードレビューの段階では、コードの見た目上は、あたかもメモリから値を読み出すかのようにシンプルに見えるため、問題に気づきにくい。変更点もごくわずかで、きれいに見えるため、見落とされがちだ。次に、開発環境やテスト環境では、本番環境に比べて扱うデータ量が少ないことが一般的である。例えば、データが12件しかなければ、400回繰り返される処理が12回で済むため、全体の実行時間は短く、問題は表面化しない。さらに、性能監視ツール(メトリクス)を見ても、個々のデータベースクエリは非常に高速に完了するため、「データベースは正常に動作している」と報告されてしまう。個々の処理が速いのに、全体としてページ表示が遅いという矛盾した状況が生じるため、どこに問題があるのか特定しにくいのである。
このような状況に陥ると、開発者はしばしば誤った場所を改善しようとする。例えば、データベースの検索速度を上げるために「インデックス」を追加したり、データベース自体の性能が悪いと非難したり、あるいはプログラムが動くサーバーのメモリを増やしたりするといった対策を講じる。しかし、根本原因が「ループ内での繰り返し行われる外部アクセス」であるため、これらの対策では何の効果も得られない。
この問題の根本原因は、プログラムの「コードが遅い」ことではなかった。プログラムが、自分のコンピューターの「プロセス」内で完結する処理(メモリからの読み出しなど)と、ネットワークの向こう側にある「外部のコンピューター」(データベースなど)との間でやり取りする処理とを、見た目上区別できないように書かれていたことにある。プログラムがどこで「自分の処理を一時停止して、誰か(外部)の助けを待っているか」が見えなかったのだ。
この問題に対する解決策は、外部との境界線を明確に「見える化」することである。まず、外部プロセスと通信するような関数(例えばデータベースからデータを取得する処理)は、その目的がはっきりとわかるような名前(例えばfetchやloadなど)を付けるべきだ。また、必要な引数や、通信が滞った場合の「タイムアウト」設定などを明確にすることも重要である。
次に、ループ処理が始まる前に、ループ内で必要となるデータをまとめて、一度の通信で全て取得するように修正する。今回のケースであれば、400件の注文データに対応する顧客名を、ループに入る前にまとめてデータベースから取得し、プログラムのメモリ上に保持しておく。そうすれば、ループ内ではメモリ上のデータから顧客名を読み出すだけで済むため、400回のラウンドトリップは不要になる。
テストを行う際には、処理にかかる「ミリ秒」といった時間だけでなく、「データベースへのクエリが何回実行されたか」という回数を計測することが非常に有効だ。処理時間はコンピューターの性能やネットワークの状況によって変動するが、クエリ回数はプログラムのロジックによって決まる「事実」であるため、信頼性が高い。もし誰かが後で誤ってループ内にデータベースアクセスを戻してしまった場合でも、クエリ回数が増加したことで明確に問題が検出できる。最終的な確認として、本番環境で想定されるデータ量、例えば400件のデータで実際にテストを実行することが不可欠だ。
この事例が示すのは、プログラミングにおいて、コードの見た目だけでなく、それが実際にコンピューターの内部や外部でどのような動きをしているのかを深く理解することの重要性である。自分のプログラムがどこまでが「自分で完結する処理」で、どこからが「外部システムに依存する処理」なのかを常に意識することが、高性能で信頼性の高いシステムを構築するための鍵となる。