ISUCON 2016に参加してスコア40700点で予選敗退した

こんにちは。れこです。
ISUCON 2016に参加し、40700点ほどで予選敗退しました。

予選敗退なので参考になる情報は少ないと思いますが、アウトプットと備忘録の意味を込めて残しておきます。

参加チーム・メンバー、言語

チームはKIM三兄弟というチームでした。

@takakikuさん、@matsuokさんと参加しました。同じ会社のメンバーで社会人枠として。

予選時の役割分担はこんな感じ。

  • @takakiku
    • インフラ・ログ解析・アーキ全般
  • @Leko(私)
    • アプリ全般を中心にアーキも見る、大規模な作り変えは私担当
  • @matsuok
    • ミドルウェア(主にDB周り)を中心にアプリも見る。DB周りのボトルネック改善など担当

挑んだ参考実装はGoでした。

事前練習

特に事前練習してませんでした

ISUCON 2015のときに作っておいたWikiを読み返して、数カ月ぶりにGo触って感を取り戻すくらいでした。

一応、Pixivさんが開催したPrivate ISUCONの問題を3人で模擬戦として挑みました。
実践を想定した時間で出せたのはスコア3万くらいでした。その後解説ブログ読んで画像にキャッシュのヘッダをつけたら一気に18万くらいに跳ね上がった。

@takakikuさんとはISUCON 2015にも参加して(予選敗退して)いたので、練習から結構息があってた印象。
@matsuokさんはISUCON初参加なのでやや不慣れな感じ。

ISUCONの初動は「勘所」と「慣れ」に尽きると思っているので、
この模擬戦で@matsuokさんに「慣れ」が作れたのが大きかったと感じた。

本番

ということで練習はさらっと流して本番へ。

初期スコアは0点

各メンバーの初動は

  • @takakiku
    • インスタンス起動、参考実装デプロイ、SSHで入れるようにetcの環境構築
  • @Leko
    • 当日レギュレーション読み込み、コードリーディング
  • @matsuok
    • DB周りの調査、コードリーディング

各人初動はすこぶる順調に潰せました。

んで早速最初のベンチ。
公式にアナウンスがあったので特に慌てることはなかったですが、Goの実装の初期状態でベンチ回したものの0点。
というかトップページが重すぎてタイムアウトしている模様。

ベンチが最後まで行ってくれないので、kataribept-query-digestもうまく刺さらず@takakikuさんが苦戦。

ツールに頼るまでもなくトップページが重すぎ。直さないとベンチが通らん
というのが初期段階での方針になりました。

初期段階での気付き

  • インデックスチューニング
    • ORDER BY CHARACTER_LENGTHをしている箇所があったので、ここが遅そう
    • ORDER BY updated_atがあるので遅そう
    • あまり露骨なテーブル定義がなく、インデックス貼っても大きく変動はしなさそう
  • 静的ファイル配信
    • Goで捌いてるpublicディレクトリはnginxで捌いたほうが良さそう
    • robots.txtもわざわざGoで捌いてる。露骨だけどスコアには寄与しなさそう
  • htmlifyを中心にトップページ
    • 何回な正規表現ゴニョゴニョしてる処理がある。とりあえず読まないで放置
    • N+1どころじゃない3重ループがある。どうせ治す必要があるんだけど結果をキャッシュで暫定対応できないものか
    • loadStarsでHTTPリクエストが飛んでる。フロント→APIならわかるがアプリサーバ→APIはおかしい
  • POST /keywords
    • isupamとかいうよくわからんプロセスが居る。スパム判定をしてるっぽい。なんだこれ
    • ソースコードがない。Goで書かれた実行ファイル? なんにせよコードがないのでブラックボックス感ある
  • テンプレートエンジン
    • コンパイルを1回にできないだろうか、外部ライブラリ使ってるので内部実装読む? 時間かかるか、愚直にはいかなさそう
  • app ↔ APIの通信
    • 分け方がおかしい、それぞれHTTPリクエスト飛ばして通信してる。これソースコード移動したらHTTPリクエスト無くなるのでは。修正量多そうだけどスコア大きく変わりそう

露骨すぎるすぐできる問題は一旦おいておき、
pixivのISUCONで学んだ「重すぎるページは結果をキャッシュする」を実践しました。

Redisでドハマリ

@takakikuさんと連携しRedisを導入+UNIXドメインソケットで接続までは一瞬でいけた。 だけどその後でなんかハマった。
Wikiにコピー&ペーストできる実装例を作っておいたので貼り付けるだけという感じだったが、Redis自体の扱いに慣れてなかったのでちょこちょこ詰まる。
なかなか思った通りに行かない。

