はじめに

ISUCON10に「イキリ社会人」というチームで参加しました。

結果は惜しくも予選突破ならずでしたが、今回の大会でやったことを時系列順に書き留めておこうと思います。

メンバー

やったこと

7/16

@soiya1919 がチームの登録をしてくれたものの、自分だけ完全に参加登録を忘れていたことに気づく。

8/1

練習しないと…

8/18

意外とみんなの予定が合わず、練習日が決まらない

8/31

やっと練習日が決定。9/6にリモートで集まって過去問を解くことになる。

9/6

早朝に会社のサーバーがぶっ壊れ、緊急メンテ。早起きしたので終わったら爆睡してしまって結局何も練習できなかった。

※自分以外の2人はちゃんと練習していました。えらい!

9/10

そろそろ練習なりなんなりするぞ……という気分を高める

9/12 09:00

気づいたら本番当日

9/12 09:40

開始が遅くなるらしいことがわかり、二度寝

9/12 11:50

起きて昼飯を食べ始める

9/12 12:20

ISUCON10 予選開始。 予選マニュアルが公開されたので、みんなで眺め始める。

9/12 12:25

予選マニュアルにわざわざbotは弾いてもいいと明記されていたので、 @soiya1919 がbotを弾くためのnginxの設定を書き始める。

9/12 12:30

みんな一通りのサーバーにsshで入れるのを確認。

9/12 12:50

今更になって後々使いそうな便利コマンドの一覧を作り始める。事前にやっとけ。

9/12 13:00

用意していたISUCON10用のリポジトリにアプリケーションコードやら設定ファイルやらをぶちこむのがこのへんでだいたい完了する。

@soiya1919 がNew Relicを入れる。

記念すべき第一回ベンチを回す。

この時点でのスコア: 447

9/12 13:30

突然initializeが失敗するようになる。自分が調査した結果、なぜかdefault charsetがlatin1になっていたことが発覚。初期化用のsqlをいじって CHARSET utf8mb4 とかを色々な箇所に入れる。 (実は自分がミスってこの時点で my.cnf が読み込まれなくなっていたのだが、この時点では3人ともそれを知るよしも無いのであった…)

9/12 13:50

New Relicでフレームグラフを見る方法がよくわからなかった(そもそも機能があるのかもよく分からなかった)ので、自分がpprofも入れる。

9/12 14:00

go tool pprof -seconds 120 とかやったら余裕でタイムアウトしたので、 proxy_read_timeout 600 あたりを自分が追加。

9/12 14:30

@soiya1919 が書いたbotを弾くやつをデプロイしたが、この時点では効果を実感できなかった。とはいえ、わざわざマニュアルに書いてるということは入れておいて損はないだろうということで入れたままにしておく。

9/12 15:00

自分がpt-query-digestとかを叩き始める。プロファイラとあわせてやっと最適化の準備が整ってくる。

9/12 15:40

@neglect_yp がデプロイスクリプトを用意する。

9/12 15:50

せっかくサーバー3台あるしってことで、 @soiya1919 がリクエストを3台全部に振り分けるようにする。 MySQLでユーザー作ったり GRANT ALL したりとかの作業を普段あんまりしてないのでちょっと詰まるが、無事分散完了。 ただし、DBで詰まってたのでそんなにスコアは変わらなかったはず。

このあたりから運用方法が固まってきて、変更は一つずつPRにだし、マージしてデプロイ→ベンチマーカーを回してスコアをメモするという流れができる。

この時点でのスコア: 476

9/12 16:00

initializeでよくコケるので、自分がinitializeでMySQLに食わせるスクリプトを実行するだけのやつを用意して確認を簡略化。

9/12 16:25

なんか突然bashrcが壊れてデプロイスクリプトが動かなくなったので、自分が修正。

9/12 16:35

とりあえずfilesort全部メモリ上にしてくれないかな、という話になり、@soiya1919 が試しに sort_buffer_size を上げてみる。しかし、 my.cnf は1時間前に自分がぶち壊してしまっていたため、なぜか設定が反映されずに苦戦する。

9/12 17:00

@neglect_ypSELECT * FROM chair WHERE stock != 0 ORDER BY price ASC, id ASC LIMIT ? を速くするため、chairに (price, id, stock) のインデックスを貼って、スコアが少し上がる。

この時点でのスコア: 554

9/12 17:05

ボトルネックになっていたNazotteを高速化するために、自分が ST_Contains をしていた部分を polyclip に置き換える。 N+1が消滅。

この時点でのスコア: 719

9/12 17:15

@neglect_ypSELECT * FROM estate ORDER BY rent ASC, id ASC LIMIT ?; を速くするため、estateに (rent, id) のインデックスを貼る。

この時点でのスコア: 913

9/12 17:20

chairとestateの ORDER BY popularity DESC, id ASC がうざかったので、自分が UPDATE chair SET popularity = -popularity して、人気度が低い椅子ほどいい椅子ということにしてしまう。これによって降順インデックスが無くても普通に (popularity, id) のインデックスが使えるようになる。

この時点でのスコア: 1312

9/12 17:35

自分がestateに (latitude, longitude) のインデックスを貼る。

この時点でのスコア: 1565

9/12 18:05

nginxがリクエストをファイルにバッファしまくっていたので @soiya1919client_max_body_size を上げてメモリ上に持たせてくれるように頑張る。

この頃から POST /api/chairPOST /api/estate がちょくちょくタイムアウトするようになる。ちなみにISUCON10のルールとしてはこれらがタイムアウトすると失格。

この時点でのスコア: 1565

9/12 18:35

@neglect_yp が、search系を多少でもマシにするためインデックスを貼りまくる。なんかよくわからないけどそこそこスコアが上がった。今回は更新系のクエリはそこまで厳しくないので、多く貼っておいてもそんなに困らない。

先ほどに引き続きPOST系がタイムアウトして失格になることがちょくちょくあるので、ベンチマークガチャが始まる。

この時点でのスコア: 2164

9/12 18:45

自分がPOST系を全部非同期でやるように変更。しかし、ベンチマーカー的には変更は即時反映されていないといけないらしいので一瞬で怒られて0点になってしまった。

この時点でのスコア: 2164

9/12 19:40

POST系を全部同期かつbulk insertでやるようにして、POST系タイムアウト問題を解決する。

sqlxのバージョンが絶妙に古く、 db.NamedExec("INSERT INTO hoge (a, b) VALUES (:a, :b)", []hoge{a, b, c, ...}) みたいな形でのbulk insertが動かずにハマる。しかもコンパイルが通らないとかの類ではなくリフレクションをやりまくった結果どっかで panic するみたいな落ち方をするのでたちが悪い。結局sqlxのコード読んだりissue漁ったりしているうちに1時間弱ほどこの罠で溶けてしまった。

この時点でのスコア: 2164

9/12 20:00-20:59

インスタンスを再起動して必要なプロセスが立ち上がるのを確認したり、ベンチマークガチャをひたすら回し続けたりする。しかし、なぜか先ほどよりも点数が出ず、若干点が落ちる。

この時点でのスコア: 2088

9/12 21:00

終了!!

感想

中盤までは順調だったのですが、最後の2時間くらいでスコアを稼ぐことができず、最終的には追い抜かれて予選敗退となってしまいました。

悔しいですが、去年のISUCONと比べると成長を感じられたのでよかったです。

来年は本選出場します!!!