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));
    }

}