ラベル Firestore の投稿を表示しています。 すべての投稿を表示
ラベル Firestore の投稿を表示しています。 すべての投稿を表示

2024/08/26

Firestore/Datastoreで複数の範囲フィルタを使ってクエリが可能に

Firestoreが登場する前の昔のDatastoreから、Firestore/Datastoreのクエリには「不等式はクエリ内で1つだけ」という制限があったのですが、2024/7/29にやっと解除されました。

上記リンクはFirestoreですが、Datastoreも同様のページがあります。Datastoreの日本語ページにはプレビューマークがついていますが、リリースノートにはGAと説明されています。

この制限のためにこれまで「DBに範囲を記録しておいて、指定の値がその範囲内にあるデータを検索」ができませんでした。具体的には以下のようなケースです。
  • 開始・終了日時を保存しておき、指定の日時に対応するデータを検索。
  • 上限・下限の金額を保存しておき、指定の金額に対応するデータを検索。
  • etc.

ただし無制限になったわけではなく、少ないながらも制限事項もあるようです。検索時間などパフォーマンス面も未検証なので、遅くないのかなど気になります。

これまではこの制限を回避するためだけにSearch APIを利用したりElasticsearchをインストールしたりといった対応が必要だったのですが、だいぶFirestore/Datastoreのみで検索ができるようになりそうです。

2023/01/13

データストアの選択

世の中には データを保存しておく製品にも、いろいろと選択肢が用意されています。GCPでも目的に応じて特徴の異なる複数のサービスが用意されています。その中でもBigQueryって特徴的で判りやすいと(個人的には)思っていたのですが、最近になってGCPにかかわりながらもBigQueryがどういうものなのかを未だに知らない人に遭遇しました。

ここでGCPのデータの保存目的に使えるサービスを比較しておきたいと思います。


一覧

GCPに用意されたサービスの一覧です。
名称略称概要スケーラブルマネージド
Firestore(特になし)NoSQL、ドキュメント指向データベース。
Cloud SQL(特になし)SQLのリレーショナルデータベース。
Cloud SpannerSpannerSQLのリレーショナルデータベース。
Cloud BigtableBigtableNoSQL・低レイテンシのデータベース。
MemorystoreMemstoreオンメモリで高速動作。
BigQuery(特になし)検索に特化したデータウェアハウス。
Cloud StorageGCS非構造化データ用のストレージ。

「略称」はGoogleが決めた正式なものではないですが、開発者の間でよく使われている呼称です。
「マネージド」の〇はフルマネージド、△はフルではないマネージドを表します。

これら以外にもFirebaseのサービスもあるのですが、それらに関しては割愛します。

各サービスの特徴

サービスを比較・選択するうえで参考としていただけるように、ざっくりと説明します。詳細はGoogleの各サービスをご覧ください。

Firestore

スケーラブルなので負荷が増えても重くなる心配がありません。むしろアクセスが少ないと優先順位が下げられて待ちが長くなる感じさえあります。フルマネージドなのでメンテナンスフリーです。
スキーマレスでドキュメント指向です。自分で各ドキュメントの構造を同じにして使えば、テーブルとして使えます。検索用のインデックスが付いた連想配列の様な感じですが、インデックスによる検索機能は貧弱です。柔軟な検索が必要なら、ElasticSearchやGAEのSearch APIと組み合わせる必要があります。
強整合性でトランザクションにも対応しています。
費用は主に保存容量に対して発生し、無料枠もあるので、ランニングコストは低めです。
SQLしか経験のない方には理解しにくいかもしれませんが、「Not only SQL」なデータベースです。
過去には似たようなDatastoreというサービスがありましたが、それが廃止されてFirestoreのDatastore互換モードとなりました。Firestoreにはネイティブモードもあり、こちらはドキュメントを階層構造にすることもできます。

Cloud SQL

MySQL/PostgreSQL/SQL Serverのいずれかから選んで使用します。これらを自分でGCEに乗せなくても、予めGoogleが用意してくれていると理解すればいいと思います。
なので利点も欠点もMySQL/PostgreSQL/SQL Serverを引き継ぎます。スケーリングに関しては設定した範囲内に限定され、無限ではありません。幸いフルマネージドでメンテフリーで使えます。
費用は主に保存容量とCPUに対して発生します。CPUはGCEと同等なので、ランニングコストはそれなりに発生します。

Cloud Spanner

スケーラブルなので負荷が増えても重くなる心配なし、フルマネージドでメンテナンスフリー、強整合性と、ここまではFirestoreと同じです。こちらはドキュメント指向でありません。SQLでリレーショナルデータベースを使いたい人にとっては、Cloud SQLとFirestore/Bigtableの「良いとこ取り」の様な感じになります。GCPではコスト以外はほぼ最強ではないでしょうか。
費用は主に保存容量とコンピューティングインスタンスに対して発生します。「Cloud Spanner vs Cloud SQL」も参考になると思います。

Cloud Bigtable

