ISUCON8に参加したのでその雑記

# はじめに

* ISUCON参加4回目です
* 528チーム中64位でした
* 僕はアプリケーションまわりを担当したのでそのあたりを中心に書きます
* 予選敗退ですがそこそこ手応えがあったので手も足も出なかったチームは参考にしてみてください

# ISUCONとは

http://isucon.net/
WEBサービスのチューニングのうまさを点数化して全国で競い合うコンテスト
「いい感じにスピードアップコンテスト」の略

# 今回の問題

今回は座席の予約システムでした

* ユーザー登録・編集
* 座席の予約
* 管理者機能: 売上の集計、CSVのダウンロード

運営から3台サーバを渡されて自由に使って良いという感じ

以下は今回のシステムの簡単な動作
（毎回思うが初期テストデータがくすっとくるものが多い）
![ItL8XKS6yQ.gif](https://qiita-image-store.s3.amazonaws.com/0/67471/0916fa01-e446-3674-fb2b-c69255881be7.gif)


# チーム

チーム名: バナナマン -4thの覚醒-
言語: `Ruby`

去年と同じチームで挑みました（[去年の記事](https://qiita.com/yamamoto_hiroya/items/ce31e8aaf5d1ab4fa291)）
同じチームで4年連続で参加してます

* ミドルウェア・インフラ周り担当（[インフラ担当の記事はこちら](https://qiita.com/asflash8/items/93f60393288cdd038e72)）
* アプリケーション担当
* 全体統括、指示出し（ブレイン）

みたいな担当で臨みました
僕はアプリケーション担当

# リポジトリ

今回はまぁまぁ成績が良かったので改修内容を公開する予定です
まだ準備ができていないので後日編集してここに貼ります
[追記]
リポジトリ用意できましたので公開します
これのruby配下がメインの修正です
https://github.com/asflash8/isucon8q

# やったこと

### 事前準備

* 毎年のことながら大体ぶっつけ本番になるため、今回は事前に作戦会議を行いました
* 各担当の確認、開始後の動きの確認
* 毎年sinatraの扱いで苦戦することがあるためsinatraの素振り
* 手元でsinatraの環境を作る練習

### 当日: 午前

* 9時頃集合
* レッドブルを飲んで気合を入れるところからスタート（毎年の恒例行事）
* Discordの雑談窓でほっこりする
* 余計なブラウザやターミナルを閉じて準備
* 10:00開始
* 当日ログインのポータルへアクセスし、各サーバの情報をキャッチ
* 各サーバへSSHし構成やコードをざっくり眺める
* とりあえずベンチを走らせ、初期スコア1500程度
* 実際に動かしてWebへアクセス、どういうアプリケーションなのかを把握する
* この間にインフラ担当がソースをリポジトリにpush
* 手元にpullしてきてすぐさま環境構築
* `bundle install --path vendor/bundle`
* 手元のPCでMySQL起動
* ソースの書き換え、DBへの接続情報で環境変数がない場合にはlocalhost, rootに繋がるようにした
* 事前の素振りの効果が出て開始50分で手元で同様のものが動くようになった
* ここまでにブレインが全体を眺めてボトルネックにあたりをつけ、`get_event`を改修することになる
* まずsheetの情報がDBに持つ必要がない
* N+1でクエリを発行しているのでそれの改修
* sheetをメモリ上に持ち、テーブルをドロップしようと思ったが各クエリでJOINしていて面倒だったため、とりあえずこのアクションではテーブルを読まずにメモリ上で完結するようにした（自分で該当のデータをソース上で作成）
* N+1のクエリをやめ、配列とハッシュの操作でIN/OUTを変えずに改修
* 配列操作に苦戦し改修に時間がかかった
* この時点でベンチを走らせたがfailしてしまい原因不明
* 解決できないままお昼に突入し暗い気持ちでコンビニ弁当を食す

### 当日: 午後

* お昼に一呼吸置いたらあっさり解決しベンチ→fail
* しかしfailになる原因が思い当たらず1つずつ戻してベンチを走らせることに
* インフラ担当が行った改修でfailになっていたようで、それを除いて先程の修正を入れたところベンチが通り2000点ちょいになる（ここでようやく点数に動きあり）
* 次にreports系のアクション2つが重かったのでそれに着手
* 売上データを条件なし（20万件）で取得してCSVを作成している処理だった（そりゃあ重いわ）
* ただこれに関しては実際手の出しようがなかったが、少なくとも20万件取得しその後に任意の形にするために20万のデータをループで回して整形していたため、これらをクエリで行いループをなくすようにした
* これで更に2000点程度アップ
* インフラ側の修正で4000点程度アップ、reservationsテーブルのuser_idにインデックス付与で1000点アップ
* 16:00
* ここらでベンチの結果が8000点程度になっていた
* 後半はブレインも改修に入り、次にネックになっていたadminのアクションに着手していた
* 僕の方はreservationsに外部キーを貼ったら早くなるかと思い試していたがinitialで失敗してしまうことから外部キーを貼るのは断念した
* canceled_atを条件にしているところが多かったがnull許可になっていたためインデックス貼っても効果なしと判断し行わなかった
* 17:00
* そろそろ手が重くなってくるころ、sheetのデータをget_eventで作っていたが毎回呼ぶ度に作るのは無駄なのでキャッシュ化することに
* しかし地味にキャッシュ化に時間がかかりこの改修は入れることができなかった
* 17:30
* ブレインが行っていたadminの部分の改修が完了しベンチを走らせたところ一気に15000点にアップ（この日1番のテンション）
* 他にもいくつか入れたい改修があったが断念しここで時間切れ
* 最終スコア15842

![スクリーンショット 2018-09-15 17.50.45.png](https://qiita-image-store.s3.amazonaws.com/0/67471/440027a0-3225-38ac-58fb-dc69fb90abf9.png)


# 結果

| 初期スコア | get_eventのN+1改修 | reportsの改修 | インフラ側改修 | インデックス付与 | adminの改修 | 最終スコア |
|:-:|:-:|:-:|:-:|:-:|:-:|:-:|
| 1500  | 2500  | 5000  | 7000  | 8000 | 15000 | 15842 |

528チーム中64位
http://isucon.net/archives/52467371.html
![スクリーンショット 2018-09-25 15.35.51.png](https://qiita-image-store.s3.amazonaws.com/0/67471/63d7e41f-c749-7d02-1ba0-5853c3f61722.png)


# 感想と反省

* 今回チーム目標として100位以内を目指していたので達成できたのは良かった
* 参加4回目ということで慣れてきた部分もあり、順調にスコアと順位を伸ばせているのが良かった
* 前回の反省点としてsinatraの素振り不足があったが、今回は事前に素振りをしていたため特に詰まることはなくスムーズに行えた
* Rubyの配列操作、ハッシュ操作で若干時間がかかったのがもったいなかった
    * 日常業務がPHPのためRubyの素振り不足…
* 前回の反省点としてfail率の高さがあったが、今回はローカル環境を真っ先に立ち上げたことにより手元で確実に動くものだけをベンチで走らせることができたため非常に良かった
* あと入れたい修正が2,3あったので、1つ1つの修正をもっと正確に早く行う必要があるなと感じた

# おわりに

目標は達成したものの結局のところ予選落ちに変わりはないので、次の目標は予選突破です
コンテストとはいえ、エンジニアのお祭り事と捉えているので楽しいのが1番だなとは思ってます:smile:
身近での参加があまり多くないので来年はもっと人数を巻き込んで参加できるとより楽しそうだなぁと思っています
この記事を読んで興味が湧いた人は是非来年参加しましょう:muscle: