2023の11日目の記事です。はじめにこんにちは、SWEの西です。普段はANDPADチャットを開発するチャットチームのメンバーとして働いています。今回はANDPADチャットのデータベースとして使われているFirestoreについてお話します。FirestoreはGCPが提供するフルマネージドのNoSQL製品です。フルマネージドNoSQLのもう一つの代表例にAWSのDynamoDBがありますね。Firestoreにしかない特徴の一つとして、データの更新をリアルタイムでクライアントに反映できるというものがあります。この特徴はチャット機能と非常に相性が良いため、ANDPADチャットではFirestoreを採用しています。そんなFirestoreですが弱みもあります。その一つがバックアップ機能です。DynamoDBとFirestoreはどちらもポイントインタイムリカバリ(PITR)に対応していますが、データの保持期間を比べると前者は35日間、後者は7日間という違いがあります。バックアップに関してリアルタイム性と保持期間の両方が求められる場合は、懸念事項の一つになり得るでしょう。BigQueryを使って克服した事例を紹介します。このエクステンションは、FirestoreのデータをリアルタイムでBigQueryに同期してくれるものです。本来はデータの分析や集計のために使うことが想定されているようですが、使い方を工夫することで、長期間のPITRを実現する手段としても使えます。それでは具体的な方法を見ていきましょう。エクステンションのインストールまずはエクステンションをインストールします*1。インストールはCLIまたはFirebaseコンソールのどちらからでも行えます。BigQueryを検索し、画面の案内に従ってインストールしてください。インストール時には、BigQueryに同期するFirestoreのコレクションを一つ指定します*2。インストールが完了すると、BigQueryにx_raw_changelogというテーブルとx_raw_latestというビューが作成されます(xにはコレクション名が入ります)。これらのテーブルとビューの詳細については後述します。既存データのインポートこのエクステンションは、新規に書き込まれたデータについてはBigQueryに同期してくれますが、既存のデータについては別途インポートを行う必要があります。必要に応じてこちらの手順に従ってインポートを行ってください。PITRへの応用さて、ここからが本題です。これまでの作業で、FirestoreとBigQueryが同期した状態になっています。ここからはBigQueryに作られたテーブル/ビューを使ってPITRを行う方法を解説します。テーブル/ビューの構造を知っておくと理解の助けになるため、まずはそちらを見てみましょう。BigQueryのテーブル/ビューの構造エクステンションをインストールすると以下のテーブルとビューが作られます(xの部分にはインストール時に指定したコレクション名が入ります)。テーブルx_raw_changelogビューx_raw_latestそれぞれどのようなものか見てみましょう。x_raw_changelogテーブルこのテーブルにはFirestoreの書き込み履歴が保存されます。カラムは以下のようになっています*3。timestampFirestoreの書き込みが行われた日時(UTC)です。document_name書き込みが行われたドキュメントのパスです。operation書き込みの種類です。以下のいずれかの値をとります。IMPORT既存データのインポートCREATEドキュメントの作成UPDATEドキュメントの更新DELETEドキュメントの削除data書き込みが行われた後のドキュメントを表すJSON文字列です。ドキュメントの全てのフィールドが含まれます。operationがDELETEの場合はnullになります。old_data書き込みが行われる前のドキュメントを表すJSON文字列です。ドキュメントの全てのフィールドが含まれます。operationがCREATEの場合はnullになります。document_id書き込みが行われたドキュメントのIDです。path_paramsドキュメントがサブコレクションに属する場合は、親ドキュメントのIDがJSON形式で保存されます。ここで意識したい点が二つあります。一つ目は、テーブルのレコードがリソースではなくイベントを表しているということです。一つのドキュメントが一つのレコードとして保存されるわけではなく、ドキュメントが変更されるたびにその変更内容が新たなレコードとして保存されます。二つ目はdataというカラムの存在です。このカラムには、変更後のドキュメントの全てのフィールドの値が記録されます。一部のフィールドしか変更されなかったとしてもです。operationによってTaroのageが29から30に変更されていますが、dataカラムにはageだけでなくnameも含まれていますね。これらの点を踏まえてx_raw_latestビューを見てみましょう。x_raw_latestビューこのビューはx_raw_changelogテーブルから作られたもので、Firestoreのコレクションの最新の状態を表します。x_raw_changelogテーブルからどうやってコレクションの最新の状態を再現しているのでしょうか。ビューの定義*4を見てみましょう(解説のために添字を付しています)。path_paramsクエリを見てみると、まずドキュメント毎にグルーピングして(1)、最新の履歴データを抽出していますね(2-A、2-B)。最後にoperationがDELETEのものを除外しています(3)。削除されていないドキュメントの最新の書き込み履歴を取得しているわけです。dataカラムにはドキュメントの全フィールドの値が記録されているのでしたから、結果としてコレクションの最新の状態を表すビューができあがります。任意の日時のデータを復元する方法さて、察しの良い方ならすでにお気づきかもしれませんが、上のx_raw_latestビューのクエリを少し変えてやるだけで、任意の日時のデータを復元できます。(UTC)時点のデータを復元する場合の例です。0:00:00"という条件を追加しました。timestampはFirestoreの書き込み日時を表しているのでした。したがってこのクエリは、「2023年1月1日0時0分0秒以前の書き込み履歴の中から、各ドキュメントの最新の履歴を取り出す」という意味になります。指定した日時のスナップショットを取得できるわけです。あとはこのビューのデータをFirestoreに書き戻してやれば復元は完了です*5。ちなみに、document_name、path_params、dataなどのカラムでフィルタすることで、一部のドキュメントだけ復元することもできます。ついては運用によりますが、x_raw_changelogテーブルのレコードを一切削除しない場合は実質的に無期限になります。最新でないレコードのうち一定日数が経過したものを削除するといった運用をする場合は、その日数が保持期間となります。おわりにこの記事では、Firebaseエクステンションを使ってFirestoreをBigQueryに同期することで、長期間のPITRを実現する方法を紹介しました。「Firestoreの導入を検討しているけれどPITRの保持期間がネックになっている」といった場合は是非参考にしてみてください。アンドパッドでは、「幸せを築く人を、幸せに。」というミッションの実現のため、一緒に働く仲間を大募集しています。ご興味を持たれた方は、下記のサイトをぜひご覧ください。engineer.andpad.co.jp参考資料https://extensions.dev/extensions/firebase/firestore-bigquery-exporthttps://github.com/firebase/extensions/blob/master/firestore-bigquery-export/guides/IMPORT_EXISTING_DOCUMENTS.mdhttps://github.com/firebase/extensions/blob/01d9102bed2f33b0784a6a0f2b4646e3034c904a/firestore-bigquery-export/firestore-bigquery-change-tracker/src/bigquery/schema.ts#L139*1:エクステンションの利用には料金がかかります。詳しくはこちらを参照してください。*2:複数のコレクションを同期したい場合は複数回インストールする必要があります。コレクショングループも指定可能です。*3:一部を抜粋しています。実際のスキーマ定義はこちらを参照してください。snapshots"で"yes"を選択したかどうかで内容が変わります。ここでは"yes"を選択した場合の例を記載しています。*5:実際の事例では書き戻し用のプログラムを別途実装しています。並行処理が得意なGoで実装しました。〜モバイル用分析基盤を整備した話〜モバイルアプリチームの工藤です。検索はてなブログをはじめよう!oct88さんは、はてなブログを使っています。ブログを報告する引用をストックしましたストック一覧を見る閉じる引用するにはまずログインしてくださいログイン閉じる引用をストックできませんでした。再度お試しください閉じる限定公開記事のため引用できません。