App EngineのEntityGroupを理解しよう
App EngineのEntitiGroupは、Keyの親子関係を利用して組み立てられたEntityの集まりです。
Entityとは、Bigtable上の1つの行で、ユニークに識別するためのKeyを持っています。
Keyは、種類をあらわすkindとAppEngineから自動的に採番されるidもしくはアプリケーション側で自由に決めることのできるnameで構成されます。
通常は、AppEngineの自動採番に任せますが、Emailのアドレスをキーに使いたい場合などは、nameを使います。kindはテーブル名のようなものだと思ってください。
Keyの親子関係は次のようにして作ります。
Grandparent, Parent, Childがkindです。createKey()の最初の引数に親のKeyを渡すことで親子関係が形成されます。
Key grandparentKey = KeyFactory.createKey("Grandparent", "しげお");
Key parentKey = KeyFactory.createKey(grandparentKey, "Parent", "ひでお");
Key childKey = KeyFactory.createKey(parentKey, "Child", "やすを");Entity grandparent = new Entity(grandparentKey);
...
ds.put(grandparent);
Entity parent = new Entity(parentKey);
...
ds.put(parent);
Entity child = new Entity(childKey);
...
ds.put(child);
しげおさんは、親となるKeyをもっていません。このようなEntityをrootのEntityといいます。
Bigtableは、行(Entity)に対するAtomicな操作(楽観的排他制御)をサポートしています。ざっくりいうと、行単位のトランザクションは保証されていると思って大丈夫です。逆に言うと、複数行に対するトランザクションはサポートしていません。
先ほど説明したEntityGroupはトランザクションの単位でもあります。EntityGroupを作る目的は、トランザクションにあるといってもいいでしょう。なぜ、EntityGroupは複数行で構成されているのにトランザクションが使えるのでしょうか。Bigtableは行単位のトランザクションしかサポートしていないのに。
AppEngineは、トランザクション中にEntityがアクセスされると、そのEntityGroupのrootとなるEntityを取得し、トランザクション用に更新します。つまり、rootとなるEntityのatomicな更新が成功すれば、EntityGroup全体のトランザクションは成功、rootの更新に失敗すれば、EntityGroup全体のトランザクションを失敗させます。
複数行を更新する場合でも、トランザクション制御は、rootとなるEntityのトランザクション制御で代用しているわけです。
例えば、先ほどの例で、トランザクション中にAさんはひでおさんを更新し、Bさんはやすをさんを更新したとします。AさんもBさんもしげおさんの行のBigtableのトランザクションの成否によってEntityGroupのトランザクションの成否が決まります。
Bigtableは1行に対するトランザクションしかサポートしていませんが、AppEngineではこれにEntityGroupという概念を導入し、rootのEntityに対するトランザクションで、EntityGroupのトランザクションを制御しているのです。
あわせて読みたい。
Life is beautiful: Google App Engine入門:Entity Groupとトランザクション処理
rootのentityによる楽観的排他制御がEntityGroupのポイントなので、「ある時点でハノイの塔に触ることが出来る人は常に一人」というのは悲観的排他制御をしているように思われ誤解を招くと思います。
悲観的排他制御をすると多くの場合、スケールアウトしないので、楽観的排他制御を使っているということは重要なポイントです。