Cloud Firestoreの勘所 パート1 — 概要

Cloud Firestoreの概要

Google製、モバイル・ウェブ・サーバー開発に対応したCloud Firestoreを使いこなすための勘所をつらつらと書いていきます。パート1の本記事ではFirestoreの全体観を掴んでいただけたらと思います。

Cloud Firestoreは、Firebaseのサービス群の1つという捉え方と、GCP(Google Cloud Platform)のサービス群の1つという捉え方がありますが、本記事ではFirebase経由で使うことを前提に書いていきます。Cloud Functionsなど関連サービスとして言及するものも同様です。Firebaseはざっくり言うとGCPのクライアントアプリ向けサービスを使いやすく提供しているラッパーサービスなので、それで足りるものはFirebase経由での利用をお勧めします。

ドキュメントも次のように2箇所にあるのですが、Firebase経由で使う場合は基本的に前者の方を見るだけで充分です。


まず、そもそもFirestoreとは何?ということに関しては、次の記事の前半に書いたので、ご覧ください。


Firestoreを使うと何が嬉しい?

まずはFirestoreの利点をざっと紹介します。

サーバーレス(サーバーの管理不要)

Firestoreに限らずFirebase配下のサービスすべてに言えることですが、サーバーの管理が不要で、主に次の恩恵を受けられます。

  • サーバーを管理するコストをカットできる
  • 特に初期フェーズにおいてサーバー代が非常に安価に済む
  • 高可用性を実現しやすい

ただ、Firestoreは現状ベータ版なので、以下の注意が必要です。

特に、SLA(サービス品質保証)適用外ということが気になると思いますが、筆者が使っている限りは特に最近はとても安定して動いています。2017年まではたまに挙動不審になったり遅延が大きくなる感じもありましたが、最近は全然体感できないです。

また、過去1週間のサービスステータスは以下から確認できますが、ご覧の通り障害ゼロです。

この前の期間も含めて、少なくとも過去数週間はFirestoreの障害ゼロです

FirestoreはまだSLA適用外なものの、筆者はプロダクション利用に充分耐え得ると感じて使っていますが、これは個人の感覚や運用しているサービスポリシーにもよるので、ベータ版な時点で使いものにならないと見なされることもあると思います。早くベータ版を脱して欲しいですね🐶

極めて高いスケーリング性能

公式ドキュメントに、世界規模の大きなアプリにも耐え得るスケーリング性能を持つと明記されています。普通に書けば普通に実質青天井にスケールするサービスを作れます。

Cloud Firestore brings you the best of Google Cloud Platform’s powerful infrastructure: automatic multi-region data replication, strong consistency guarantees, atomic batch operations, and real transaction support. We’ve designed Cloud Firestore to handle the toughest database workloads from the world’s biggest apps.
https://firebase.google.com/docs/firestore/

次の記事も参考になります。

データの高速な同期処理をクライアントコードのみで容易に実現できる

このあたりから、実利用する時の観点になってきます。Firestoreはその登場以前からある Firebase Realtime Database と同じく、従来からよくある構成のデータベースを持つ自前のWeb APIを介するサーバー・クライアント間の処理を、クライアントコードだけで簡単に実現できます。

ローカルデータベースに近い感覚でデータの取得・更新処理を書くだけで、クラウド上のデータベース(内部的には Google Cloud Datastore の模様)と同期されます。オフライン時は、データ取得の場合キャッシュ済みのデータを返してくれたり、データ更新の場合は一旦ローカルに保持されオンラインになり次第クラウド同期、というケアもFirestore SDKが自動的にやってくれます。

これを従来のやり方でWeb API含めて自力で正確かつ高速な実装をしようとすると大きな労力がかかりますし、スケーリング性能を考慮するとさらに難易度が上がります。これまでの少人数開発ではそのあたりの何かしらを妥協しながら開発進めざるを得ないことが多かったですが、Firestore(や類似技術)の登場により短期間で極めて品質の高いアプリを作ることが可能となりました。

### クラウドのロジックを挟むことも可能

また、「クライアントコードのみで同期処理を書かなくてはならない」という制約があるわけではなく、要件によってはCloud Functionsを併用することで次のようにクラウド上のロジックを組み合わせることも容易にできるという柔軟性も持ち合わせています。

  • ドキュメント(データ)の追加・更新・削除をトリガーに特定のロジックを実行(Firestoreトリガーを利用)
  • クライアントからCloud Functionsを叩いて、そこからいくつかの処理を組み合わせながらデータ更新(HTTPSトリガーを利用)

Firestoreの不利な点

ここまで良い点ばかり挙げていましたが、もちろん不利な点もあります。主に、NoSQLデータベースであることに起因するものが挙げられます。

複雑なクエリ・集計処理が困難

まず、Firestoreでは「クエリがまったくできない」というわけではないです。これはRealtime Databaseから大きく改善された点でもあります。

ざっくり次のことは対応されています。

  • 単一フィールドによる、単純な絞り込み・ソート
  • 複数のフィールドの組み合わせによる、単純な絞り込み・ソート

デフォルトでは、すべての単一フィールドにインデックスが貼られているため前者のクエリはできるものの、後者の複数のフィールドの組み合わせによるクエリをしたい場合、その組み合わせによってその都度あらかじめインデックスの作成が必要なことにも注意です。

インデックス未定義のフィールドの組み合わせのクエリを投げると、次のような実行時エラーが発生してインデックス定義を促されます。

