GASでDocumentAppが遅い?CacheServiceで“体感速度”を改善する実践設計

に公開

はじめに

前の記事の続きです
Google Apps Script(GAS)で

  • Drive上のGoogle Docsを読み
  • Markdownに変換し
  • Web API(doGet)として公開する

……ということをやると、ほぼ確実に遅くなります

原因はシンプルで、

  • DocumentApp.openById
  • DriveApp.getFiles
  • getBody() 系のAPI

が、とにかく重い。

この記事では、
**CacheServiceの制約を踏まえた「現実的なキャッシュ設計」**で
「最初の体感」を大きく改善した実装パターンを紹介します。


全体方針(結論)

やったことは難しくありません。

  • キャッシュは 二段構え
  • 「一覧」と「本文」を分離
  • 定期バッチで 温める
  • API側は キャッシュがあれば使う/なければ正しく生成

キャッシュは最適化であって、依存してはいけない
壊れても動く、あれば速い

これを軸に設計します。


キャッシュ構成:二段キャッシュ設計

① 一覧キャッシュ(Index Cache)

  • Key: "0"
  • Value: フォルダ内の Docs ID 一覧(名前順)
cache.put("0", JSON.stringify(allIds), CACHE_TTL);

一覧はほぼ全リクエストで必要なので、
最優先でキャッシュします。


② 本文キャッシュ(Entity Cache)

  • Key: docId
  • Value: { id, title, markdown }
cache.put(docId, payload, CACHE_TTL);

一覧と本文を混ぜないことで、

  • 一覧だけ欲しい
  • 上位N件だけ欲しい

といったユースケースを効率よく処理できます。


バッチで“温める”:preCacheAll()

なぜバッチが必要か

初回アクセス時に

  • DriveApp
  • DocumentApp
  • Markdown変換

を全部やると、初速が終わります

そこで、

  • 時間主導トリガーで
  • よく使うデータだけ
  • 先にキャッシュしておく

という戦略を取ります。


やっていること

export function preCacheAll() {
  // 1. 一覧をキャッシュ
  // 2. 先頭10件の本文をキャッシュ
}

ポイントは 「全部やらない」 こと。

  • アクセスが集中しやすい
  • トップページに出す
  • 体感に効く

この 上位10件だけ を温めます。


Web API 側:getAllでN+1問題を潰す

一覧 + 記事キャッシュ取得の流れ

const top10Ids = allIds.slice(0, 10);
const cachedArticles = cache.getAll(top10Ids);
  • cache.get() を10回呼ばない
  • getAll() で一括取得

CacheServiceは呼び出し回数が増えるほど遅くなるため、
ここはかなり効きます。


キャッシュに無い場合はフォールバック

if (!cachedArticles[id]) {
  // Drive / DocumentApp から生成
}

重要なのは、

  • キャッシュが無くても
  • 正しく結果が返る

という点。

キャッシュが壊れても API が壊れない
これが運用上いちばん大事


サイズ制限への防御(めちゃ重要)

CacheServiceには サイズ制限があります。

そこで、

if (payload.length < CACHE_SIZE_LIMIT) {
  cache.put(...)
}
  • JSON化したあとにサイズチェック
  • try/catch で例外を握る
  • キャッシュ失敗=致命傷にしない

「キャッシュは失敗する前提」 で書きます。


TTL設計:なぜ10分?

export const CACHE_TTL = 600;

理由はシンプルで、

  • ドキュメントは頻繁に変わらない
  • 数分の古さは許容できる
  • アクセス集中を吸収したい

「どれくらい古くてもいいか」 を先に決めると、
TTLは自然に決まります。


改善余地(次の一手)

この記事の実装は、さらに伸ばせます。

  • on-the-fly生成後に cache.put する
  • 一覧キャッシュ欠損時に再保存
  • キャッシュキーの namespace 化(list:v1 など)
  • サイズ超過時の縮退(本文を途中で切る等)

「最初から完璧」を目指さないのがコツです。


まとめ

GASで重い処理を扱うときは、

  • CacheServiceは 前提
  • でも 信用しすぎない
  • 「壊れても動く」を最優先

このバランスが一番長生きします。

キャッシュは速さのため
正しさはロジックで担保する

GASでも、ここまでやれば
「遅いから使わない、あきらめる」 はだいぶ減らせます。

Discussion