セグフォが起きたりキャッシュの結果がおかしかったり、パッと見ではわからないドハマりを起こしてしまい、ややつらい状況。

あまりに詰まるってたので、matsuokさんtakakikuさんが簡単に潰せるところをちょこちょこ潰し始める。 でも13時過ぎて未だベンチが通らず(タイムアウトが解消しない)

matsuokさんweb↔APIの改修

私がRedisにハマってたので、matsuokさんが改修開始。

  • APIのGET /starsを改修し、HTTPリクエストを削減
  • APIのPOST /starsを回収し、直接DBを見るように修正

これで通るのでは!? と思ったもののベンチ通らず。

Redisではなくオンメモリでキャッシュに切り替え

Redisにドハマリしてしまって仕方なかったので、Redisやめてオンメモリにデータを持つ方針に切り替え。
幸いメモリには余裕があるようなので、載せられる限り載せてみる方針に修正。
修正量はさほどなかったので2-30分くらいで修正完了。

ベンチを回してみる。
やっとトップページの遅さが解消してきたらしく、負荷走行までたどり着くことができた。
ただしトップページに今度はエラーが発生して、スコアは依然0のまま。

まさか今年はスコア0なのか…! ?! ?
という焦りが生まれ始める。

takakikuさん障害対応

16:30時ごろ
ISUCONとは関係なく、社内サービスのアラートが上がる。
なかなか致命的な障害が起きてしまい対応にあたる。

インフラおらんやん、詰んだ…
とさらに弱気に。

isupamに触れてみた。38000点でた

17:00ごろ。
@takakikuさん不在のまま@matsuokさんと対応中。
トップページの結果はキャッシュ済みだけど、最初の1回が遅すぎてタイムアウトする
という問題が残ってました。

  • API側を改修してもらったので、HTTPリクエストが残るのは怪しいブラックボックスのisupamのみ
    • 文字列を飛ばして結果を見ているので、文字列をキーにisupamからのレスポンスをキャッシュしてみる

を試してベンチ回してみると、なんかログが流れる速度が違う。めっちゃ早い。
初めてスコアが出て、38000点くらい出ました。
文字通り 「ファッ!?」 と叫んでしまう感じのスコアの変化に驚きが隠せない。

おそらくisupamだけが決定的な要素だったわけではなく、
isupamを解消したことでトップページのボトルベックを全て取り払ったから一気にスコアが出たのだと思われる。

@matsuokさんはトップページのページネーションの高速化に着手中。
ブラウザからさっと動作確認する限り動いているように見えるが、ベンチが通らずドハマリ中。

ちょっと伸ばした

17:30くらい。@takakikuさん復活。
38000では予選を超えられないと判断し、再起動耐性よりもスコアを伸ばすことを優先。

  • ログ出力を極限まで減らす
  • トップページの初期表示時に必要なデータの分だけサーバ起動時にキャッシュしたら早くなるのでは…?
    • サーバ起動時に全データのhtmlifyの結果をキャッシュしようとしたが、遅すぎて諦めた
  • それに加えてINSERT or UPDATEしたときにhtmlifyのキャッシュを作成/更新するように修正すればトップページ更に早くなるのでは

もう時間が残ってないので、時間内に直せて効果ありそうなものを潰していく。
最初に上げた簡単にできる修正などもなるべく潰していく。ベンチの動きを予想しつつセコくてもいいからスコア上がらないか画策。

結果的にちょびっと伸びて40700点が最終スコアでした。
スコアは出たものの、リンク不足のエラーが解消できず。あれが解消したら多少スコア上がったんだろうか。

総評

私自身の動きとして、

  • コードリーディング→問題点の洗い出しまでの速度・精度は割と良かった
    • 悪くいうと「よくあるISUCON慣れで突破できる要素」に慣れた
  • 対応がしょぼかった。Redis素人すぎ。もっと趣味等でも触って慣れておかなければ
  • ドハマリしてる時間が長すぎた。もっとはやく見切りをつけられた
    • オンメモリ実装に切り替えるまでが長すぎた
    • 俯瞰的に見ればisupamの改修は真っ先に入るレベルだったのに視野が狭くなっていた
  • オンメモリ実装に切り替えてからのスピード感は良かった

あたりが振り返りです。
学生枠なら5位に入ってたとか一瞬思ったけど、もう私は学生じゃないし言い訳よくない

兎にも角にも、悔しいです!!
辛酸を嘗めつつ、解説ブログを読みつつ、「その手があったか」と後悔する日々に浸ろうと思います。

今年も勉強になりました。予選突破したかった…

問題を報告する

このサイトはGithubのリポジトリを公開しています。
もし問題などがありましたらIssueにてご報告いただけますと幸いです。
Leko/WEB-EGG