Error Domain=FIRFirestoreErrorDomain Code=9 “The query requires an index. You can create it here: https://console.firebase.google.com/project/PROJECT_ID/database/firestore/indexes?create_index=EghtZXNzYWdlcxoNCgljcmVhdGVkQXQQAxoQCgxzZW5kZXJVc2VySWQQAxoMCghfX25hbWVfXxAD"

記載のURLを踏むと、次のように定義の足りないインデックスの作成画面に飛びます。

インデックス作成ダイアログ

ちなみに、インデックス管理はこのままサクッとWebコンソールから手動作成もできますが、実アプリ開発ではFirebase CLIを利用することをおすすめします。インデックスをコード管理できたり、複数環境のインデックス更新をミス無く行えるようになります。

上のダイアログで促されたインデックス作成を次のようなインデックス定義JSONファイルに落とし込んでデプロイ( firebase deploy --only firestore:indexes )すると、手動操作した場合と同じ結果になります。

firestore.index.jsonの例

2つ以上のキーによるクエリ・ソートの度にインデックス定義が強制されるのはやや面倒と思うかもしれませんが、これによりすべてのクエリにはインデックスが使われることになり、つまり基本的に重いクエリを投げることが不可能となっていて、むしろ優れた点だと捉えています。

単純な絞り込み・ソート

それぞれ「単純な」と注釈付きなのは、一般的なRDB(リレーショナルデータベース)では当たり前にできることも、FIrestoreではできないことが多いからです。ドキュメントの「クエリの制限事項」に5つほど記載されているものを眺めるとイメージが湧くはずです。個人的には != による絞り込みができないことがちょくちょく気になります。ドキュメントには >< の2つのクエリを統合することで解決可能と書かれていますが、特定フィールドが非 NULL のものを抽出したい要件はこれでは解決できません。この場合、データ構造をそれにフィットさせるか、あるいは全件取得してクライアント側で NULL のものを除去する処理が必要となります。

また、FirestoreではSQLのJOINGROUP BY などの集計操作などは概念さえ存在しません。つまり、次のような考え方の変化が必要です。

  • RDBでは、基本的にはとにかく正規化して必要に応じてインデックス貼りながらクエリするパターンが定石
  • Firestore(NoSQLデータベース)では、アプリ要件に応じてクエリ無しかするとしても単純なクエリで済むようなデータ構造として保持しておくことが定石
  • データ構造の工夫だけでは済まないケースでは別サービスの併用が必要(ドキュメントにはAlgoliaを併用した全文検索の対応法が紹介されています)
  • [2018年8月に解決]: (原文)Firestoreのデータを直接ターゲットとしたデータ分析は複雑なものはほぼできず、BigQueryなどデータ分析に適した入れ物に適宜格納しておくなどの前処理が必要になる(Firestoreのデータに対して無理矢理分析するとしてもSQLなどでスマートには解決できず全ドキュメントを舐めたりする必要があってあまり現実的ではない)

最後に述べたFirestoreドキュメントのデータ分析のしにくさは、Firestore → Google Cloud Storage(GCS) export → BigQueryへロード・クエリー、というやり方が公式にサポートされたので、かなり解消されました。

詳しくは以下の一連のツイートなどご覧ください。

ただ、RDBはエクスポート・ロードなどの手順無しにすぐにクエリーできるので、その手軽さには劣るはずですし少しコストが嵩むのも気になります。とはいえ、RDBでもデータ分析用にリードレプリカ追加したり分析に適したデータベースに適宜移すなどして別途分析基盤を整えることも普通なので、それと同等のことを簡単にできるようになったと言えます。

フルマネージドなバックアップサービスが用意されていない

2018年8月頃からインポート・エクスポートのREST APIおよびgcloudコマンドが提供されました:

そのため、次のようにそれらを組み合わせて、バックアップ・リストアの仕組みを構築可能です。

以前と比べると大きく改善されはしましたが、いわゆるフルマネージドなバックアップサービスはまだ提供されていません。

Firebase Realtime Database では次のように、バックアップ・リストアのサービスが提供されていて、簡単な設定をするだけで、毎日Google Cloud Storage(GCS)にバックアップされます。

Firestoreでも、ベータ版が外れる頃には同様な仕組みが提供されそうな気がしています🧐

2018年7月までの状況:

Stack Overflowなどでも質問があり、その回答の通り、現時点ではFirestoreにマネージドなバックアップサービスが存在しません。

ただ、node-firestore-backup というOSSは存在し、これを定期的にCron Jobなどで実行することでバックアップは可能です(もちろん自前で別のバックアップ処理を書いてもOKです)。


本記事で述べたFirestoreの特性をまとめると、次のような感じでしょうか。

  • RDBではできてFirestoreではできないこと(苦手なこと)をきちんと把握して、アプリ要件に適したデータ構造・使い方の工夫が大事
  • Firestoreでも単純なクエリ・ソートは可能なものの、それで解決困難な要件では外部サービスの併用が必要
  • まだベータ版なことに注意(SLA適用外・マネージドなバックアップサービスが無い、など)

NoSQLデータベースであることに起因する不利な点は、強力なスケーリング性能とのトレードオフとして割り切るしかないですね。個人的には、単純なクエリができるだけでもありがたいと感じています。


本記事のパート1ではFirestoreの概要説明だけとなりましたが、パート2では実践的な使い方をなぞっていきます。