balmysundaycandy-marble-brokerをリリースしてはいませんが使えることは使える状態にしました!

さて、ajn5が終わりブログに感想も書かず何をしていたかというと、松尾さんのプレゼンで見たappstatsをパクっていました。結構前からやはり松尾さんのつぶやきで存在は知っていたのですが、いろんな事情で手が付けられない感じでした。

とはいえ、@bufferings氏や@ashigeru氏のpushと倍pushを受けて、これは土日にやらねばならぬとして簡単にですがものをつくってみました。もちろん、どの土日かは宣言していないので、「つまり・・・・我々がその気になれば・・・リリースは 10年後 20年後ということも、可能だろう・・・・・・・・・・ということ・・・・! 」です。

まぁ常識の範囲ということで、デモが動くようにしています。見切りでリリースしてもいいのですが、それはfairでない気もするので。

説明が遅れましたがいちおうappengineに詳しくない人向けにappstatsについて簡単に書いておきます。appstatsというのは、appengine上で動作しているコードのrpcをロギングするツールです。rpcというのは、例えばbigtableに対するアクセスとか、メールを送ったりとか、そういうもののことをイメージしてもらえればだいたい正解です。一般名詞としてのrpcもあると思いますが、ここではappengine上のサービス呼び出しととらえてもらった方がわかりやすいです。appstatsは、appengine sdk 1.3.1からpython版の本体に組み込まれましたが、java版には類似のツールはありません。呼び出し回数と呼び出しのタイムラインが見れたりします。pythonなので、rpcの呼び出しもとのソースコードが直接見れたりします。


前置きはさておき、もののデプロイは、

http://marble-broker.latest.balmysundaycandy.appspot.com/

にしているので、quota/limitにかからない限りは遊んでもらえます。

タブがいくつかあるので、簡単に説明します。

・brokerage config
設定をします。ここでチェックを入れてbrokerモードにすると、apiproxyのdelegateを書き換えてrpcのログをとりにいくようになります。チェックなしでupdateすればbrokerモード解除になり、ログは取得されなくなります。

・timeline
まだ実装していないですが、実装したいですね。ログのデータ構造も換えないと実現できないので、こなれたuiが要求されることも考えるとちょっと時間がかかりそうです。

・call count
rcpごとの呼びだし回数です。素直にqueryしているので、1000件制約があります。また、手を抜いた実装で、servicename - methodnameのペアごとにqueryしているので、run queryのカウントがもっさもっさあがっていきます。あまり気にせず。

・detail logs
rpc呼び出しの詳細です。どの呼び出しが行われたか、rpc呼び出しに使用されたrequest/responseのprotocol buffer表現を取得します。appengineではrpc callに使用するprotocol bufferを開発者から隠蔽していないので、こういうことができます。low level apiのオブジェクトモデルがrpc callに使用するprotocol bufferに変換されて使用されており、後者の方が多くの情報をもっていることが多いので、いろいろと内部動作の理解についての参考とすることができます。例えば、entity groupの実体もget/putのprotocol buffer表現では明示的にelementとして実現されていたり、ということが見えます。この辺は百聞は一見にしかず、ということで、興味があればデモを見てみたりするといいかなぁと思います。一応、呼び出しのstatck traceもデータとしては持っているのですが、表示するのはもっさもっさし過ぎなのでviewからは省いています。というようなプロパティがいくつかあります。このviewでデータをすべて削除することができますが、それはconfigにあった方が自然だなぁと書いてて思ったり。

・snatch something
デプロイされているモジュールなので、なんかログをとってみたいかなぁという操作を用意しておきました。適当にリンクをクリックすると、その呼び出しが行われます。実装は、slim3とlow level apiを使っています。ソースコードへのリンクも用意したので、詳細はそちらを参照してください。


ところで、名前の考案には、ぶいてくのたけざきさんにもご協力いただきました。marble-brokerというのは、多分ajn5の懇親会でたけざきさんにだしてもらったアイディアのはずです。お酒が入っていたので完全に確証はないですが。。ちょっと別の件での名前だしだったのですが、brokerという響きがよさげなのでここで使わせてもらいました。

