ISUCON8予選で敗退しました...
ISUCON8の予選に @hanhan1978, @trtraki とyokohama-northというチームで参加しましたが、最終スコア30824で敗退しました。無念。。
以前と同様のメンバーでしたが、今回は役回りを変更。過去に参加した時はいつもDB等ミドルウェア・OS周りをやってたけど、今回はアプリケーション改修班に。Goでやりました。担当を変えたことで別の体験ができてすごく良かったです。
今回のお題はイベントの座席の予約サイトという感じ。アプリケーション触ったりコード見たりしてて排他制御があるなー、それ以外は普通かな?と思ってたのですが、実際はやたらと呼ばれてるgetEventという関数の改善で右往左往してるだけでほとんど終わってしまいました。もっとプログラミングの質とスピードを上げたいのと、複数台サーバー使った時に排他制御のところどうやっていこうかなというのをじっくり考えたいなぁという気持ちです。
とにかくすごく楽しかったし、ベンチマーカーやポータルサイトも非常に快適でした。運営の方々、チームメンバーの方々、こんなに楽しい機会をいただき本当にありがとうございます!
以下、雑な思い出タイムライン。
- 10:00に滞りなく開始!
- とりあえずGoに切り替える
- pprofとecho-pprofを導入
- 使ったことがないgbとかいうパッケージ管理ツールが使われてて一瞬焦る
- まずはコード読んだりアプリケーション使ってみたり
- h2oであることに気がつく
- h2o使ったことある人がいないので、Nginxに置き換えることをチームで決定
- pprofがうまくいかない
- 502 BadGatewayだったので、h2oのタイムアウトかなぁ、という感じ
- Nginxに置き換えられるの待ち
- 11:00くらい
- チームで話し合い
- Nginxへの置き換えをとりあえず終わらせて、ログをalpで分析しよう、ということに
- alpの結果から
GET /
とGET /api/users/:id
とで分担。GET /
を担当することに - pprofしたけど、handlerが無名関数で書かれていてプロファイルできない
- handlerを関数化する。最初の1時間でやっておくべきだった
- 関数化完了。pprof見ると、ほとんど
getEvent
- 各エンドポイントで N+1 にならないようにしましょう、とメンバーと話す
GET /
のフロントのコードを読むと、reservationsをそもそも使っていないので、DBから取得する必要すらないだろう、ということで修正する- 修正したけど、ベンチが通らない。あー、ベンチマーカーには画面を返してるわけじゃなくJSON返してるんだから、レスポンスの構造は変えちゃダメじゃないかということに気がつく
- getEventのインタフェースは維持して、中身のロジックを修正する方針に切り替える
- eventとreservationsのN+1を解消する
- 最初うまくいかなかったところ
- OUTER JOINを使ったSQLの結果をstructにマッピングするときにNULLを
int64
に代入しようとして実行時エラー sql.NullInt64
を使って対応
- OUTER JOINを使ったSQLの結果をstructにマッピングするときにNULLを
- 次にうまくいかなかったところ
- 存在しないイベントに対する応答がベンチマーカーが期待してたステータスコードと違う
db.QueryRow
を使ってレコードがない場合はScan
時にsql.ErrNoRows
が返ってくるが、db.Query
を使うように変更したのでレコードが存在しない場合の挙動が変わってしまった。明示的にsql.ErrNoRows
を返すようにして対応
- getEvent修正終わった
- 点数が10000点超えた。たしかここで16:00くらいになってた気がする
- alpすると、
GET /
とPOST /api/events/:id/actions/reserve
が遅い - pprofすると
POST /api/events/:id/actions/reserve
は結局getEvent
- 引き続き
GET /
を対応する- 公開済みのeventのデータだけをN+1を解消して取得するようにする
- 修正できたけど
GET /
速くならず・・・! - 最初のgetEvent改修時のSQLをチューニングしてなかったことに気がついてインデックス作成する
- 20000-30000くらいをいったり来たり
- 17:00くらい
- なんかfailになる時がちょいちょいある
- しかもサーバー上のコミットがなんか意図したものと違うことに気がつく
- gitで調整しつつ、バイナリ作る
- うまくいくコミットが見つかったので、ビルド。バイナリを保存
- reboot後の動作確認して終了ー