#isucon で優勝したチームのメンバーとして参加してた

既に多くの参加者の方々がブログに書いておりますが。 #ISUCON に参加しました。
livedoor Techブログ : なんでもありのWebアプリケーション高速化バトル、#isucon 開催のお知らせ
livedoor Techブログ : ISUCONやりましたーっ! 最終結果発表 #isucon


以前参加したチューニンガソンも面白かったし、コレも出たいなーと思って社内で調整してみたころ、ちょうどよく3人集まった、ということで@さんと@さんと一緒に参加しました。結果は優勝!
#isucon で優勝してきました - 酒日記 はてな支店
#isucon ではどんなことを考えながら作業していたか - 酒日記 はてな支店
おそらくはそれさえも平凡な日々: #isucon で優勝させてもらってきました
お二人のエントリの通り、fujiwaraさんとsongmuさんでとても効率よくイイカンジにチューニングしてくださっていました。
僕がやったことと言えば、サイドバーをMySQLに重いクエリ投げず取得するためmemcachedに入れて管理するように、と以下のようなコードを書いたくらいでした。

diff --git a/lib/Isucon.pm b/lib/Isucon.pm
index e8d7b52..c0fff8c 100644
--- a/lib/Isucon.pm
+++ b/lib/Isucon.pm
@@ -6,6 +6,7 @@ use utf8;
 use Kossy;
 use DBI;
 use JSON;
+use Cache::Memcached::Fast;
 
 our $VERSION = 0.01;
 
@@ -31,13 +32,21 @@ sub dbh {
     });
 }
 
+my $memd;
+sub cache {
+    my $self = shift;
+    return $memd if $memd;
+    my $config = $self->load_config;
+    $memd = Cache::Memcached::Fast->new({
+        servers => $config->{servers}{memcached},
+    });
+}
+
 filter 'recent_commented_articles' => sub {
     my $app = shift;
     sub {
         my ( $self, $c )  = @_;
-        $c->stash->{recent_commented_articles} = $self->dbh->selectall_arrayref(
-            'SELECT id, title FROM article ORDER BY comment_created_at DESC LIMIT 10',
-            { Slice => {} });
+        $c->stash->{recent_commented_articles} = $self->cache->get('recent_commented_articles');
         $app->($self,$c);
     }
 };
@@ -83,11 +92,26 @@ post '/comment/:articleid' => sub {
         $c->req->param('body')
     );
 
-    $sth = $self->dbh->prepare('UPDATE article SET comment_created_at = NOW() WHERE id = ?');
-    $sth->execute(
-        $c->args->{articleid},
+    my $article = $self->dbh->selectrow_hashref(
+        'SELECT title FROM article WHERE id=?',
+        {}, $c->args->{articleid}
     );
-
+    # get from cache
+    my $cached = $self->cache->get('recent_commented_articles');
+    my $exists = { $c->args->{articleid} => 1 };
+    my $new_data = [{
+        id    => $c->args->{articleid},
+        title => $article->{title},
+    }];
+    # filtering and push
+    for my $data (@$cached) {
+        unless ($exists->{$data->{id}}) {
+            push @$new_data, $data;
+            $exists->{$data->{id}} = 1;
+        }
+    }
+    pop @$new_data if @$new_data > 10;
+    $self->cache->set('recent_commented_articles', $new_data);
     $c->redirect($c->req->uri_for('/article/'.$c->args->{articleid}));
 };

memcached自体はfujiwaraさんがrevサーバにインストールしてくださっていたので、hosts.jsonにrevサーバの情報を追記するだけ。最初キャッシュできてなくて何だろうと思ったらrevサーバ11211ポートが塞がれていて繋がらなかっただけ。一応これで初期データがキャッシュに入っていればPOSTされるタイミングでサイドバーも更新されるはず…だとは思ったのだけど、微妙に安定しなかったり。そもそもキャッシュから取れなかったときには通常通りDBから取りに行く、というようなフェイルオーバーを考えない漢らしいクソ設計で作ってしまったため、たびたびサイドバーが丸ごと消える、などの不安定な事態が起こり、これはイカン、ということで結局ロジック部分は全部revertした。残ったのはuse Cache::Memcached::Fast;の行とC::M::Fインスタンスを取得する部分だけ。
あとは一緒に相談はしつつも、ひたすらfujiwaraさん、songmuさんが手を動かしているところを見守っていました。
とは言え本当にスゴい人たちが味方として考えチューニングしていく様を間近でリアルタイムに観ることができる、というのも非常に貴重な体験で(一緒に仕事していたとしてもやはり分担されることが多く、操作を一緒に見ながら、という機会は少ないでしょうし)とても刺激になりました。チームの戦力になれたか否かはともかく、自分としては得るものが非常に大きい、良い機会となりました。優勝できたのも良い思い出になりましたし。


サーバの準備から当日の運営、ベンチマークツールもしっかり作られていて途中経過を出しつつ最後の判定までスムーズに、と主催のライブドアの皆様は並々ならぬ労力をかけて出題、準備、シミュレーションをしてきたことと思います。非常に良いイベントで存分に楽しむことができました。本当にありがとうございました! 参加者の皆様もお疲れさまでした!!

追記

多くの参加者の方々が振り返りや検証を書いているので、とても勉強になります。
タグ「isucon」を検索 - はてなブックマーク