たけざきさん、ご協力ありがとうございました。


ソースコードは、

svn checkout http://balmysundaycandy.googlecode.com/svn/trunk/balmysundaycandy-marble-broker

にあるので、触ってみたいひとはここからとっていってください。classpathの関係で別のプロジェクトをとらないとコンパイルエラーになりますが、これを触りたい人なら問題ないと思います。


では、簡単にmarble-brokerの威力を見てみましょう。
ajn5のひがさんのセッションでもgtxのサンプルとして挙げられていた、"異なるentity groupに属する複数entityのgtxによるput"の中をすかし見るとこのようなrpcが実行されているのがわかります(slim3はいま最適化中のようなので、すでに若干変わっているかもしれません)。

service name method name request response spendTimeMilles
datastore_v3 BeginTransaction handle: 0x7bf3e447e7a5fb1f app: "balmysundaycandy" 32
datastore_v3 Get key < app: "balmysundaycandy" path < Element { type: "Test" id: 21353 } Element { type: "slim3.Lock" id: 1 } > > transaction < handle: 0x7bf3e447e7a5fb1f app: "balmysundaycandy" > Entity { } 31
datastore_v3 Put entity < key < app: "balmysundaycandy" path < Element { type: "Test" id: 21353 } Element { type: "slim3.Lock" id: 1 } > > entity_group < Element { type: "Test" id: 21353 } > property < name: "timestampType" value < int64Value: 0x126cd9a2ee0 > multiple: false > property < name: "globalTransactionKey" value < ReferenceValue { app: "balmysundaycandy" PathElement { type: "slim3.GlobalTransaction" id: 21302 } } > multiple: false > > transaction < handle: 0x7bf3e447e7a5fb1f app: "balmysundaycandy" > key < app: "balmysundaycandy" path < Element { type: "Test" id: 21353 } Element { type: "slim3.Lock" id: 1 } > > cost < index_writes: 0 index_write_bytes: 0 entity_writes: 0 entity_write_bytes: 0 > 21
datastore_v3 Commit handle: 0x7bf3e447e7a5fb1f app: "balmysundaycandy" cost < index_writes: 5 index_write_bytes: 5 entity_writes: 1 entity_write_bytes: 435 > 48
datastore_v3 BeginTransaction handle: 0x11300760da2bcf42 app: "balmysundaycandy" 15
datastore_v3 Get key < app: "balmysundaycandy" path < Element { type: "Test" id: 21354 } Element { type: "slim3.Lock" id: 1 } > > transaction < handle: 0x11300760da2bcf42 app: "balmysundaycandy" > Entity { } 19
datastore_v3 Put entity < key < app: "balmysundaycandy" path < Element { type: "Test" id: 21354 } Element { type: "slim3.Lock" id: 1 } > > entity_group < Element { type: "Test" id: 21354 } > property < name: "timestampType" value < int64Value: 0x126cd9a2ee0 > multiple: false > property < name: "globalTransactionKey" value < ReferenceValue { app: "balmysundaycandy" PathElement { type: "slim3.GlobalTransaction" id: 21302 } } > multiple: false > > transaction < handle: 0x11300760da2bcf42 app: "balmysundaycandy" > key < app: "balmysundaycandy" path < Element { type: "Test" id: 21354 } Element { type: "slim3.Lock" id: 1 } > > cost < index_writes: 0 index_write_bytes: 0 entity_writes: 0 entity_write_bytes: 0 > 14
datastore_v3 Commit handle: 0x11300760da2bcf42 app: "balmysundaycandy" cost < index_writes: 5 index_write_bytes: 5 entity_writes: 1 entity_write_bytes: 435 > 42
datastore_v3 Put entity < key < app: "balmysundaycandy" path < Element { type: "Test" id: 21353 } Element { type: "slim3.Journal" id: 1 } > > entity_group < Element { type: "Test" id: 21353 } > property < name: "globalTransactionKey" value < ReferenceValue { app: "balmysundaycandy" PathElement { type: "slim3.GlobalTransaction" id: 21302 } } > multiple: false > raw_property < meaning: 14 name: "content" value < stringValue: "j j\020balmysundaycandyr\f\013\022\004Test\030\ufffd\001\fr\020\032\004name \000*\006\032\004test\ufffd\001\f\013\022\004Test\030\ufffd\001\f" > multiple: false > raw_property < name: "deleteAll" value < booleanValue: false > multiple: false > > entity < key < app: "balmysundaycandy" path < Element { type: "Test" id: 21354 } Element { type: "slim3.Journal" id: 1 } > > entity_group < Element { type: "Test" id: 21354 } > property < name: "globalTransactionKey" value < ReferenceValue { app: "balmysundaycandy" PathElement { type: "slim3.GlobalTransaction" id: 21302 } } > multiple: false > raw_property < meaning: 14 name: "content" value < stringValue: "j j\020balmysundaycandyr\f\013\022\004Test\030\ufffd\001\fr\021\032\004name \000*\007\032\005test2\ufffd\001\f\013\022\004Test\030\ufffd\001\f" > multiple: false > raw_property < name: "deleteAll" value < booleanValue: false > multiple: false > > key < app: "balmysundaycandy" path < Element { type: "Test" id: 21353 } Element { type: "slim3.Journal" id: 1 } > > key < app: "balmysundaycandy" path < Element { type: "Test" id: 21354 } Element { type: "slim3.Journal" id: 1 } > > cost < index_writes: 6 index_write_bytes: 6 entity_writes: 2 entity_write_bytes: 1043 > 174
datastore_v3 Put entity < key < app: "balmysundaycandy" path < Element { type: "slim3.GlobalTransaction" id: 21302 } > > entity_group < Element { type: "slim3.GlobalTransaction" id: 21302 } > raw_property < name: "version" value < int64Value: 1 > multiple: false > > key < app: "balmysundaycandy" path < Element { type: "slim3.GlobalTransaction" id: 21302 } > > cost < index_writes: 1 index_write_bytes: 1 entity_writes: 1 entity_write_bytes: 401 > 39
datastore_v3 Put entity < key < app: "balmysundaycandy" path < Element { type: "Test" id: 21353 } > > entity_group < Element { type: "Test" id: 21353 } > property < name: "name" value < stringValue: "test" > multiple: false > > entity < key < app: "balmysundaycandy" path < Element { type: "Test" id: 21354 } > > entity_group < Element { type: "Test" id: 21354 } > property < name: "name" value < stringValue: "test2" > multiple: false > > key < app: "balmysundaycandy" path < Element { type: "Test" id: 21353 } > > key < app: "balmysundaycandy" path < Element { type: "Test" id: 21354 } > > cost < index_writes: 6 index_write_bytes: 6 entity_writes: 2 entity_write_bytes: 615 > 48
datastore_v3 Delete key < app: "balmysundaycandy" path < Element { type: "Test" id: 21353 } Element { type: "slim3.Journal" id: 1 } > > key < app: "balmysundaycandy" path < Element { type: "Test" id: 21354 } Element { type: "slim3.Journal" id: 1 } > > cost < index_writes: 6 index_write_bytes: 6 entity_writes: 2 entity_write_bytes: 504 > 45
datastore_v3 Delete key < app: "balmysundaycandy" path < Element { type: "Test" id: 21353 } Element { type: "slim3.Lock" id: 1 } > > key < app: "balmysundaycandy" path < Element { type: "Test" id: 21354 } Element { type: "slim3.Lock" id: 1 } > > cost < index_writes: 10 index_write_bytes: 10 entity_writes: 2 entity_write_bytes: 498 > 49
datastore_v3 Delete key < app: "balmysundaycandy" path < Element { type: "slim3.GlobalTransaction" id: 21302 } > > cost < index_writes: 1 index_write_bytes: 1 entity_writes: 1 entity_write_bytes: 271 > 41

なにがすごいの?って言われるとこまるので、中をじっくり見て感慨を味わってください。



実は呼び出しの実時間millsが見えるのが一番いいんじゃないかと思ったのですが、それは言っちゃだめーーーーー!!