ISUCON9に出場して、予選敗退しました。

はじめに

イタンジ株式会社に出向中の田渕です。
9/7(土)に、ISUCON9に出場して、598組中50番目の最終スコアでした!

再起動試験の結果は不明ですが、通っていても点数が足りず、予選敗退となってしまいました。

予選通過まではあと1000点強でしたので、結構惜しいところでした。。。

f:id:ktabuchi:20190907235600p:plain
ブログを書くのだったら、チーム名をもう少しちゃんとすればよかった

ISUCONとは

お題となるWebサービスを決められたレギュレーションの中で限界まで高速化を図るチューニングバトル、それがISUCONです。(公式サイトのメタタグより)

(ISUCONってIikanjini Speed Up Contestの略なんですね!ブログを書くために調べて初めて知りました)

メンバー紹介&担当

田渕:  
新卒1年目のwebエンジニア。
インフラを触らないこともないが、基本的にサーバーサイドでRubyを書いていることが多い。  
ISUCONは初出場。
競技プログラミングも趣味程度に嗜んでいるため、実装力にはそこそこの自信あり。  
一方でインフラは経験不足のため、戦力になれないと判断し、今回はアプリコードの実装に専念。
中村:   
田渕と同じく、GAからイタンジに出向しているエンジニア。  
ISUCONは2回目の出場。
最近は、エンジニアリング以外の仕事に忙殺されており、「まだエンジニアだと思いたい」とは本人の談。  
インフラとフロントエンドが得意だが、Rubyは書き慣れていない。  
今回では、遊撃として、アプリ側とインフラ側の両側を担当。
福崎:  
イタンジの執行役員。チーム名の由来の2/3はこの人。  
ISUCONは2回目の出場。  
同じ業務に携わったことがあまりないが、かなり幅広くwebエンジニアリングをしているイメージ。  
経験が一番長いこともあり、インフラを担当。

前準備

本番の2週間前に、一度集まりISUCON7の問題を解きました。が、初期スコアからまともにスコアを上げることができずに終わりました。

本当はこれ以外にも各自対策をしようとしていたが、全員本業に忙殺されてしまい、何の成果も得られませんでした!!!!!
ただ、この日に、お互いの役割を決めきれたので、やった意味はかなり大きかったと思います。

教訓: 一度でいいから集まって問題を解く日を作ろう!

作戦

最初の1時間だけ方針を決めていました。slackからそのままコピペします。(内容はこちらのサイトを大いに参考にしています)

## 中村さん

- 全員共通の~/.ssh/configを作る
- GitHubに手軽にpushできるように、deploy keyをサーバー上のisuconユーザーに入れておく
- コードをリポジトリにpushする
- ローカルで開発環境を作れないか考えて、作れそうなら作る
- MySQL・画像などのバックアップを開発環境用に作成

## タブチくん

