ISUCON7本戦は惨敗でとても悲しい気持ちになりました

ISUCON7本戦に出てきました。 結果はスコア0、最終的にはFAILで終わるという惨敗でした。

出場メンバーは会社の同僚との3人組で、予選と同様に他の2人がアプリケーション担当、僕がインフラ担当という役割分担をしました。

残念な結果となってしまい、とても悲しい気持ちなのですが、その思いを昇華させるためにも当日やったこと、考えていたことなどを記録しておこうと思います。 尚、悲しい結果に終わったこともあり、後悔や言い訳の多い見苦しい文章となっています。あらかじめご了承ください。

当日の流れ

10:00 にレギュレーションが閲覧できる様になりました。 しばらくは運営側でid, passwordの配布に時間がかかっており、コンテストが始まらなかったので、その間に3人でレギュレーションの読み合わせを行いました。単純に読んでいるだけでも、複雑なアプリケーションで単純な最適化は難しそうだなーという印象を受けました。

最初の30分は僕が各サーバーのセットアップ(git管理下に置く、各人がsshできる様にauthorized_keysを配置する、netdataの監視を仕込むなど)を行い、他2人がDBのデータの確認とアプリケーションのコードを読んでの概要把握を行いました。

その後、最適化の方法をざっくり話し合い、以下の方針を決めました。

  • 方針1. calc_status が明らかにヘビーだから、なんらかの形でキャッシュする必要はありそう
  • 方針2. dbを触ってる部分はRedisに載せていくと良さそう
  • 方針3. roomごとにスコア計算は完全に独立しているから、roomごとにhostを振り分けて、hostごとにデータを持つ様にしたらthroughputが出そう(この時点では、redisをhostごとに建ててそこにデータをstoreすることを考えていたので、redisの負荷が減りそうくらいのイメージで捉えていた)
  • 方針4. static file配信はいつもの様にnginxでgzipで配信すると良さそう

今振り返るとこの時点での方針は悪くなかったと思うのですが、結果的には「方針4」以外は実装が思う様に進まず、ほぼmergeされる事はありませんでした。

自分が後悔している事の一つは、この時アプリケーションを読んでいた同僚から「ロックが必要になる」という事を言われた際に、ロックの方法についてもうちょっと自分もアプリケーションを読み込んだ上で議論すべきだったという事です。 自分はアプリケーションのコードは1行も読んでなくて、ただ「DBでロックを獲得するコードがある」と言われて議論した結果、DBを捨ててRedisを使うならRedisのincrでロック獲得を記述できそうという結論になりました。この「Redisのincrで実装したロック」は動作はしたのですが、毎回ロック獲得トライをRedisに問い合わせて行うというコストの高い動作の為か、スコアアップには繋がりませんでした。後で同僚からは「ロックはroom_nameごとに独立してかければ良かった」と聞かされて、そこまでの知識があればこの時点でroomごとに処理するprocessを分けて、ruby内部でmutexを活用しようという提案ができたかも。。。と後悔しています。

現実には、同僚の一人が「方針1」を、もう一人が「方針2」を進め、僕は「方針4」を行った後、便利スクリプト集(deployやログ閲覧、アクセスログ回収、各ホストの状態チェックなどをローカルのマシンからコマンド一発で実行できるもの)の整備やちょっとしたパラメータチューニング(pumaが10processで動いてmemoryを結構使っていたのでprocess数を減らしてthread数を増やしたりしてた)、2人とのペアプロなどを行なっていました。

「方針4」は一応スコアに効いて、9,600ぐらいになりました。しかし、それ以降僕らのチームのスコアが上がる事はありませんでした。

同僚が2人とも苦戦していたので、僕は定期的にペアプロでハマり解消を手伝っていました。

「方針1」を進めていた同僚が「途中まで出来た」といってベンチにかけてみたところ、スコアは上がらないどころか若干下がっている様に見えたので、その時はもう少し実装を進めてスコアを上げてからmergeしようという話になりました。結局、その後そのブランチがベンチを通る事はなく、彼は最後までそのブランチで作業し続けることになりました。(余談ですが、スコアが上がらなかったのは、おそらく前述の通りRedisをロックに利用していた為では無いかと疑っています)

「方針2」を進めていた同僚もRedisでロックをかけていて、こちらもベンチがなかなか通らずずっとハマっていました。最終的にロックを色々な箇所に設定する事でベンチは通ったみたいですが、それは終了の30分前であり、スコアも上がりませんでした。

(尚、「スコアが上がらなくてもmergeだけでもしておこう」といって「方針2」の一部はmergeされたのですが、最終スコアはFAILになってしまいました。不思議に思ってコードを見返すと、分かりやすく「引数が無いメソッドコール」というバグがありました。「ベンチ通ったからmergeした」と聞いたのに悲しい。。。)

結果としては、1日を通して有効な変更をほぼ入れられず、やりたいことが実現できないまま終了してしまったコンテストとなりました。

反省

自分の反省点はいろいろありますが、大きいのは以下の2つだと考えています。

1つ目は、自分がアプリケーションを全く読み込まなかったことです。「アプリケーションの仕様」を理解してないことから、途中で2人とペアプロする際も「コード的な間違いの指摘、修正」に終始してしまい、「アプリケーションの仕様の範囲で可能な限り効率的な実装を考える」という事が出来ませんでした。特に、今回の問題は効率的な実装をするにはスレッドプログラミングの知識が求められるものになっていたと思うので、自分の知識は本当はもっと活かせたんじゃ無いかと思っています。

2つ目は、自分がアプリケーションコードをほとんど書かなかった事です。これは1つ目の反省とも繋がるのですが、アプリケーションを読み込んでない状態では改善のアイディアを考えづらく、さらに残り時間が少なくなるほど「これからアプリケーションに着手する」といった手が取りづらくなり、結果としてほとんどアプリケーションを書きませんでした。アプリケーションがボトルネックというのは初めから分かっていたので、インフラ作業を捨てるというのも1つの選択肢だったように思います。

自分は普段はアプリケーションエンジニアとして仕事をしており、ISUCONは貴重な「インフラ知識をつけることができる、インフラオペレーションに慣れる事ができる」機会となっています。カーネルミドルウェアのチューニングをしてマシンを効率的に利用したり、デプロイツールなどを自作して様々なタスクを自動化するのは僕にとって楽しくて、ISUCONではインフラ担当としての参加にこだわっていました。ただ、今回はそのこだわりが良く無い方向に働いたな、と感じています。

結論

今年は悔しい結果となったので、また来年頑張りたいです!!!

謝辞

運営の皆さん、お疲れ様でした。綺麗に整理されたコードで複雑なアプリケーションが実装されており、運営の皆さんの気合を感じました。取り組みがいのある、良い問題だったと思います。ありがとうございました。