2012年1月18日水曜日

Slim3 Source Code Reading #4

今日はSlim3 Source Code Reading #4を開催しました。
参加者が私と@sinmetalさんの2人だったので、当初想定していたメンバーになりました。

今回は、

  1. EntityGroupとQueryについての前提知識の共有(というよりは、前回間違った説明があったのでその辺りの共有)
  2. Transaction、GlobalTransactionのソースコードを読み進めました。
  3. EntityGroupに関する実装上の話題。構成とか。Ancestorクエリにするケースなど。
  4. sinmetalさんの「サービス単体でテストコードを動かすと、Cross Group Transactionが正常に動作するのに、プロジェクト全体のテストを行うとテストに失敗する件」

を行いました。

Transactionについては、Datastore.javaにあるDatastore#beginTransaction()を読み進めました。
ただ、この中身は、Google App EngineのTransactionAPIを利用しているだけで、Slim3で特別何かをしているということはありませんでした。
ただし、TransactionのOptionでSystem.getProperty("slim3.useXGTX")の結果がTrueである場合、Cross Group Transactionが有効になります。
System.getProperty("slim3.useXGTX")はappengine-web.xmlに記述してある値を取得します。※現時点ではデフォルトでは記述がないので、値はfalseです。

GlobalTransactionに関しては、かなり大変な事をしてあるので、概要だけ説明を書いておきます。
(sinmetalさんが検証コードを書いて試してくれるという事なので、その結果を待ちます)
実際、GlobalTransactionが必要になる事はこれからは少なくなってくると思いますので
(現時点で既にDeprecated)Cross Group Transactionを利用した方が良いと思います。

具体的にはどうしているかと言うと、

  1. トランザクションを開始する。Datastore.beginGlobalTransaction();
  2. エンティティをGet or Putする時、Lock用のキーMapに、現在のキーの祖先が存在しているかを確認する。存在していれば、例外をスローする。ただし、存在していても、キーのTimestamp+30秒を超えていれば無視する。
  3. GlobalTransaction#Get or Putを呼び出す。その時、対象のエンティティの祖先のキーを取得し、Lock用のMapに退避しておく。キーにはTimestampを保存しておく。また、Getの場合はKeyのみ、Putの場合はKeyとEntityのペアをJournal用のMapにPutする。
  4. トランザクションをCommit or Rollback(GlobalTransaction#commit、rollback)する。この時、GlobalTransactionの場合は、Journal用のMapからGet用のKeyを削除し、Put用のEntityをslim3.GlobalTransactionカインドにPutする。Get用のKeyにはEntityが登録されていないので、Entity==nullであれば、Get用のKeyと判定する。ただし、単一EntityGroupのみの場合(Lock用のMapが空)はLocalTransactionと判断しそのままCommitする。
  5. GlobalTransactionの場合、TaskQueueにキューを登録する。登録するキューはGlobalTransactionServletへのリクエストとして登録する。
  6. GlobalTransactionServletで、RollForwardトランザクションの場合、Commitされた場合は、GlobalTransaction用のカインドにある、KeyとEntityのペアを取り出し、本来のカインドのエンティティにPutする。Rollbackトランザクションの場合は、カインドから対象のエンティティを削除する。

大体の処理は上記のようになっています。
正確な処理については、大変ですが、ソースコードを読んでみてください。

最後にsinmetalさんから相談された、「単体テストでHRDのテストが動作しない件」ですが、見せていただいたテストコードには、setUp()でSystem.setProperty("slim3.useXGTX", "true");の
記述がありましたが、この設定を反映するAsyncDatastoreDelegateが何度もインスタンスが生成されるようであれば良いのですが、メンバとして保持している、
Datastoreクラスがstaticであるため、実行開始後、1回しか設定を読み込まないようになっているようです。したがって、System.setProperty()が呼び出される前にDatastoreクラスのメソッドが呼び出されてしまうとその時点でデフォルトのトランザクションオプションが反映されてしまって、System.setProperty()が意味をなさなくなってしまいます。

これについても検証コードの結果を待ちましょう。

ただ、この後にも話をしたのですが、開発環境HRDのオプションの遅延は、
かなり大きいので、Queryのテストをしたい時にまでHRDの遅延が発生すると
テストが通らない問題に直面してしまいます。
一つは、遅延確率を設定するオプションを0にして遅延しなくするという方法を
提案しましたが、テストクラスの本数が多くなってくると面倒という問題があるようです。
また、Thread.sleep(2000)とか入れてみるという提案もしたのですが、
これも同様に本数が増えてくると、テストの実行時間が長くなってしまうので
全体のテストを行う時に問題になる。ということでした。

今日は、時間も長めにやりましたが、内容も濃かったです。
疲れました。