タグ「gaeja」 の検索結果(1/1)

2012年2月24日 23:54

appengine ja night #19に参加しました #ajn19

凛子かわいいよ凛子(挨拶)

@shin1ogawaさんのご好意により appengine ja night #19で LTをさせてもらいました。


自分の発表資料


喋り忘れたこととか
  • AZusaar!のAPIを使っていいか?という質問に関して補足
    • 外部公開してるAPI系はquutaにかからないようにがっつりチューニングしてるので他アプリから多少呼ばれても問題はないはず
    • jsonpだとFrontend Cache Controlが効かなくなるのでできればjson推奨
    • APIのリクエストとレスポンスは基本的にATND準拠ですが、AZusaar!で使ってない部分に関しては基本全部削っています
    • jsに書かれていなければ実装されてないという認識でOKですw
    • jsと実際のレスポンスから仕様と空気を読んで自己責任でご利用ください
    • 更に需要がありそうならどっかで仕様をまとめます
  • 尺の関係で自分が賞をとったAPIの説明ははしょりましたが、ジオどすAPIの説明はここに置いていきます。(社内向けなんで内容はそんなに深くないですがw)
  • 一番アクセスがあった(と思われる)自分がLTしてた時間帯も1インスタンスで捌きれていたみたいです
  • 課金は$2.1/weekのみですが、Billing Onしたら$50貰えるキャンペーンの分がまだ半分くらい残っていますw
  • テスト周りについて
    • Java: JUnit+QuickJUnitでがっつりTDDやってました。プロダクト1700行に対してテストケースは250個くらい(djUnit換算なので実際はもっと行数あるかも)。カバレッジはline, branchともに85%くらい
    • JavaScript: QUnitを使用。プロダクトコード1200行に対してテストケースは50個くらい。ロジックに関するテストはやってますがDOM生成周りはテストをサボってますorz
    • クロスブラウザ周りのテストは手元にある環境は全部試してます。WinだとIE9, FireFox, Chrome, Safari。MacもIEない以外は一緒。あとはAndroid(IS03)とiPad
    • スマホUIの確認にはMobilizerが重宝しました。機能的にはXCodeのシミュレータには劣るけどAdobe AIRなのでWinでも動くのは嬉しい

追記:主催の@kazunori_279さんのブログに他の方の資料やust録画がまとまっています

2011年12月15日 22:10

AZusaar!がMashupAwardを受賞しました

12/11(日)にMashup Awards 7の授賞式に参加し、AZusaar! がジオどす賞を受賞しました。

ジオどすAPIを使ってる作品が全部で4作品だけしかなかったため、最初受賞の連絡がきた時はうすうすこの賞かなぁとは思ってましたw


ジオどすの「ジオコーダ前処理API」は、京都の住所用と思われがちですが、その他の住所でも「ビル名等を判断して削除」する機能があります。
ATND、こくちーずなど、「セミナー」という性質上、ビル名・部屋名は必須情報であり、それが地図の自動表示を邪魔していることが非常に多くあります。
この作品では、対象サイトの地図が住所検索に失敗してアフリカ沖を指している(緯度経度が0度)イベントに対しても、きちんと近隣のホテル検索が機能していました。これはイベントの住所をジオどすの「前処理API」を通した上でジオコーディングしていて、まさに本APIが本領発揮できる場所で使って頂いたことを高く評価いたしました。

を読んでちょっと感動。ジオどすAPIは結構便利なのでジオエンコーディング(住所から緯度経度を算出)をするのなら使って損はないと思います。

授賞式の様子。

僕は1:36:00付近に映ってます。受賞台の上であずにゃんペロペロしたのは当日の思いつきです。iPadって便利ですねぇw

貰った物

賞状
MA7_1.png

副賞の舞妓さん無料体験券
MA7_2.png

今流行りの男の娘ですねw 京都に行くとしたら3月か5月の連休かなぁ・・・

その他、Tシャツや各種ノベリティを貰いました。


今回の賞は自分だけの力だけで獲れたというわけではなく、かわいいあずにゃんを描いてくれたのみあっきゅさん@akkyu323)とデザイン修正をしてくださったほっしーさん@hossie)、各種APIを提供してくださった企業様、普段参加しているGoogle App Engineの勉強会(appengine ja night)のナレッジのおかげだと思います。

