2012年6月5日火曜日

GAE/GでバッチGetをした時の注意点

最近、Androidアプリ「ええことなかったわー(β)」を開発したのですが、
実は、データを貯めているサーバ側はGAE/Gで開発しています。

本当に、レスポンスとしては早いですよ。
今のところ、リクエストに対するレスポンスは、
ms単位の時間でレスポンスを返しています。
(GAE/Jでも仕事でサービスを利用していますが、レスポンスの内容が全然違いますが、1s前後のオーダだったりするので…)

先ほど、Admin ConsoleでのPerformanceを最弱にしていなかったので、
まだユーザ数も少ないので、最弱(Max Idle Instance = 1+Min Pending Latency = automatic)にしてみました。その結果は後日…ということで。

さて、本題のバッチGetを利用するときの注意点なのですが、
結論から言うと、「指定したキーのエンティティが1件でも無いと、appengine.MultiErrorを返す」ということです。
実際には、以下のようなコードになると思いますが

data := make([]HogeEntity, len(keys))
if err := datastore.GetMulti(c, keys, data); err != nil {
// 何かのエラー
}

全てのキーのエンティティがあれば、エラーの判定にならない(戻り値=nil)なのですが、
1つでもエンティティが存在しない場合、全てエラー(戻り値 = appengine.MultiError)となります。
(おかげで、全部エラーレスポンスに…)

で、これは困ったという事で、以下のようなチェックを行うことになります。

data := make([]HogeEntity, len(keys))
if err := datastore.GetMulti(c, keys, data); err != nil {
 if _, ok := err.(appengine.MultiError); ok {
  // バッチGetだと指定したキーのエンティティが1個でもない場合、MultiErrorが返る。
  c.Infof("Hoge NoSuchEntity")
  return keys, data, nil
 } else {
  c.Errorf("Batch Get Error %s", err.Error())
  return keys, nil, err   
 }
}

実際、appengine.MultiErrorが返されても、存在しているデータは取得できるので
取得したデータが構造体にセットされている部分と、空の部分がsliceに存在する
状態になるので、後で、空の部分を除外するなど、編集処理が必要となります。

ちなみに、appengine.MultiErrorはtype MultiError []errorと定義されていますので
複数のエラーが含まれた状態になることも考慮しておく方が良いかもしれません。
私はGetだけの利用だったので、今のところ、正常なものは利用しています。

MultiErrorの戻り値はGetMulti/PutMulti/DeleteMultiで戻される事があるという
ことなので、(ドキュメントのBasic Operationsに明記)利用するときはappengine.MultiErrorの中身を確認してみてください。