システム開発

APIのタイムアウトをキャッシュで抑制した話|重い集計処理を速くする設計

  • #キャッシュ
  • #タイムアウト
  • #パフォーマンス
  • #AWS
  • #設計

システムを運用していると、「最初は速かったのに、データが増えたら急に遅くなった」という問題に出会います。この記事では、重い集計処理が原因でAPIがタイムアウトしていた問題を、キャッシュで解消したときの考え方と設計を紹介します。

何が起きていたのか?

ある管理画面で、一覧を開くたびに集計APIを呼び出していました。データが少ないうちは問題ありませんでしたが、件数が増えるにつれて応答が遅くなり、ついにはAPI Gatewayのタイムアウトでエラーが返るようになりました。

API GatewayとLambdaを組み合わせる構成では、Lambda自体の実行時間に余裕があっても、API Gatewayの統合タイムアウト(REST APIの既定値は29秒)を超えるとエラーになります。

原因は何だったのか?

調べてみると、原因はシンプルでした。

  • 画面を開くたびに、同じ集計を最初からやり直していた
  • 集計の元データは1日に数回しか変わらない
  • 複数のスタッフが同じ画面を開くため、同じ計算が何度も走っていた

つまり、ほとんどのリクエストで前回と同じ結果を、毎回時間をかけて作り直していたのです。

どう解決したのか?

タイムアウトを延ばすのではなく、計算を減らす

タイムアウトの時間を延ばす方法もありますが、データが増えればまた同じ問題が起きます。また、何十秒も待たされる画面は使われません。そこで、集計結果をキャッシュし、データが変わったときだけ作り直す設計に変えました。

キャッシュの設計で決めたこと

決めること今回の判断
何をキャッシュするか集計結果(画面に出す形に整えたもの)
どこに置くか複数のLambdaから共有できる外部ストア
いつまで有効か有効期限を設定(保険として)
いつ消すか元データが更新されたとき

Lambdaは実行環境が使い回されるとは限らないため、メモリ上の変数ではなく、外部のストア(DynamoDBやElastiCacheなど)に置くのがポイントです。

実装の流れ

キャッシュを使った集計(概要)
const CACHE_KEY = 'summary:v1';
const TTL_SECONDS = 60 * 60; // 保険としての有効期限(1時間)
 
export async function getSummary() {
  // 1. まずキャッシュを見る
  const cached = await cache.get(CACHE_KEY);
  if (cached) return cached;
 
  // 2. なければ集計して保存する
  const summary = await buildSummary(); // 重い集計
  await cache.set(CACHE_KEY, summary, { ttl: TTL_SECONDS });
  return summary;
}
 
// 元データが更新されたら、キャッシュを消す
export async function onDataUpdated() {
  await cache.delete(CACHE_KEY);
}

この形にすることで、元データが更新された直後の1回だけ集計が走り、それ以外のリクエストはキャッシュから即座に返るようになりました。

さらに改善できることは?

古いデータを返しながら裏で更新する

キャッシュが切れた直後のリクエストは、集計を待つ必要があります。これを避けるには、有効期限が切れても古い結果をいったん返し、裏で新しい結果を作る方法(stale-while-revalidate)が有効です。

集計を事前に作っておく

さらに進めると、データ更新のイベントをきっかけに集計を作り直しておく方法もあります。利用者がAPIを呼ぶときには、常に計算済みの結果が用意されている状態になります。

キャッシュのキーにバージョンを入れる

集計の内容や形式を変えたときに古いキャッシュが残らないよう、キーに v1 のようなバージョンを入れておくと安全です。

キャッシュを入れるときの注意点は?

  • 常に正確な値が必要な処理には慎重に使う:在庫・残高・定員の残りなどは、古い値が表示されると問題になる
  • 消し忘れを防ぐ:データを更新するすべての経路でキャッシュを消す。経路が多い場合は、有効期限を短めにして保険にする
  • 同時アクセスに備える:キャッシュが消えた瞬間に大量のリクエストが来ると、全員が同時に集計を始める。ロックや事前作成で防ぐ

データベースやサーバーの選び方で運用費を抑える考え方は、コスパ最強の開発構成|DB・サーバー・LLMまで運用費を抑える選び方でまとめています。

まとめ

  • タイムアウトは「時間を延ばす」より「同じ計算を減らす」で解決する
  • キャッシュは「何を・どこに・いつまで・いつ消すか」を先に決める
  • 正確さが必要なデータには慎重に使い、同時アクセスにも備える

システムの遅さやタイムアウトでお困りの方は、既存システムの調査からご相談いただけます。

よくある質問

タイムアウトの時間を延ばすだけではだめですか?

一時的にはしのげますが、データが増えればまた同じ問題が起きます。また、利用者は長い待ち時間に耐えられません。処理そのものを速くするか、結果を使い回す設計にする方が根本的な解決になります。

キャッシュを入れると古いデータが表示されませんか?

有効期限だけに頼るとその間は古いデータが返ります。データの更新時にキャッシュを消す(無効化する)仕組みを組み合わせることで、ほぼ最新の状態を保てます。

どんな処理がキャッシュに向いていますか?

読み込みが多く書き込みが少ない処理、計算に時間がかかる処理、多少の遅れが許される処理です。逆に、残高や在庫のように常に正確な値が必要な処理には慎重に使います。

システム開発のご相談はスエキチシステムへ

要件が固まっていなくても大丈夫です。まずはお気軽にご相談ください。

この記事を書いた会社

スエキチシステム

「企業品質を、個人価格で。」を掲げる福岡のシステム開発会社。HP制作・業務システム開発・LINE予約アプリ・AI導入支援を手がけています。 開発現場で得た知見を発信しています。

IT・システムの仕組みは、YouTubeのショート動画でも解説しています:システムのオタク