- コードを読んでGitHubのissueに書く(cf: スクショ・各パスの役割・どういうアプリケーションか・キーになる関数 https://github.com/catatsuy/isucon7-qualifier/issues/1)
- 実際に使ってみてどういうアプリケーションなのか把握しつつ、スクショを取って共有する
- パスの一覧を共有
- スキーマ一覧を共有
- キーになる関数があれば特定する

## ふくさき

- 必要なパッケージなどインストール
- 何もせずにベンチマークを流す
- Ruby実装に切り替えてベンチマークを流す
- nginxで計測できるようにする
- ハードウェアの構成を調べる
- 動作しているプロセスを確認しておおよその構成を理解する
- データベースなど各アプリケーションの設定値を確認してgitにコミットする

最初はよほどの猛者じゃない限り焦ると思うので、最初だけは方針を決めておくと、迷走しにくいと思います。
残りは出たとこ勝負で行こうと決めていました。

当日

全員の記憶が許す限り書き下していきます。そのため、一部は田渕以外の2人が書いた部分もあるため、文体などのブレはご容赦ください。

開始まで

前日も特に連絡せず、田渕は15分前に会社に到着。
よく考えたらRAM(AWSで言うところのIAMに相当)ユーザーを作っていなかったことに気づき、慌てて発行。
1分前に中村が到着して、準備が間に合わないかと思いましたが、開始が10分遅延したので僥倖と言って笑い合っていました。

午前中

isucon7もubuntuだったのでisucon7ベースで今回用に用意していたansibleを使い以下の環境を整えました。

- mysqlにslow queryのログ仕込む
- alpとdstatを入れる
- nginx,mysql,webアプリのdeploy環境を構築
- nginxのログ設定をalp用に変更
- alpで計測結果を集計して共有

福崎が作った最終的なansibleはこちらです。

田渕が速攻でインスタンスを3台立てました。最初のベンチを走らせるまではかなり早く、3番目くらいだったと思います。
予定通り中村がgithub周りの設定を整えて、福崎がベンチを走らせましたが、最初から落ちたので焦りました。

もう一度走らせたらベンチが通り、初期スコアは2000強でした。(最初のベンチが何故落ちたのかは謎のまま)

その後、Ruby実装に切り替えたりloggerを仕込んだりgithubの設定などをしているうちに、""何もしていないのに""ベンチが落ちるようになりました。

インスタンス上のアプリケーションディレクトリを直接リモートリポジトリに同期させようとした都合で、リポジトリ上での管理漏れで色々トラブったからでした。 色々の内訳として以下がありました。

* 仕込んだloggerが吐かれるディレクトリのパーミッションが間違ってた => コンテストなんで割り切ってchmod777のガバガバ設定にした
* 画像アップロード先のディレクトリのパーミッションが(略
* systemd経由で実行すると `bundle rackup command not found` => .bundle/config ちゃんと設定した

この辺は、毎回localからansibleなりでデプロイするという方法をとればそもそも避けられたのですが、 それぞれのインスタンスで並行で作業しながら、version切り替えつつ試行錯誤してベンチ回せたので、
結果的には良かったかなと思います。そう思いたい。

この間に田渕がさっくり実装を読み、ローカル開発環境をなんとか整えました。
ベンチもなんとか通り始めて、「俺たちのISUCONはまだ始まったばかりだぜ(涙)」となっていました。

また、ざっと見て静的ファイルがsinatoraで配信されてたので、福崎がnginxで配信するように変更しました。 しかしスコアに全く変化がなく焦りました。(むしろ下がった)

福崎がUberEatsで頼んだモスバーガをみんなで食べました。美味しかったです。

午後①

田渕が初めてのまともなPRを出したのが12時46分でした。なお、get '/new_items.json'のN+1問題を修正するだけでしたが、しっかりバグらせて、レビューの中村さんが止めてくださりました。
レビューの修正後にも他の箇所がバグっていましたが!!!競プロで培った実装力とは何だったのか。

同時に福崎と中村が冗長構成を整え始めて、スコアが3000くらいまで上がりました。

冗長構成の内訳は以下の通りでした。

- DB + app(画像のアップと参照を行うuploadとsellのみ)
- app
- app

ALBに身体がなれきっており、nginxでロードバランシングするの何気に初めてだったので、無駄にちょいちょいハマります。 この辺は予習しておこうと思ってたけどやれてなかったので反省です。

田渕がもう一つのPRでget_config_by_nameをキャッシュする実装なども行いました。

ベンチの回し方が悪く、どれが利いたのかはわかりませんでしたが、じわじわとスコアは上がっていきました。

午後②

福崎と中村が着々と冗長構成を進める。

田渕がやることがなくなったので、レギュレーションをよく読んで、気になるところを二点見つけました。

まずはキャンペーン率。とりあえず5パターン全てを試してみて、増やせば増やすだけ負荷が増えていくことを確認しました。

次に以下の記述です。

新着一覧については、上記の制限を満たした上でよりユーザにあわせた商品の一覧を返すことで、購入の機会を増やすことができます。

新着一覧については、すでに売れているものを見せる必要はなさそうです。というわけでクエリを変更しました。
加えて、金額の上限も5000にしたりして、少しでも購買意欲を掻き立てようと足掻いていました。(効果は不明です)

ここらへんで3台すべて使えるようになり、スコアは6000点程度でした。

夕方

pumaの設定のチューニング・残っていたN+1問題を潰すなど、細かめの改善を重ねます。

nginxのworker数を増やしたりpumaのthreadを増やそうとしました。
rackupで起動するってのをやったことがなくconfig/puma.rbを設置して、pumactlでsystemd経由で起動させようとするが起動せず焦る。

最終的にはrackupでもconfig/puma.rbを見てくれることに気づいたので、pidfileとdaemonizeの指定を外して無事起動。

冗長構成が完成した後は、get /users/transaction.jsonあたりを改善しないともうスコアが上がらないと思いましたが、時間が足りませんでした。

最後

pumaの設定を限界までリソースを使い切るように変えて、再起動試験を行った結果、ベンチで頻繁にfailが起こるように。

よくわかりませんでしたが、負荷が大きすぎると考え、キャンペーンの値を3に下げました。
するとベンチが安定し、ベストスコアも出るようになりました。

1時間くらい余っていましたが、最後は安定を取ってほとんど触らず、ESC(AWSで言うところのEC2)の設定などを見返していました。

数回ベンチガチャを回して、最終スコアは8730でした。

感想

全員の感想を集めたので、単に貼り付けます。
(執筆に疲れたとも言う)

田渕: 
初出場にしては頑張れた気がします。
bcryptがかなり重いことを知っていたし、実装で変えられることにも一度は気づいていたのですが、
途中で完全に忘れていたのが勿体なかったです。
インフラ周りで全く力になれなかったので、そちらも鍛えたいですね。
あと、みんな言っていますが、計測は大事です。
施策の数としては悪くなかったのですが、重要なところを修正できなていなかったと思います。悔しい。
中村:
4~5年振りの出場です。
インフラも得意と言いつつ最近は基本的にコンテナで開発していたので、色々しどろもどろでした。
systemdとか久しぶりに見た。イップマン観てないでRubyもっと頑張るぞ。
福崎: 
計測がalpだけだったけど、rubyアプリのプロファイラを入れて計測できるようにしておけば、
もっと的確に優先順位つけて対応できたなと反省、次こそは予選突破するんご・・・!
予選突破できなかったけど業務では直接関わらないメンバーとやれてめっちゃ楽しかった。
課題も面白かったです、運営の皆様ありがとうございました。