Hatena::ブログ(Diary)

@vvakame の日記

2011-10-06

複数EntityGroupに対するトランザクション処理が追加されたらしい

| 11:18 | 複数EntityGroupに対するトランザクション処理が追加されたらしい - @vvakame の日記 を含むブックマーク

appengine SDK 1.5.5 prerelease がリリースされました。

https://groups.google.com/forum/#!topic/google-appengine/f9YVplAlRs4/discussion

気になるのはこれ。

  • App Engine now supports Cross Group (XG) transactions with the High

Replication Datastore, which allow you to perform transactions across multiple

entity groups.

すごい!!複数EGに対してトランザクション処理が可能だって!?

appengineだと今まではひとつのEGしかトランザクションの対象に出来なくて、要するに関連性のない複数のレコードの更新がトランザクション下で達成することは出来ない感じになってました。

代表例としてよく言われるのは、銀行の口座間送金ですね。

口座Aからお金を引き落とす。

口座Bにお金を入金する。

をシンプルに1つのトランザクションとして処理することは出来なくて、口座Bへの入金が失敗したらTQで非同期でリトライするみたいな工夫が必要だったんす。

これがちゃんと何も考えなくてもトランザクション下で安全に処理できるようになった。みたいな。

で、ある処理を非同期でやるってのは結構めんどくさいです。

自動でTQを生成するための https://github.com/vvakame/appengine-deferred みたいなライブラリを作ったりもしてたんですが、どうやらかなりの場合に今回のXG transactionでまかなえるんじゃね?しかも同期的に処理出来ることを保証できるパターンが増えるんじゃね?

ってのが今回の(*´Д`)=3 ムッハー! ポイント

検証用コードを書いてみました。

https://gist.github.com/1264065

結果、DevelopmentServer(要するにローカルのエミュ環境)と実環境 共に5EGまで同時に扱えるみたいです。

6EGからは例外が発生すると。

java.lang.IllegalArgumentException: operating on too many entity groups in a single transaction.

ちなみに、HRD専用なのでM/Sを利用しているappIdは移行ツール使って新appIdにお引越ししなければならないみたいです。

面倒くさいけど、やる価値はあるなぁ…!

これは高まる…!(*´꒳`*)

