appengine ja night #5に行ってきた。 #appengine #ajn5

みなさまお疲れ様でした。今回も楽しかったw。


ajn5ではpythonは松尾さん、javaはひがさん。お二人とも有名なフレームワーク開発者という超豪華メンバーだった。その影響かATNDの申し込みは24時間たたずに定員オーバー、申し込みが軽く100人を超えて定員を10人増やすという事になりました。AppEngine人気ですね。
今回は以前にも貸して頂いたひろーーい会場をグリーさんからお借りしました。いつもありがとうございます。


さて、エントリーなんですが、blogを書く前にid:bufferingsさんのブログを読んだら下記の事が書いてあった。。bufferingsさんのまとめが詳細に書いてありすぎてそれ以上どうまとめればいいのかという感じです。詳しくはこちらで終わらせそうな勢い。

たぶんちょっと後に @bluerabbit777jp さんが詳しくまとめてくれると思う。(プレッシャーではないw

おしながき

App Engine アンチパターンその他

AppEngineで動作するKayフレームワークの開発者です。
アンチパターンの紹介でした。アンチパターンとその解決策をコードで紹介して頂けるという濃い内容でした。自分の知らないアンチパターンが見つかると事前に問題を回避できるのですごくためになりますね。

  • 正規化
    • 正規化しすぎないで冗長にデータを保持しようという話。しかし、セッション中に今は検索も早くなっているので変更が入りそうな場合は正規化してもいいかもという話だった。この辺りはバランスですね。
  • 不要なインデックス
    • 検索に使用しないインデックスは作成しないようにindexed=Falseを指定した方がいいよという話。インデックスを作成するとその分Datastoreの容量を消費してしまう。また、可能であればプロパティ名も短いほうが容量節約にはいいとの事。

この時にいい質問が出て。@shin1ogawaさんがつぶやきでまとめてくれています。

[Q]インデクス対象にしない、と指定した属性を、やっぱインデックス対象にしたときは?
→[A]インデクス対象にした以降のものだけインデクスが作成される。インデクスしない状態で保存したエンティティは再度PUTしなおさないとインデクスが生成されない。

スキーマレスなのでレコード毎にインデックスが存在したり、しなかったりという事が発生します。これは要注意ですね。この場合は過去のレコードをget,putをし直してインデックスを作成しましょう。

  • AppEngineではソートにもインデックスが必要になる時がある
    • 「where contents = 'hoge' order by created」みたいなwhere句と異なるキーでorderする場合はインデックスが必要になる。セッション中では可能であればソートはインメモリでするのがいいんじゃないかと言う事だった。
  • Index爆発
    • ListPropertyでコンポジットインデックスを作成するとListに保持している値の組み合わせ分インデックスが作成されてしまう。その結果インデックスの最大件数5000を超えてエラーになってしまうよという話。
  • モジュールロードの遅延
    • 通常はモジュールのロードはプログラムのはじめに宣言して使用するが、AppEngineではspin upの時間もあり可能な限りロードを遅らせた方がいいとの事。
  • SDK1.3.1の新機能 紹介
    • Transactional Task Queue
      • Datastoreでコミットできた場合にのみTask Queueを実行する機能。

この紹介の際に、TaskQueueでセッションを一時間後に削除するということをやっていた。TaskQueueではaddしてすぐに実行ではなくetaを設定すれば指定した時間に実行できるようだ。この機能は初耳だった。

Global Transaction

まずは2相コミットについての説明から始まりその後slim3のGlobal Transactionのソースを読むという進め方。

  • gtxがあればEntityGroupを気にしなくても良い?
    • EntityGroupはできるだけ小さく作るのは変わらない。gtxはEGのLocal Transactionよりも遅い。どうしても必要な場合にのみ使用する必要がある。
  • gtxの処理
    • 1.ロックの取得(Lock Entityをput)
    • 2.ジャーナルの書き出し(Journalをput[Journalには適用したいEntityの更新値を保持])
    • 3.内部的なコミット(こうなると何が何でもJournalでEntityを更新する準備に入る)
    • 4.ジャーナルの適用(Entityの更新)
    • 5.ロックの解放(Lock Entity, GlobalTransaction Entityを削除する)
  • セッション中にソースリーディングしていてAppEngineのはまりどころについて解説があった。
    • DatastoreTimeoutExceptionが出ても実際はputされていることがある。そのため、自動採番なputをリトライすると2重登録される可能性がある。事前に採番しておいた方がいい。

私は事前にgtxのソースをある程度読んでいたのですが、いまのgtxのソースはとても読みやすく勉強になります。App EngineではDBの処理を手組みするような感覚があり、ひがさんがそれをどのようにして実現したのかをソースで読めるのはとても貴重です。ぜひぜひみんなも読むといいよ。

TinyDSのトランザクション

@urekatさんはTinyDSというappengine-jruby用のDatastoreライブラリを作成しています。
その@urekatさんがappengine-jrubyのコミッタになったそうです。パチパチ


今回のBTでは前回の@ashigeruさんの仮想言語をrubyで実現したぽかったです。
TinyDS.tx{}という記述で宣言的トランザクションを実装。そしてBaseトランザクションもTinyDS::BaseTx.create_journa, TinyDS::BaseTx.apply(journals), TinyDS::BaseTx.rollforward。
最後にTinyDSでもついにgtxを実装した!!実装の詳細は上記の資料リンクから是非。

はじめてのGAEモデリング

京都からはるばるajnのために東京に来てくれました!!
bufferingsさんはslim3の日本語翻訳をしてくれた人です。また、appengineサーバ上でjunitを動かすフレームワークKotori Web JUnit Runnerの作者です。
twitterの情報を読み取るアプリケーションを作る時のモデリングをみんなでやるという面白い試み。twitterアカウント名をKeyにしようとしてたのを「アカウント名は変えられますよ。」とツッコミありましたが、ほかはおおむね指摘無しでした。早く完成するといいですね。

SchemaVersionの薦め

私が発表させていただきました。
(あとで補足いれます)と思ったけど、めっちゃ詳しく、しかも私より適切に書いてくれているではありませんかここは委譲を使いますw

  • RDBでは後からテーブルにカラムを追加した場合、既存レコードの追加したカラムにはNULLが設定される。しかし、データストアでは後からKindにプロパティを追加した場合は、既存エンティティにそのプロパティが追加されることはなく、そのプロパティが存在しない状態(missing)となる。これは、プロパティが存在し、それにnullが設定されているのとは異なる。
  • あるプロパティでフィルタまたはソートするには、そのプロパティが存在する必要がある。指定したプロパティが存在しないエンティティはヒットしない。
  • Kindが同じエンティティは、同じプロパティを持っていないと不都合が発生するケースが多い。そのため、プロパティの増減や型の変更に伴い、古い「モデル」に基づくエンティティを最新の「モデル」に合致させる作業が発生する。その時、各エンティティがどのバージョンの「モデル」に対応するものなのか判断したいケースがあり、その目的でschemaVersionプロパティが使われる。

あと、これを通してApp EngineではDatastoreService#get以外のアクセスは必ずインデックスを経由するという所を伝えたかったです。

  • プロパティがある
    • Indexがある
      • 検索できる、ソートできる。
  • プロパティがない
    • Indexがない
      • 検索できない、ソートできない(ソートでも抽出されない)。
  • queryはIndexを経由する
    • RPC二回発生する
      • 遅い
  • getはIndexを経由しない
    • RPC一回ですむ
      • 早い