本当にありがとうございます!

来年こそは100万円を獲りたいところ。(それ以前にネタを思いつくかどうか怪しいですがw)

【2012/1/7追記】
授賞式の写真がupされました

2011年11月30日 23:00

Memcacheに1MB以上のオブジェクトを格納する

AZusaar! ではDatastoreからとってきた検索結果を100件ずつMemcacheに入れているのですが、最近ちょっとDatastore Reads Oppsが増えてきました。

チューニング前

これが一番多かった時ですが、$0.11/日課金されてます。普通だとFrontEnd InstanceやDatastore Writesで課金がかかると思うんですがうちの場合これらは余裕ですw

これくらいなら微々たるものなのですがなんとかチューニングできないかと課金対策してみました。

とりあえず検索結果を100件ずつではなく全件Memcacheに入れるようにしたところ、下記のようなエラーが発生。

Uncaught exception from servlet
com.google.appengine.api.memcache.MemcacheServiceException: Memcache put: Error setting single item (dev20111122.354967843445918452_KokucheeseSearchService__ALL_201111)
	at com.google.appengine.api.memcache.AsyncMemcacheServiceImpl$7.transform(AsyncMemcacheServiceImpl.java:427)
	at com.google.appengine.api.memcache.AsyncMemcacheServiceImpl$7.transform(AsyncMemcacheServiceImpl.java:419)
	at com.google.appengine.api.memcache.MemcacheServiceApiHelper$RpcResponseHandler.convertResponse(MemcacheServiceApiHelper.java:59)
	at com.google.appengine.api.memcache.MemcacheServiceApiHelper$1.wrap(MemcacheServiceApiHelper.java:98)
	at com.google.appengine.api.memcache.MemcacheServiceApiHelper$1.wrap(MemcacheServiceApiHelper.java:92)
	at com.google.appengine.api.utils.FutureWrapper.wrapAndCache(FutureWrapper.java:57)
	at com.google.appengine.api.utils.FutureWrapper.get(FutureWrapper.java:98)
	at com.google.appengine.api.utils.FutureWrapper.get(FutureWrapper.java:90)
	at com.google.appengine.api.memcache.MemcacheServiceImpl.quietGet(MemcacheServiceImpl.java:27)
	at com.google.appengine.api.memcache.MemcacheServiceImpl.put(MemcacheServiceImpl.java:79)
	at org.slim3.memcache.MemcacheDelegate.put(MemcacheDelegate.java:551)
	at org.slim3.memcache.Memcache.put(Memcache.java:361)

エラーメッセージからは分かりづらいですが、Memcacheに入れようとしたデータが1MB超えているためのエラーのようです。(実際計算したら1.3MBくらいでした)

シリアライズした状態のバイナリは実は全然圧縮されていないため、gzip圧縮かけてMemcacheに入れるためユーティリティを作りました。

(appengine 1.6.0, slim3 1.0.15で確認済)

使い方はテストコードを見ればだいたい分かると思いますが、Slim3のMemcacheとだいたい同じです。
注意点としてはrootPackage配下に置いておかないとhotReloading時にエラーになること。

気になるProduction Server上でのパフォーマンスはこんな感じです。

put時 (1.3MB -> 286KB)
serialize:53ms
compress:121ms

get時 (286KB -> 1.3MB)
uncompressed:55ms
deserialize:19ms

※データの大部分がStringなので結構圧縮率が高いです

圧縮や再シリアライズによる多少のオーバーヘッドはありますがこれくらいなら許容範囲かと。
体感的にもそんなに変わってないです。
他のライブラリに依存していないのでspin upによるコストもありません。

普通だと圧縮してまでMemcacheに入れるということはあまりないと思いますが、もしどうしても1つのデータとしてMemcacheに入れたい場合はこういう方法もあるといういい例ですね。

このチューニングを施した後は見事Datastore Readsを$0に抑えることができました。わーい!
チューニング後


2011年10月22日 0:25

総合イベント検索サービス「AZusaar」をMashup Awards 7に応募しました