後は11/1からの新料金プランがもちょい手加減してくれれば…(´ω`;)

2011-04-18

appengine-deferred 0.1 作成 #appengine

| 01:15 |  appengine-deferred 0.1 作成 #appengine - @vvakame の日記 を含むブックマーク

appengine-deferred を作成しました。

https://github.com/vvakame/appengine-deferred

DeferredTask について

appengineの1.4.3からDeferredTaskという、RunnableとSerializableのサブタイプなものが追加されました。

これは、DeferredTaskをimplementしたクラスをTaskOptionのpayloadとして渡してTQを起動すると、なんかまぁ /_ah/queue/__deferred__ だかのTQとして起動される、みたいなControllerタイプではない起動の仕方のTQです。多分。

作ったもの

特定のメソッドに @Deferred をつけておくと、例外発生時に自動的にTQでの実行に切り替えてくれるライブラリを作りました。

注意事項がいくつかあるのですが、まぁ後にします。

なんで作ったか

appengine用のコードを書いていて、ある処理を冪等にするために、失敗時には非同期でリトライにする、というコードを何回も書いてきました。全体の作業時間の10%程度を占めている作業なんでないかなーと思います。

僕は、以前からある特定の処理を自動的に非同期的に実行してくれるように変換できないかなー…と思っていましたが、Controller型のTQをベースに考えると、出来なくもないけど使い方も難しく、ライブラリ自体の開発コストが多分50時間超えるだろうなー、って感じでした。

ですが、1.4.3からはDeferredTaskが追加されたのでまぁいっちょ自動化してみるかーっというわけで作りました。だいたい12時間ぐらいかかった気がします。

使い方

APTライブラリなので、APTjarを適当にSlim3っぽく指定してください。

mvnリポジトリも一応用意してあるので、最新の appengine-deferred-usage のpom見てください。

非同期化したいメソッドに @Deferred つけるだけです。

appengine-deferred/SampleService.java at 3d651162457ed61cf9b17bd86287736d27ab0d00 ? vvakame/appengine-deferred ? GitHub

利用側

appengine-deferred/SampleServiceTest.java at 3d651162457ed61cf9b17bd86287736d27ab0d00 ? vvakame/appengine-deferred ? GitHub

アノテーションを付けたメソッドが属するクラスが SampleService とすると、自動的に SampleServiceDeferred というクラスができ、同名、同シグニチャメソッドが定義されるので、それを呼び出すようにしてください。

想定されている例外について、DeferredUtil.throwWithValue(...) 経由で再throwすると、XxxDeferredクラスの方でキャッチされ、自動的に非同期で再実行されます。

呼び出し側ではthrowWithValueに一緒に渡した値が返ってくるので、処理が成功したか失敗したかは気にせず処理を継続することができます。

例外発生時に DeferredUtil.throwWithValue(...) で処理しなかった場合、想定外の例外ということで非同期化せず、普通に呼び出し元に伝播します。

あと、特定の例外が発生した時にメール送りたいなー、みたいな時のためになんかまぁ独自処理を追加できるようにしました。

SampleService.ownTask() とか、その辺みてください。

注意

冪等であるかを強く意識しつつ利用しないと、データの整合性が簡単に壊れちゃったりする気がします。

DeferredTaskはSerializeして投げられるので、呼出し元から渡される引数インスタンスの状態を変更するようなメソッドを自動で非同期化すると、冪等にしたつもりでなってなくてうぎゃー!とかなったりすると思います。引数はImmutableであると考えてコードを書いたほうがいいと思います。

あとがき

多分僕が自分で使って幸せになる!ぐらいしか考えてないので、説明がだいぶ適当だと思います。

使ってみたい人がいたら、分からないところがあったら聞いてくれれば多分答えます。

2011-02-22

ajn14行ってきたん

| 23:56 | ajn14行ってきたん - @vvakame の日記 を含むブックマーク

appengine ja Night #14 行ってきたん。http://atnd.org/events/12652

知らない単語がいっぱいあったので、メモっておく。

間違ってたら、誰か教えてください。

知らなかった 理解しておく

シャード技法。全体をいくつかに分割して扱う。分割したひとつひとつを"シャード"と呼ぶみたい
ShardingCounterテクニック。あおうささんのブログとか読む。
タブレット300MBくらいのEntityの塊。同じEGのEntityは連続するらし。聞いたことある。要おさらい。
Scatter0.8%の確率で自動的にEntityに付加されるプロパティ。EntityのKeyのハッシュらしい。一様分布のはずなので、シャードを作る時に、シャード毎のデータ件数が涙目で分割する手法に比べて均一になり易いはず。らしい。BeginKeyとEndKey得られるよね的な。Entity.SCATTER_RESERVED_PROPERTY。
Memcacheのexpire有効期限の事。そんなん設定できたっけ?
AppEngine MapReduceGoogleがオープンソースで作ってる。Hadoopと同じAPI。まだReducerないらしい。
eventual consistency readよくわかってない。整合性について、古いEntityがみえちゃうかもよ的な意味?こんしすてんし。zakiさんに教えてもらった http://www.publickey1.jp/blog/09/eventual_consistency.html

知らなかった 存在だけ覚えておく

ElShardエルシャダイじゃないよ。
PAXOSぱくそす。HDRの技術的バックボーン?よくわからんけど、むつかしいねんて。

知ってた!

もしくは、昨日くらいまでに知った。

TQTaskQueue
txTransaction
EGEntityGroup
TPSTransactionPerSecond。ThirdPersonShootingじゃないんね。
HDRHigh Replication Datastore. なんか比較的最近始まったアレ

その他の知識

  • HDRは同一EGに対する更新性能は最悪1TPSくらい出る
    • 普通のDatastoreは10TPSくらいっておがわさんに聞いた
  • ajn行くとピザ食える

2011-01-30

Android+slim3(appengine)連携の冴えたやり方?

| 12:08 | Android+slim3(appengine)連携の冴えたやり方? - @vvakame の日記 を含むブックマーク

わかめの言ってること分からんって言われたのでちょっと書き直しました。

Android+appengineは、昔きょーりゅーてんていが言ってた通り、夢の組み合わせなわけですよ。

世界中で動く端末!個人が気軽に使える(気分的に)無限にスケールするサーバ!!

ただ、今まで冴えたサーバ、クライアント間連携の仕方って思いつかず、2〜3ヶ月くらいずっと悩んでました。

最近、一応現実的な解を思いついたので、ここに書いてみんなから意見を貰えたらなぁと思います

目指すこと

サーバ側とクライアント側での通信内容を解釈するためのコーディングをほぼゼロで済ませたいし、テスト可能な作りにしたい。

手作業で通信内容をパースするような処理を書くと、エンバグする可能性がある。こわい><

そのために、フォーマットを規定するPOJO自体を共有化したい。

前提

通信は基本的にJSONで行う。

JsonPullParserをガンガン使う (次バージョン(0.3?)からDeserializeだけじゃなくSerializeも出来るようになる予定)

結論

クライアント←クライアント用モデルプロジェクト→サーバUnitTest こうする。

方針

で、そのためには、サーバとクライアントの間でモデルの共有(Encoder, Decoderの共有化)が必要だとずっと思っていました。

つまり、 クライアント←共通モデル共有用プロジェクト→サーバ っていう形ですね。モデルにJsonPullParserを適用してJSONでやりとりする感じです。

ただ、単純にこの方法を推し進めようとすると無理があることがようやくわかりました。

困ること

1. Slim3アノテーションが邪魔

ただまぁ、これはSlim3の@ModelはリテンションがSOURCEでコンパイルすれば消えてしまうアノテーションであることと、@AttributeはリテンションがRUNTIMEですが、これもまぁAttributeのモックアノテーションを作ってクライアント側で利用するか、ProGuardかなんかでアノテーションを取り除いたclassを生成してやればいいのかなという気がしなくもないです(できるか知らないけど)。

2. サーバ側EntityとAndroidのモデルを共有化するとデコンパイルされてサーバ側の保持データがバレる

デコンパイルされるとかなり困る。例えば、JSONでやりとりしているデータ以外に更新日付やアクセス元IPアドレスを保持している、とか、JSONでやりとりしないデータに、サーバ側の動作制御用Entityが絡んでいて、コンパイルを通すためにはそのEntityも含めないといけないので制御用Entityの構造も一緒くたにバレる。

とかがあって、サーバ側とクライアント側のモデル(=JSON変換ライブラリ)を共有化するのは辛いよなぁ…という結論になりました。

最初は1だけしか気がついてなくてここさえクリア出来れば…!!とか思ってたんですけど、2がでた瞬間サーバ側Entity構成を全部バラさざるを得ないことが判明して挫折したわけです。

解決方法

結局、サーバ側とクライアント側のモデル共有化は諦めることにしましたorz

で、代替案としてどうすることにしたのか?

クライアント←クライアント用モデルプロジェクト→サーバUnitTest こうすることで、クライアント側とのIFの整合性が取れていることを自動的に担保するようにすればいいのではないかと思いました。

具体的には

  1. クライアント用モデルプロジェクト作成
    1. サーバから送られてくるデータにあわせてPOJOを作成
    2. JsonPullParserを適用
  2. antでコンパイルし、クライアントにjarをインポートして利用 (POJO+JsonPullParser生成物)
  3. mvnでコンパイルし、サーバ側で scope test で利用
    1. UnitTestで処理結果をクライアント側モデルに突っ込み、エラーが発生しないことを確認

ていう感じでやっていってみようかと。

今回テストしているのはサーバ→クライアント側の通信だけですが、まぁ今回はこれでいいかなと

クライアント→サーバ側通信で考えられるのは

  1. 普通の ?hoge=fuga&hige=nobiru
  2. JSONをPOSTで送信

の2パターンないしその組み合わせかと思います。

1についてはまぁ適当にやればいいですし、2についてはSlim3 1.0.6からSimpleControllerというInputStreamが扱えるコントローラが増えたので、それを使えば楽に検証できていいと思います。

参考リンク 404 shin1のつぶやき ないわー Not Found: #slim3 1.0.6 のSimpleControllerを使った場合のテスト方法 #appengine

問題点

クライアント側とモデル編集をする人と、サーバ側の担当者が別だと、モデル編集されてコミットされるとサーバ側のテストが壊れてHudsonさんに超絶怒られる。

→めんどくなって手動でjarを更新したものがHudsonさんにも適用されるように設定を変える→即時チェックの恩恵が薄れる→喧嘩になる

こういう未来が見える('A`)ドウシヨウ

