GameWith Developer Blog

GameWith のエンジニア、デザイナーが技術について日々発信していきます。

functions を使って firestore のGETをキャッシュしてコストを抑える #GameWith #TechWith #firebase

こんばんは! @peka3 です

今回の内容は、firestoreの読み込み結果をキャッシュしてインフラコストも抑えましょう、という話です

今回もfunctionsを利用します

firestoreと利用料と怖いところ

2022/12現在の東京リージョンのfirestoreの利用料は以下のような従量制になっています

簡単に概算すると、ドキュメント20件が表示される一覧ページがあったとして、そのページのPVが100万PVあったとすると

(0.038/100,000) * 20 * 1,000,000 = $7.6

となります(無料枠除く)

これでも十分安いのですが、即時性が大切なコンテンツではユーザーが更新を連打することもありますし、一覧画面で20件以上のドキュメントを表示したいケースも多いと思います

また、何かのイベントなどで急激にユーザーが流入してきたとして、不意にPVが跳ね上がっても、実際のコストがいくらになっているのか、翌日以降でないと反映されないため、気づいたときには時既に遅しとなりがちなのが、 firestore(firebase)の怖いところでもあります

そうならないために、事前に対策を打っておきたいところです

functions で firestore を簡易キャッシュ

ここから先 functionsfirestore が沢山出てくるのですが、間違えないようお気をつけください😵

一番簡単にできる対策が firebase の functions を利用したキャッシュです

流れとしては以下のようになります

  • functions で、必要なドキュメント一覧を返すAPIをデプロイ
  • クライアントは firestore へダイレクトにアクセスするのをやめ、functions に立てたAPIを使って画面生成

以下functionsのコード例です

const db = admin.firestore();
const postsRef = db.collection("posts").limit(20);

const snapshot = await postsRef.get();
const posts: firestore.DocumentData[] = [];
snapshot.forEach((doc) => {
  const post = doc.data();
  posts.push(post);
});
// 60秒間キャッシュする
const cacheAge = 60;
response.set("Cache-Control", `public, max-age=${cacheAge}`);

response.status(200).send(posts);

functionsはすべてのコンテンツがCDNを通して配信されています(実際にはfirebase hostingの機能)

詳細はこちら Manage cache behavior  |  Firebase Hosting

Cache-Controlにpublicを指定するとCDNでのキャッシュ期間を指定できます

これはfunctionsの機能というよりは一般的なCDNの仕様です

つまり上記のコードは postsコレクションのドキュメント20件を60秒間CDNとクライアントでキャッシュする というものになります

(クライアントにキャッシュさせる必要がない場合は public, max-age=0, smax-age=60と指定すればよいです)

このようにレスポンスヘッダを設定してCDNキャッシュしておくことで、どんなにPVが増えても、1日あたり 60*24*20=28800 件のドキュメント読み込み しか実行されないため、コストを抑えることができます

実際にはfunctionsのほうでも課金が発生するのですが、functionsはこのようなシンプルな使い方だとかなり安く済む料金体系なので、こちらを経由したほうが一般的にはコストを抑えられると思います

料金  |  Cloud Functions  |  Google Cloud

firestore データバンドル について

functions には firestore の複数クエリとその結果の snapshot をまとめて1レスポンスとして返すことができるのですが、

そちらを使って同様にキャッシュをするのも良いと思います

この機能を使えば

  • クエリをfunctions側に閉じ込められる
  • クライアントから複数リクエストfirestoreに投げる必要がない
  • firestore独自のデータ形式をAPIでもクライアントでもそのまま扱える(timestampなど)

と言ったメリットがあります

今回は長くなってしまうのでそちらの機能については詳細を伏せたいと思います

資料だけ貼っておきます

Cloud Firestore data bundles  |  Firebase