#ISUCON 6 に参加して僅差で人権を失いました

結果について

予選敗退、惜しくも決勝進出ライン90214点を逃した。

参加の動機について

力試し

25歳。ただの事務職から4回の転職を経て、Webサービスをやっている会社で技術者のはしくれをやっていましたが、自分自身が持つ実力がどの程度なのか未だにわからなかった。

技術者として1年やっていくごとにどんどんわかるようになり、どんどんできる領域が増えているという感覚はあったけど、それらは定量的に計ることができるでもなく、自身に対する上司や同僚からの評価・査定やアドバイス、意見した事柄に対する受け止めなどが客観的にどれほど適切なのかがとにかくわからなかった。

そんな中、少なくともISUCONはWebサービス界隈の有力者がうごめいていて、かつ業務上慣れ親しんだルールの上で競うことができ、自身のベンチマークとしては最適だろう、ということで参加を決意した。

チーム編成について

最初、所属している会社の同僚と出場するという算段を踏んでいたが、同僚各位に外せない予定ができてしまったため解散した。

困っていた所、繋がりのあった supermomonga 氏に「絶対に優勝しよう」と声を掛けたところ、心よく応諾してもらえたのでここにチームを結成した。

あと一人について、僕はいわゆるアプリレイヤの実装や全体の設計するのが得意で、モモンガ氏はアプリもインフラもできるがデータベースは分からないと言っていたので、データベースができそうな人を探しましょう、ということになり、モモンガ氏が aomoriringo 氏に声を掛けてくれて、ここに3人が揃った。

言語は僕とモモンガ氏が共通で使えるRubyを書くことになり、リンゴ氏はRuby書いたことはなかったがデータベースやミドルウェア周りを見て頂くことにした。

チーム名ですが、全員の頭文字を取って SMAP (SuperMomonga, Aomoriringo, Purintai) となった。

当日までの環境的な準備について

まず、運営からの割と大切な連絡がメールでくることが分かっていたが、代表者1人にメールが来るなり共有することに各位不安があったためプライベートなGoogleグループ、要はメーリングリストを作成し、そのメールアドレスで登録した。これは情報共有コストを格段に落としたのでとても良かった。

次にSlackを立て、過去問や他者の参加記や便利そうなツール・ライブラリをひたすら共有して蓄積し議論した。この試みもとても良くて、その理由は後述する。

Azureは先述のメーリングリストのアドレスを用いて登録した。いざという時にメンバーの誰かがパスワードを再発行してマスターアカウントでログインできるようにした。これは心理的な負担を少しでも減らすための試みで、いつ人は消えるかわからないので職務上もそのようにしておくと安心です。

直前集中合宿について

とりあえず過去問をたくさんやろう、会ってやろう、という話が交わされていたが、9月第2週にわたしが RubyKaigi 2016 @京都 に参加しており、予習のタイミングが本番直前の土曜しかなかった。

ところが、リンゴ氏が金曜に有給休暇を取るということになり、私も有給休暇を取って急遽、金土で過去問をやることになった。

金土日と連続でモモンガ氏の家(とても広い)にお邪魔してそこで初顔合わせとなったが、いわゆる初対面のわだかまりはなく、それはSlackで言葉を交わしていたことがとても大きく、また技術選定や方針についても「あの大会参戦記のアレを参考にしよう」とか「あのライブラリ・方法を試そう」などスムーズにすり合わせができ、普段同じ現場やプロダクト開発をしている同僚などではない間柄でも事前に前提知識の共有並列化が可能であり効果的であることがよくわかった。

過去問は ISUCON 5 予選問題と、Pixiv社内 ISUCONの問題をそれぞれ1日づつ使って解いた。ISUCON 5 予選問題は決勝進出程度のスコアになり、Pixivの問題は1位の点数を上回ってだいたい満足した。

作業の分担については僕が主にアプリコードを書いたりRedisを活用、モモンガ氏がアプリ書きつつnginxチューニングやログ集計可視化、ベンチマーク系環境整備、リンゴ氏がテーブルやSQL文やアルゴリズムをどんどん良くしていく感じで、それぞれの得意分野は各位が引っ張っていく感じでやった。

