8
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

超簡単!Firestore を Firebase Cloud Function 経由で BigQuery に定期インポートする方法

Last updated at Posted at 2019-04-17

はじめに

image.png
出典: https://medium.com/google-cloud-jp/firestore-bigquery-3b887a5bc27e

firestore はリアルタイム性に優れている一方で、過去まで遡ったで大量のデータに対して複雑なクエリーを実行することができません。
自分が担当しているサービス規模だと、今まではギリギリ対応できるデータ量だったので直接 firestore で全件取得した後集計処理を書いていたのですが、サービスが成長するにつれてこれではまかないきれなくなってきてしまいました。
そこで今回は、firestore の集計に関するコレクションを BigQuery にインポートするまでに行った手順をまとめてみます。

手順

Cloud Storage のバケットを作成

バケットの作成

ライフサイクル設定

スクリーンショット 2019-04-17 21.25.30.png

BigQuery のデータセットを作成

今回は firestore.~ としたかったので firestore で作成
https://cloud.google.com/bigquery/docs/datasets?hl=ja

スクリーンショット 2019-04-17 21.20.51.png

スクリーンショット 2019-04-17 21.20.10.png

Cloud Function の関数作成

Export 用関数の作成

対象コレクションを指定の Bucket へエクスポートします。
実際は引数を取って好きな日付で実行できるようにしておくとベターかと思います。

import { google } from 'googleapis';

import { Config } from 'src/config';

import * as Moment from 'moment';
import * as Functions from 'firebase-functions';

export const firestoreExportToBigQueryCron = Functions.pubsub
    .topic('topicName')
    .onPublish(async message => {
        const targetYmd = Moment().subtract(1, 'day').format('YYYYMMDD');

        const auth = await google.auth.getClient({
            scopes: ['https://www.googleapis.com/auth/datastore', 'https://www.googleapis.com/auth/cloud-platform'],
        });

        await google.firestore('v1').projects.databases.exportDocuments({
            name: `projects/${Config.projectId}/databases/${Config.projectId}`,
            requestBody: {
                collectionIds: ['collectionId'],
                outputUriPrefix: `gs://${Config.gcs.firestoreExportBucket}/${targetYmd}`,
            },
            auth,
        });
    });

Import 用関数の作成

対象 Bucket のアップロードに反応し、エクスポート結果を BigQuery にインポートします。

import { google } from 'googleapis';

import { Config } from 'src/config';

import * as Functions from 'firebase-functions';

export const bigQueryImportStorageTrigger = Functions.storage
    .bucket(Config.gcs.firestoreExportBucket)
    .object()
    .onFinalize(async object => {
        const name = object.name!;
        const matched = name.match(/all_namespaces_kind_(.+)\.export_metadata/);
        if (!matched) {
            return console.log(`invalid object: ${name}`);
        }

        const collectionName = matched[1];
        const auth = await google.auth.getClient({ scopes: ['https://www.googleapis.com/auth/bigquery'] });
        const result = await google.bigquery('v2').jobs.insert({
            auth,
            projectId: Config.projectId,
            requestBody: {
                configuration: {
                    load: {
                        destinationTable: {
                            tableId: collectionName,
                            datasetId: 'firestore',
                            projectId: Config.projectId,
                        },
                        sourceFormat: 'DATASTORE_BACKUP',
                        writeDisposition: 'WRITE_TRUNCATE',
                        sourceUris: [`gs://${Config.gcs.firestoreExportBucket}/${name}`],
                    },
                },
            },
        });

        console.log(result);
    });

IAM の設定

どうやら GAE デフォルトアカウントにはエクスポートやインポートの権限がないようなので IAM から追加します
https://cloud.google.com/iam/docs/quickstart?hl=ja

スクリーンショット 2019-04-17 11.02.10.png

スケジューリング

GAE か Cloud Scheduler で先程の関数を定期実行できるように設定します。
https://cloud.google.com/scheduler/docs/quickstart

ハマったこと

cloud storage のエクスポートからのインポートを手動で確認する工程にて...
エクスポート実行直後、パッと見ディレクトリとかは生成されているのでエクスポートが終了したと勘違いし、その中途半端なディレクトリの再インポートに失敗してハマっていました...

データ量の多いプロジェクトの場合は下記コマンドでジョブの終了をちゃんと確認しましょう...

gcloud beta firestore operations list

また、現在の BigQuery は以前までよく使われていた日付別のテーブル分割が非推奨になっているようですが、大量のデータがあるスコープにおいては上書きインポートではなく、追加で管理したいものです。
そうしないと全データを firestore 側にも保持しないといけなくなるため、DB 復元時間やコスト面に支障が出てきます。
しかし firestore からエクスポートしたファイルからは BigQuery への追加インポートができないため、大量のデータを扱う必要のあるスコープにおいては日付別テーブル分割で妥協することにしました...

おわりに

今回の対応で 10 分近くかかっていた集計処理を 1 分未満に短縮できました。
また、firebase と BigQuery はどちらも GCP に属するサービスなので、IAM を介して簡単安全に連携できる点も素晴らしいです。(今回作成したスクリプトはどちらも認証情報の記述が不要)
今後も引き続き BigQuery を活用していきたいと思います!

参考

8
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?