スケーラブル、フルマネージド、NoSQLと、ここまではFirestoreと同じです。低レイテンシが最大の売りでしょうか。
GCPに昔からあるサービスで、他のデータベース/ストレージの基盤となるサービスだと聞いたことがあります。(今GoogleのWeb上のドキュメントを探してもそのような説明は見当たらないのですが、当時詳しい人だったかGoogle日本法人の人から聞いた話なので、嘘ではないはず)
費用は主に保存容量とコンピューティングに使われるノードに対して発生します。ノードは事実上GCEと同じなので、たぶんそこそこかかるはず。

Memorystore

Redis/Memcachedのいずれかから選んで使用します。たぶんそれらをGCPに乗せただけと思います。オンメモリなので速いはず。しかもスケーラブル。
速さを活かしてキャッシュや書き換えの集中するデータの保存に向きます。

2021/12/18

Firestoreのトランザクションのリトライがおかしい

公式ドキュメントの説明 

トランザクションとデータ競合」には以下の記載があります。

Firestore では、いずれかのオペレーションを遅延または失敗させることで、データ競合を解決します。データ競合が原因で失敗したトランザクションは、Firestore クライアント ライブラリによって自動的に再試行されます。一定回数の再試行が行われると、トランザクション オペレーションは失敗し、エラー メッセージが返されます。

つまりデータ競合によりトランザクションが失敗するのは、一定回数リトライしても競合を解決できなかった場合ということになります。


実際に起きた例外

ところが Java版のサーバクライアントライブラリを使用したアプリで、割とあっさりとこのエラーが発生しました。以下がその時のログに出ていた例外とそのメッセージ。

java.util.concurrent.ExecutionException: com.google.api.gax.rpc.AbortedException: io.grpc.StatusRuntimeException: ABORTED: Aborted due to cross-transaction contention. This occurs when multiple transactions attempt to access the same data, requiring Firestore to abort at least one in order to enforce serializability.


検証用にアプリを作って故意にデータ競合を起こしてみると、以下の例外が発生しました。別アプリなのでJavaライブラリのバージョンも違っていたかもしれません。

java.util.concurrent.ExecutionException : com.google.cloud.firestore.FirestoreException: Transaction was cancelled because of too many retries.

この例外のgetCause()メソッドで得られたThrowableは、以下。

 com.google.cloud.firestore.FirestoreException : Transaction was cancelled because of too many retries.

 

ネットで検索してみるとStack Overflowにも同様の現象に遭遇した質問「Firestore retry transaction logic」が見つかります。状況は最初の方と同じです。この記事には回答がまったく付いていなくてさみしい限りです。


考察

例外が発生するときと、例外が起きずに正常動作する時とで実行時間にほとんど差がないことから、公式ドキュメントの説明と異なり、実際には以下の実装ではないかと考えられます。

  • トランザクションはリトライしていない、あるいは
  • リトライはしているがウェイトがないのでデータ競合が解決することなく規定回数を回ってしまう

Javaクライアントライブラリのソースを探して解析すれば何かわかるかもしれませんが、面倒ですしそんな時間もありません。


回避策

ベストな解決策は、Googleさんがこの問題を解決したバージョンのライブラリを公開してくれることです。
しかしそれを待っていられないなら、自分でウェイト付きリトライをトランザクションにかましてみればいいでしょう。私はこの方法で、上記の例外がほぼ発生しなくなっています。


最後に

公式ドキュメントにはリトライはクライアントライブラリが実行しているとの記載があります。今回発見した現象はJavaライブラリによるものですが、他の言語のライブラリは使ったことがないので未確認です。


2021/12/23追記

本日、Javaライブラリをひととおり新しくしてみたら、上記の例外は発生しなくなっていました。どのあたりのバージョンからかは分かりませんが、修正されたようです。

2021/12/08

GCPのデモアプリ : vsReversi


 GCPのデモとして、vsReversiを作ってみました。ネット対戦リバーシ(オセロ)です。

  • 対戦相手を選択できる。
  • 適当にマッチングして対戦もできる。
  • 相手がいないときは対コンピュータ戦もできる。

使用しているGCPのサービスは以下のとおりです。
  • App Engine : Webサーバ
  • Firestore : データベース
  • Identity Platform (Firebase Authentication) : ユーザ認証
  • Cloud Tasks : 対コンピュータ戦の思考ルーチン
対コンピュータ戦の思考ルーチンはJavaで書いていますが、実質数秒以下で1手を打ちます。そのためCloud Tasksを使用する意味はないのですが、GCPのデモとして無理矢理使っています。でないとただのGAEのデモなりそうなので。
デモにコストをかけられるほど裕福ではないので、GAEを中心に無料枠の範囲内で運用できるように、GAEの最大インスタンス数を制限しています。そのため利用が集中すると、応答が悪かったり、エラーになる可能性があります。最大インスタンス数の制限を外せば大勢が同時にアクセスしてもレスポンス低下の起きにくいGAE/Firestoreの特徴を活かせるのですが、残念なことになっています。GAE/Firestoreのいいところを全くアピールできていない、デモとしてはダメアプリです。

それでも、見つけた方は楽しんでいただければと思います。