uehaj's blog

Grな日々 - GroovyとかGrailsとかElmとかRustとかHaskellとかReactとかFregeとかJavaとか -

Entity Groupとその簡単な構成法

GAE/Jのメモね。

以下、エンティティブループでトランザクションが可能な理由を,同一マシン上のテーブルに配置しているから、と予測していましたが、当時BigTableの実装方法は不明であり推測でしたた。実際には、rootによる楽観的排他制御でトランザクションを実装しているとのことであり、結果的に「同一マシン上である」という理由に必然性は無いと思われます。修正してお詫び申し上げます。(2010/6/2)

Entity GroupはGAE上のトランザクション処理の制約に関係する*1インスタンス間の関係です。UMLでいうとコラボレーション図上にツリーとして現れてくるような関係です。またこれは、従来のリレーション(1:1、1:多とか、到達性とか)とは本来直交する概念です*2

Entity Groupの意味ですが、同じEntity Groupに所属するEntity群の間でのみ、トランザクションが可能になります。このことが含意するのは、「1つのEntityGroupに所属するEntity群」は分散DB(Big Table)上でも分散されず、1つのマシン上のストレージ(Tablet?)に保存される(そうとは限らないみたい。2010/6/2削除)ということです*3GAEのデータストアをその上で実現している基盤技術Bigtableは分散トランザクションを持たない分散DBでありますので、マシンをまたがるとトランザクションが不可になります。Entity Groupをまたがるとトランザクションができなくなるというのはそういうことです(2010/06/02削除)。

逆に言うと同一Entity Gropuに対するアクセスはスケールアウトしません。データ量が多くなってくるとアクセスが遅くなるとおもわれます。

GAE/J上のDatastoreをアクセスするためのAPIのひとつであるJDOでEntity Groupを構成するための一番簡単な方法は、Entityに以下のようなフィールド"parentKey"を作っておくことです。

    @Persistent
    @Extension(vendorName="datanucleus", key="gae.parent-pk", value="true")
    private Key parentKey;

    public void setParentKey(Key parentKey) {
        this.parentKey = parentKey;
    }

こうしておいて、Entityを作成した後で永続化する前(newした後、makePersisitent()する前)のタイミングで、親にしたいEntityのKey*4をsetParentしておいてやれば、PrimaryKeyには自動的にAncestor情報つきのKeyが充足されます。

なお、「Entity Groupを構成する」ということの、Datastoreレベルから見た具体的な意味は、Entityに「Ancesor情報つきのKey」を持たせるということです。そういうKeyを生成してEntityに付与することさえできれば、後はGAE DatastoreがそのEntityをmakePersistantする際に自動的にどっかのマシン上のTabletにうまいこと割り振ってくれるというわけです

なお、既に生成/永続化してしまったEntityのPrimaryKeyを変更することはできませんので、Entityが所属するEntity Groupを変更することはできません(似たようなことがやりたければ、新規にEntityを作ってそこにコピーするしかない)。もしできるとしたらそれはマシン間をまたがってEntityが移動することを伴う訳です(原理的にできてはいけないという訳ではないだろうけれども)。

*1:Entity Groupの結びつきの意味を業務モデリングとかと混同しているページもある気がします。開発経験のない身で言うのもあれですが、それはちがう。Entity Groupはデータ設計より下のレベルの、それに直交する、純粋に性能/実装に関するテクニカル情報です。もっというと「最適化のヒント」の逆で、指定すると必ず実害を伴うものです。Entity Groupの使用は無ければ無いほど良い。Entity Groupはスケールアウトしない範囲を明示的に指定させることでその存在を意識させ、使用を最低限にとどめるための、GAEの各所に仕掛けられた「スケールアウトするアプリを書けるようになるための養成ギブス」の一つです。Googleの技術力なら分散txを導入するのは容易いはず。しかしそれをしないことに意味がある。

*2:GAEのJDOのORマッピングではEntity Groupの所属関係がリレーション&Ownership関係に紐づいているけど、これって混乱を招くというかEntity Group濫用をまねきかねないような・・。将来的には区別できるようになると思うけど。

*3:そのほかに、Enity Groupの1段親のフィールドはクエリの条件に指定できる、という効果もある。

*4:上では、PrimaryKeyとしてcom.google.appengine.api.datastore.Keyを使っていますが、KeyFactoryを使えばこの方法でもlongやUnencoded StringをPrimaryKeyとしてもつEntityでGroupを設定することもできるんじゃないかな(未確認)。