メニュー

2012年11月28日

AppEngine開発環境でのConsistency


AppEngineのDatastore(今では標準となったHigh Replication Datastore)では、クエリのConsistency(一貫性)はEventualです。

このため、開発環境でもHRDの設定にしている場合、Eventual Consistencyな動作になります。

実は開発環境でのDatastoreのConsistencyの動作は設定によって変えられるのですが、デフォルトはTimeBasedHRConsistencyPolicyというものになっています。

# このクラスは google.appengine.datastore.datastore_stub_util の中にあります。

これは名前の通り、時間の経過によってクエリに検出される確率があがります。

Python SDKのコードを読むと、その確率は100msで98%、300msで99%、2000msで99.5%、240秒で100%となっています。

さて、AppEngineのSDKは単体テストを開発環境だけで実行することが容易になっていますが、前述したConsistencyについて、テストの場合には注意が必要なので、解説します。

Python版では、単体テストにはtestbedというモジュールを使います。これは、テスト用の各種サービス(API)のスタブです。

testbedについては公式のドキュメントも参照してください。
https://developers.google.com/appengine/docs/python/tools/localunittesting?hl=en

単体テストの場合、ConsistencyのデフォルトはMasterSlaveConsistencyPolicyです。つまりConsistencyはStrongで、保存したデータは即座にクエリに検出されます。

単体テストではStrong Consistencyは便利なのですが、MSなのでCross Group Transactionが使えません。よって、HRDアプリのテストではHRD用のConsistencyを使う必要があります。
self.testbed = testbed.Testbed()
self.testbed.activate()
self.testbed.init_datastore_v3_stub(consistency_policy= datastore_stub_util.TimeBasedHRConsistencyPolicy())

こんな感じです。

ここで困るのは、Eventualだとテストが書きづらくなることです。

保存したデータがクエリに検出されるかどうか、というテストが、Eventualなので成功したり失敗したりします。

実は、保存したデータをGetするとインデックスが作られるので、 Put > Get > Query とすると、テストが上手く行きます。

しかし、もっとスマートにテストを書くために、TimeBasedHRConsistencyPolicy ではなく、 PseudoRandomHRConsistencyPolicy を使ってみましょう。

これはクエリに検出されるかどうかを、(時間経過によらず)ランダムに決定するポリシーです。
コンストラクタの引数で確率を0.0から1.0までで指定できます。ここで確率を1.0、つまり100%にすると、Strong相当の動作になります。
self.testbed.init_datastore_v3_stub(consistency_policy= datastore_stub_util.PseudoRandomHRConsistencyPolicy(1.0))

これで、シンプルにテストを書けるようになります。


なお、Javaにも同様の機能があるようです(LocalDatastoreServiceTestConfigクラス)。
https://developers.google.com/appengine/docs/java/tools/localunittesting