[Firebase] Functionsを並列実行する


前置き

Firebase Functionsで、日次のバッチ処理をする場合に、処理を並列に実行して、すべての処理が完了する時間がを短くしたい場合がありました。具体的には、Firestoreのコレクション内の複数のドキュメントの一つ一つに、時間がかかる処理をしたい場合です。

なお、ここでの並列とは、Functions自体を複数に分けて、それぞれ別のインスタンス上で処理を実行させることを意味しています。Promiseを使った非同期処理による並列ではありません。

Functionsを並列に実行する方法

Stack OverFlowに参考になる質問があります。
How to invoke other Cloud Firebase Functions from a Cloud Function

※ どちらのケースでもFunctionsの呼び出し回数は増えるので課金に影響があります。

今回は、問題の少ない、Pub/Subを使ったアプローチを試してみました。

Cloud Pub/Sub トリガーを使って、Functionsを並列実行する

FunctionsからPub/Subにメッセージを発行するには@google-cloud/pubsubを利用します

npm install @google-cloud/pubsub

Functionsのサンプルコードは以下の通りです。

import * as functions from "firebase-functions";
import { PubSub } from "@google-cloud/pubsub";

// スケジュール実行される
export const scheduleFunctions = functions
  .region("asia-northeast1")
  .pubsub.schedule("*/5 * * * *") // 検証のため5分毎にしている
  .timeZone("Asia/Tokyo")
  .onRun(async context => {
    const pubsub = new PubSub();
    for (var message of ["hello", "world"]) {
      const data = JSON.stringify({ message: message });
      const dataBuffer = Buffer.from(data);
      // Pub/Sub 特定のトピックにメッセージ送信
      const eventId = await pubsub.topic("topic-functions").publish(dataBuffer);
      console.log(eventId);
    }
  });

// Pub/Sub 特定のトピックにメッセージ受信で実行される
export const topicFunctions = functions
  .region("asia-northeast1")
  .pubsub.topic("topic-functions")
  .onPublish(async (message, context) => {
    await new Promise(resolve => setTimeout(resolve, 5000)); // 検証のため5秒待つ
    console.log(Buffer.from(message.data, "base64").toString());
    console.log(context);
    return true;
  });

実行結果

制限

Functionsのスケーラビリティには制限があります。未検証ですが、最大同時呼び出し数 1,000 に該当すると思います。
※上記のコードを使って、同時実行数100までは検証しました。

まとめ

Pub/Subを使って、1つのFunctinosから複数のFunctionsを発行できました。業務への組み込みはこれからなので、まだ見えていない課題はあるかもしれません。

別の並列化の方法や間違っている部分があったらご指摘ください。