「総合イベント検索サービス」ってのは今考えました<挨拶


というわけでMashup Awardsに応募してみました。


エンジニア界隈で比較的メジャー(?)なATND, Zusaar, こくちーず, PARTAKEを一度に検索できるようにしました。遠征クラスタのために会場近くのホテルも検索できます。

名前の由来は「ATND + Zusaar = AZusaar → あずにゃんペロペロ〜(^ω^)」です。
このネタのためだけにフォロワーの人にペロペロできるあずにゃんの絵を描いてもらいましたw


今回自分なりの新しい試みとしてフルAjaxでやってます。
サーバサイドではjsonを返すだけのシンプルな設計にして、画面は全部JavaScriptで動的に生成。フロントエンドのコード比率はJava : JavaScript = 1 : 9 といったところでしょうか。cronはJavaで泥臭くやっていますがw

以前TDD BootCamp 札幌2.1の講演した時に「spin upと戦うためにはAjaxを使うしかない」と言いましたが、その集大成がこれですね。自分で言うのもなんですがappengineの長所を最大限生かしてると思いますw

あと、今回はGoogle App Engineの他に一部Herokuも併用しています。本当ならappengineで全部やってしまいたかったのですが、一部APIをappengineのProductionサーバからを叩くとエラーになるため仕方なくAPI用のHerokuでサーバを立てました。(LocalサーバやProductionサーバのktrwjrなら問題ないんですが、URLから叩くとタイムアウトエラーに・・・)

大賞100万円と贅沢は言わないので、せめてどっかの賞にひっかかってくれるといいんですががが

2011年10月 1日 22:00

indexの更新遅延を考慮した実装を行う

実はバリデータ関係でちょっと問題があったりします
これは会議室の名前の重複をチェックするバリデータですが、appengine固有の問題により正常に動きません。
ユニットテストでも問題ないため最悪本番稼動した後に発覚する一番質の悪いバグですね。これに関してはまた別の機会に書きます。
(これはappengineで何らかのサービスを運営してしてないと気づかないかなぁと)

前回のブログでこんなことを書いてましたが解答編です。タイトルにもあるように、インデックスの更新遅延が原因でバリデータは正常に動きません。(appengineじゃなかったらこういう問題はおきないんですがねw)

Entity自体はすぐにDatastoreに反映されるのですが、indexの更新には若干ラグがあります。厳密に調べたことはありませんがひどい時で1?2時間くらいindexが更新されなかったことがあります。
検索系は多少古い検索結果が返ってくることを許容することでいけますが、データの整合性に関してはそうもいきません。

例えば今回の場合だと登録するタイミングによっては同一名の会議室が存在する可能性があります。

よってindexやqureyを利用しない重複チェックを使用することになります。解決方法としては2つあります

1. keyに会議室の名前を入れる

keyはuniqueなのでこれがベストですね

2. Datastore.putUniqueValueを利用する

keyを利用するのがベストなんですが、運用中で容易にkeyを変えられないという場合があります。また、key以外の要素でもuniqueな要素を持たせたいというのはあると思います。
そういう場合はSlim3のDatastore.putUniqueValueを利用するのがいいです

これはソースを見ててたまたま見つけたやつなんですが、uniqueな名前を管理するためのEntityを1つ作ってしまうというやり方です。valueをDatastoreのkeyにしているので原理的には1と全く同じです。

サンプルコード

import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;

import org.junit.Test;
import org.slim3.datastore.Datastore;
import org.slim3.tester.AppEngineTestCase;

public class PutUniqueValueTest extends AppEngineTestCase{

    private static final String UNIQUE_INDEX_NAME = "ConferenceRoomName";

    @Test
    public void test() throws Exception {
        boolean actual1 = Datastore.putUniqueValue(UNIQUE_INDEX_NAME, "会議室A");
        assertThat(actual1, is(true));

        // 重複登録しようとするとfalseが返却される
        boolean actual2 = Datastore.putUniqueValue(UNIQUE_INDEX_NAME, "会議室A");
        assertThat(actual2, is(false));

        assertThat(tester.count(UNIQUE_INDEX_NAME), is(1));
    }

}