Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

CloudFunctionsで一定時間経過後に処理を実行する方法

はじめに

CloudFunctionsで何らかの処理を実行して、その何分後かに別の処理を呼び出したいことがあります。
だけどsetTimeoutを使いたくない。事前にジョブを登録しておくわけにもいかない。

そんな時に以下の方法で実装してみました。

  • cloudSchedulerに対して、実行させたい日時(例:○分後)にジョブを登録。

  • 実行時刻になると処理が呼び出される。

  • 処理が終わったら不要になったジョブを削除。

参考:CloudSchedulerリファレンス

サンプルコード

index.js
const functions = require('firebase-functions');
const admin = require('firebase-admin');
const { google } = require('googleapis');
const cloudScheduler = google.cloudscheduler('v1beta1');
const moment = require('moment');

process.env.GOOGLE_APPLICATION_CREDENTIALS = 'path/to/serviceAccountKey.json';

async function authorize() {
  const auth = new google.auth.GoogleAuth({
    scopes: ['https://www.googleapis.com/auth/cloud-platform'],
  });
  return await auth.getClient();
}

const projectId = 'my-project-id';
const locationId = 'us-central1';
const topicName = 'my-topic-name';
const jobName = 'my-job-name';

// 10分後処理実行を登録する処理
exports.setTriggerIn10minutes = functions.https.onRequest(
  async (req, res) => {
    // 現在時刻の10分後を取得し、cronフォーマットを生成
    const cronStr = moment().add(10, 'minutes').format('m h D M *');
    // GCPのOAuth認証
    const authClient = await authorize();
    // ジョブ追加リクエストを作成
    const request = {
      parent: `projects/${projectId}/locations/${locationId}`,
      resource: {
        name: `projects/${projectId}/locations/${locationId}/jobs/${jobName}`,
        schedule: cronStr,
        pubsubTarget: {
          topicName: `projects/${projectId}/topics/${topicName}`,
          data: Buffer.from(`${topicName} ${cronStr}`).toString('base64'),
        },
      },
      auth: authClient,
    };
    // APIリクエスト
    try {
      const response = (await cloudScheduler.projects.locations.jobs.create(request)).data;
      console.log(JSON.stringify(response, null, 2));
    } catch (err) {
      console.error(err);
    }
    res.status(200).send('OK');
  }
);

// 10分後に呼び出される処理
exports.beCalledAfter10minutes = functions.pubsub.topic(topicName).onPublish(
  async (event, context) => {
    const pubsubMessage = event.data;
    console.log(Buffer.from(pubsubMessage, 'base64').toString());

    // TODO: 実際にやることを記述

    // GCPのOAuth認証
    const authClient = await authorize();
    // ジョブ削除リクエストを作成
    const request = {
      name: `projects/${projectId}/locations/${locationId}/jobs/${jobName}`,
      auth: authClient,
    };
    // APIリクエスト
    try {
      await cloudScheduler.projects.locations.jobs.delete(request);
    } catch (err) {
      console.error(err);
    }
  }
);

実行確認

最初の処理

ブラウザから以下のURLにアクセスし、Cloud Functionsを実行します。
https://location-id-my-project-id.cloudfunctions.net/setTriggerIn10minutes

Cloud Functionsのログを確認し、setTriggerIn10minutesが正常に実行されたことを確認します。
Cloud Schedulerのジョブ一覧を確認し、トピック:my-topic-nameのジョブが10分後の実行予定で追加されたことを確認します。

10分後に呼び出される処理

Cloud Functionsのログを確認し、beCalledAfter10minutesが正常に実行されたことを確認します。
Cloud Schedulerのジョブ一覧を確認し、トピック:my-topic-nameのジョブが削除されたことを確認します。

注意事項

  • FirebaseのSparkプラン(無料)では使えませんでした。Blazeプラン(従量制)にする必要あり。

  • Cloud Schedulerの管理者権限をIAMで割り当てる必要あり。

  • 実行時刻は秒単位での指定はできません。

といった制約があるので使える場面は少ないかもしれませんが、もしもの時に役立てばと思います。

takusamar
南の島でよんなーよんなー働いてます。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away