たごもりすメモ

コードとかその他の話とか。

AppEngine SDK標準テスト用環境の問題

そういえばしゃべったりTwitterに垂れ流したりするだけでまとめてなかったぞ、ということでまとめてみる。よく考えたら問題点もまとめずにテスト環境の改善が!とか言ってても、何が問題なのかわからない人にはわからないよなあ。反省。
まとめたら @tmatsuo さんがGooglerを直撃してくれるらしいぞ!(ひどい他力本願っぷり)

で、問題は100%、データストアにあります。
もっと具体的に言うと datastore_file_stub(および最近追加された datastore_sqlite_stub)。同種の問題なので、まとめて「テスト用datastore stub」とここでは言いましょう。

テスト用datastore stubの問題

改めて考えてみたら、問題らしい問題がトランザクションしか思い付かなかった。あれー? 他になんかなかったっけ。

トランザクション

テスト用datastore stubはトランザクションを正常に処理していない。ここでいう「正常に」とは、AppEngine Datastoreの大きな特徴である楽観的排他制御のことを指す。
実装としてはトランザクション開始時にロックを取って、その間は他からデータ操作が不可能なようになっている。それ悲観的ロックじゃん! しかもトランザクション開始時にロックが取れるかどうか見るだけの実装だから、無理矢理トランザクション外操作でエンティティのPutとかやると容赦なく上書きされたりする。おいおい。

Python on GAE SDKではその構造から、通常は同一スレッドから複数トランザクションを並行して走らせることができないし、トランザクション処理を開始してから終了するまでの間にトランザクション外の処理を行うこともできない。*1
また開発用の実行環境である dev_appserver.py もシングルスレッド動作でリクエストをシーケンシャルにしか処理しないので、テスト用datastore stubが正常にトランザクションを処理しなくても、手元でテストをしている限りはそれに気付かない。

開発環境で問題なく動くならいいじゃないか、という意見もあるかもしれないが、いや、問題はある。

AppEngineにおけるロジックはすべて、以下のどちらかの前提に沿って書かれていなければならない。

  • 他で干渉するデータ操作があったときに検出してRetry/Rollbackされるべきもの
    • 要するにrun_in_ransaction()内で動作するコード
  • どこでどんなロジックが同時に動いていても問題のないもの
    • 大部分のコードはこちらに属するはず

開発環境でテストコードを書いている限り、これらのロジックの正常系は正常に動作する。そりゃそうだ、他からの干渉なんて入れようが無いんだもん。シングルスレッドでしか動作しないから。
ただし、以下のような状況のテストは開発環境では絶対にできない。

  • Transactionにおけるデータ衝突時の対処
    • 更新が衝突した場合にRetryされても整合性を保っているかどうか
    • 回復不可能な更新衝突が起きたときに正常にRollbackが実行されているか、Rollbackが実行されたときユーザに出すべき情報を出しているか
  • Transaction外のコードの記述の並列性
    • 本当に他のデータ操作の影響下で正常に動作するかどうか

これらの状況をテストするのは非常に面倒くさい。普通のPython on GAE SDKでは(テスト用datastore stubが対応しても)テストを実行することもできない。
だけど「面倒だからやらない」というのと「どうやってもできない」ということの間には大きな壁があるし、決定的に異なるものだと思う。楽観的排他制御というものがどういうものか実感できない人が開発環境で試すこともできない、というのも大きい。

正直に言って、これはバグで、修正されるべきだと思う。

欲しい機能

CapabilityDisabledErrorのテスト

これはあると嬉しいレベル。GAE/Jにも無いはずだし。*2
AppEngine Datastoreはしょっちゅうおかしくなる。実にタイムリーにこんな素晴らしいまとめもある。

* 定期メンテナンス:毎月1回程度。最近は日本の早朝に行われることが多い。5:00AM-6:00AM(13:00PST-14:00)
* DS障害:1ヶ月 〜 3ヶ月に一度はありそう。数時間書き込み停止。
* App障害:最近はほとんどない
* Cron障害:最近はほとんどない
* その他API障害:ほとんどない
なのかな。
要するに
* 毎月数時間、突発的に起こるDS書込遅延を受け入れられるかどうか
がGAE採用するかどうかのポイントになりそう。

GoogleAppEngineのダウンタイムはどれくらいなのか、公式アナウンスを元にまとめてみた - ikasamt

毎月数時間の異状があるのに、その状況をテストできない、というのは開発環境として厳しすぎる。テストしたい。
できればCapabilityDisabledError(明らかな書込不能)だけじゃなく、書込遅延についてもテストしたいところだ。

まとめ

すごい不満があると思ってたが、挙げてみるとひとつ(要望含めてふたつ)しかなかった。あれー? 印象としては不満たらたらなんだけどな。
Googleからこのあたりが満たされた何かがリリースされれば一番うれしいです。無きゃ、誰か作るしかないよね。

*1:拙作 run_notin_transaction もしくは類似の方法で無理矢理実行することはできるけど。 http://d.hatena.ne.jp/tagomoris/20100422/1271951752

*2:slim3のTestHelperなら可能なんだっけ?