GoogleCloudDatastore

GoogleCloudDatastoreのEntityについての覚書

はじめに

CloudDatastoreを使い始めるにあたって、Entityについての覚書です。

ドキュメント

データ整合性の説明
https://cloud.google.com/datastore/docs/concepts/structuring_for_strong_consistency?hl=ja

クエリの書き方
https://cloud.google.com/datastore/docs/concepts/queries?hl=ja

クエリの書き方
https://googlecloudplatform.github.io/google-cloud-ruby/#/docs/google-cloud-datastore/v0.23.0/google/cloud/datastore

Datastore/Go のデータ設計のコツ
http://pospome.hatenablog.com/entry/2017/02/05/171635

イベンチュアルコンシステンシー(結果整合性)とストロングコンシステンシー(強整合性)

GoogleCloudDatastoreでは
一般的なNoSQLで使われる"イベンチュアルコンシステンシー"と呼ばれる動作->clientデータが即座に検索結果に反映されない
に加えて、
"ストロングコンシステンシー"を持った検索を併せ持つ。

クエリの検索方法

グローバルクエリ | Ancestorクエリー

例えば、ショッピングカートの例を考える

Users | Sales という2個のKind(RDBではtableと同様)があった時に、下記のように設計できる。

Users ('Users':5639445604728832)
Users ('Users':5659313586569216)

Sales Key(Users, 5639445604728832, Sales, 5629499534213120)
Sales Key(Users, 5659313586569216, Sales, 5629499534213120)
Sales Key(Users, 5659313586569216, Sales, 5668600916475904)
Sales Key(Users, 5659313586569216, Sales, 5707702298738688)

スクリーンショット 2018-01-19 15.42.06.png
スクリーンショット 2018-01-19 15.42.16.png

これを2種類の検索方法で検索してみる

grobal_query.sql
select * from Sales where user_id = 2
ancestor_query.sql
select * from Sales where __key__ 
has ancestor key(Users, 5659313586569216)

スクリーンショット 2018-01-19 15.49.59.png

この場合、どちらも同じ検索結果が取得できるが、グローバルクエリの場合は、イベンチュアルコンシステンシーとなるので、
必ずしも最新の状態でない可能性がある。
そのため、ショッピングカートなどの記述においては、Ancestorクエリーで取る。

また、グローバルクエリで取る場合は、indexを作製が必要。

indexes : 
-kind : Sales
    ancestor:no <- グローバルクエリー用のインデックス
    properties:
        - name: user_id

次に、Ancestorクエリのindexの場合は、例えば、where句にprocess_data >= '2018-01-01'などを条件に加える場合は

indexes : 
-kind : Sales
    ancestor:yes <- Ancestorクエリ用のインデックス
    properties:
        - name: process_data

パフォーマンス

1つのエンティティグループの書き込み処理については、1秒に1回。

例えば、上記のUser,Salesの例でいくと、秒速で、カートに追加されるなどのことがなければ、
ユーザー毎にエンティティグループを分けているので大丈夫なはず。

トランザクション処理も可能なので、決済成功後に、Salesのstatusを決済済に更新する。
決済に失敗した場合はロールバックして未決済にするようなことも可能となる。

Railsでglobalとancestorを書き分ける

global_and_ancestor.rb
  def self.global_query_search
    query = Google::Cloud::Datastore::Query.new
    query.kind("Sales").
      where("process_data", "=", "2018/01/01")
    results = dataset.run query
  end

  def self.ancestor_query_search
    query = Google::Cloud::Datastore::Query.new
    query.kind("Sales").
      where("process_data", "=", "2018/01/01")
    parent_key = dataset.key "Users", 12345
    query.ancestor(parent_key)
    results = dataset.run query
  end

そのほかの参考サイト

Google::Cloud::Datastore【訳】 
https://qiita.com/kotazi_/items/2bc9a0c8542f75809da9

Google App Engine と Cloud Datastore だけでオークションサイトを作った
http://mitomemel.hatenablog.com/entry/2016/09/28/231157

Cloud Datastore は Entity 毎になぜ秒間1回の書き込み制約があるのか
http://addsict.hatenablog.com/entry/2017/12/10/232320

Cloud Datastore を用い、大規模ゲームでのランキング処理を 1 時間から 5 秒に短縮 
https://cloud-ja.googleblog.com/2014/09/cloud-datastore-1-5_8.html

送金のトランザクションパターン
http://songofcloud.gluegent.com/2009/11/blog-post_18.html

これで怖くない Datastore のトランザクション
http://www.apps-gcp.com/datastore-transaction/

GAEでハマったこと(´・ω・`)
https://qiita.com/hogedigo/items/25b4dbefe694dbfc7dbc