将来的には

モデルの共有化は辛いということがわかったので、O/RマッパーならぬO/Oマッパーが必要なのかなぁ…と思ったり。あるクラスとあるクラスで、名前が同じフィールドがあったら相互変換できればいいよね的な。

Twitterでもうちょっとおはなししました。

higayasuohigayasuo 2011/01/30 13:09 デコンパイルされても実際のデータがバレることがなければ大丈夫だと思うけど。
Slim3は実行時にはアノテーション使わないのでリテンションをソースにしてもいいですよ。

vvakamevvakame 2011/01/30 13:30 Twitterの方でもちょっと書きましたが、"こういうデータも取られてるのかぁ…"的なこととか、サーバ側の構造を漏らすことによって攻撃手法を考える糸口にされる可能性とかありそうだなぁ…と考えると、怖いのです…。
@Attributeについて、リテンションがCLASSになっているのがSOURCEになると、.apt_generatedを含んでjar→サーバ用 含まないでjar→クライアント用 みたくできて嬉しい気がします!

vvakamevvakame 2011/01/30 13:31 CLASSじゃなくてRUNTIMEでしたorz

2010-06-15

TwitHubBridge作りました。

| 20:56 | TwitHubBridge作りました。 - @vvakame の日記 を含むブックマーク

Twitterで呟いたTODOがgithubに連携されるといいな、と思って作りました。

http://twithubbridge.appspot.com/

TwitterのScreenName(vvakameとか)と、githubのusername(vvakameとか)とAPI KEYを登録しておくだけ!

後は、 "TODO projectName Title/Body"の形式のツイートをすると勝手に登録されます。

http://twitter.com/vvakame/status/16186106412

↑これが↓こうなる

http://github.com/vvakame/DroppShare/issues/#issue/1

おおむねTweetから30分以内には突っ込まれるんじゃないでしょうか。

API KEYは復号化しないと使えないので、制作者の僕にはAPI KEYが分かります。めんどいけど。

わかめは悪い奴じゃなさそうだよなー。と思う人だけご利用ください。

バグとかあって同じIssueがたくさん突っ込まれたりしても泣かない!

http://github.com/vvakame/TwitHubBridge

ソース置いてあるので、自分で適当に設置して使ってもらってもいいんじゃないかと思う。