僕はSQLがやや苦手で「ここをこんな風に高速で呼び出し繋ぎたいがどうすれば良いか」と聞くとリンゴ氏がサクっとSQLを書いてくれたりざっくり伝えると最適な形でALTERしてくれたりして最高で、モモンガ氏が色々便利ツールを拾ってきて快適な過去問練習環境の構築やalpやkataribeなどのログを綺麗に再集計したり事前キャッシュしたりする書き捨てスクリプトをどんどん書いてくれたりしてくれて、僕はアプリコードを良くすることに集中できて本当に助かり、最高のチーム編成だと思った。

メシは2日連続で近所の海鮮丼と油そばを食べ、夜はアマゾンプライムで NEW GAME! を見て過ごした。

当日について

NEW GAME! を見て気分を高めていた所、開始10分前に突然モ氏がクソコラを作りはじめてやばかった。

開始直後、これは増田(Isuda)とはてなスター(Isutar)ですね、後なんかスパム判定マイクロサービス(Isupam)があるという話になり、僕はソースコード読みから仕様の把握に努めてお二人はデータベースのバックアップを取ったりコンフィグを確認したりをした。

事前に始まったらやること一覧リストというのを作ってあって流れ作業で進行しており、僕はまずソースコードを読んで機能一覧を書きだし、それからアプリコードを書き出すあたりにはRedisやPhpMyAdminなどが動いていてとてもよかった。

まず二人がhtmlifyが明らかにやばいことが分かったがデータベース利用が絡むため一旦検討を二人に託して、単純にPOST系でスコア稼げそうな、はてなスターとログイン認証をさくっとRedisにのせかえて、Isutarは消滅しハッシュされたパスワードは平文で保存され平文検証となりすこし速くなった。

記事が投稿されると過去の記事にリンク張り替え直さないといけないことが明らかにやばく、キャッシュしてもPOST毎に検証&破棄しないといけないかもしれない、ということが分かって最初は Redis の Sorted Set を活用することが検討されたが、仮実装で明らかに無理があることが判明したのですぐに切り戻しが行われ、とりあえずキーワード長カラムと予めエスケープしたキーワードを保存するカラムを拡張しようとなった。

拡張や再実装の方針について二人で検討している間、とりあえずスコアの伸びが確認できない問題だろうと仮実装の Sorted Set などを活用してキャッシュする機構や、どうせベンチマーカーは GET / の20要素しかすぐには見ないから、先頭の要素だけすぐに再生成すれば良くないですか、後は非同期でやりましょう、ということになり急遽 Sidekiq を導入して、サクっとワーカーを書いたら84000点出た。

後は Sidekiq を活用していい感じに非同期でやるように頑張っていったり、モ氏が Etag 活用して変更なければ 304 Not Modified 返すなどを仕込んだりしたら終了23分前に11万点出て、全員が予選突破できるかも、という気持ちになった。

ただ、dstatを見るとメモリが余りまくっていて unicorn プロセスを増やせる余地があることに気づき、思い切って200プロセスとかにしたらメモリを使いきってしまって5万点とかが出て、慌てて設定を完全に戻したら93224点になり、焦りからもう一度キューを入れたら89923点(ぐらい)が出て終了した。

結果、15位のボーダーが90214点でもう数百点の違いで、しかもベンチ回し直さなければ決勝進出できていたのでかなりつらくなり、皆で街に出て慰労会をやった。

終わった後について

この結果はもはや運の領域であり、運も勝負の一部であることから我々は確かに負けたのであったが、どうにもつらい、やりきれない気持ちとなった。

結果発表後にベンチマーカー実装の不具合と思われる事象や、それによる採点と運営レギュレーションとの乖離について愚痴が発生したが、仮にベンチマーカーに不具合があるならばそれを突いたり活用するのは正攻法であるため、アプリを書く役割として割り切れなかったことについても反省がある。勝負は勝負でやり直しはないということを久々に噛み締めた。

参加の動機について、敗退が決まった後は無力感などが存在していたが考えなおしてみるとアプリを書く能力はまあ、あるのでは、となり自信と自意識が高まってよかった。

終わった後、男3人によるエモい話などがあり、プリパラ1~11話を見てこれはかわいいですねとひたすら言いながら眠りについた。

そして朝が来て皆起きて、あそこはやはりああするべきだった、この部分の実装はもっといい方法があった、など各位から色々出てきたので、来年があれば本当にまたこのチームで参加したいと思った。

最後に海鮮丼を食べて、豚汁を飲んで電車に乗り帰った。