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 を使って対応
    • 次にうまくいかなかったところ
      • 存在しないイベントに対する応答がベンチマーカーが期待してたステータスコードと違う
      • 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後の動作確認して終了ー