ISUCON9予選惨敗録
こんにちはnasaです。
ISUCON9で惨敗したので来年に向けて筆を執りました。
事前準備
特になにかやった感は無いですが、alpやpt-query-digestのインストールスクリプト、nginxのアクセス解析、mysqlのスロークエリ出力の方法をまとめたり、をやっておきました。
あとはチーム内で最初に何をやるかを話し合っておきました。
チームメンバーはインフラ担当akidukiさんと、インターンで会った強いお方kazさんです。
惨敗当日
とりあえずボクの知ってる範囲でやったことを書いていきます。
マニュアル読み読み
kazさんがインスタンスを立てている間にボクとakiさんでマニュアルを読みました。
ISUCARIは椅子を売りたい人/買いたい人をつなげるフリマアプリです。
ここでボクとakiさんはクスクス笑ってました、、、 kazさん邪魔してごめん、、、
今回は負荷レベルを自分で上げていくんだな〜というのがマニュアルを読んで分かりました。
Git管理
ここでコードをローカルで開発出来るようになりました。 go buildすると特にハマりどころ無くビルド出来て楽でした。
ここで最初にgit add
する時に余分なファイルもgit管理してしまい、他のインスタンスを立ててmaster pullすると、エラーを吐いてしまい、pull出来ない状態になってしまいました。
結果として、要らないファイルを消せば行けたので、最初から余分なファイルはaddしないようにしたいと思いました。
アクセスログ、スロークエリを見る。
kazさんとakiさんがやってくれました。
ここでベンチを回して、ログを見ます。 あとhtopも(初期スコア2000くらい)
htopを見るとmysqlの負荷が100%になっていました。合わせてスロークエリもpt-query-digestで集計してみると、クエリ発行数が偉いことになっていて、n+1いっぱいありそう。と思いながらコードを読み始めました。
getNewItemsのN+1をこの世から消し去る
itemsの数だけ、categoryやsellerをselectしていたので、これを一括で持ってくるようにしました。
この時itemsのselectとsellerのselectを別で書いてましたが、今思うとjoinして持ってくればよかったなーと、、、 反省です。
categoryの方は変更がないデータだと分かったのでkazさんがキャッシュしてくれました。
ここで3000イスコインくらいになった気がする。
indexはりはり。
itemsのseller_id、buyer_idにはりはり。 あとは、(status, category_id, created_at)にはりはりしました。
これでスコアが、3600くらいになった気がする。 (正直スコアについてはあまり覚えてない)
DBをmariadbに + nginx confをいい感じに
akiさんがやってくれました。 これらに関するconfファイルはgit管理されてなかったので、どんな設定をしたか知らない状態ですが、スコアは4000位になりました。
何したんだろ。
N + 1を駆逐する。
getTransactionsのN + 1を駆逐して回りました。
さっきも言いましたが、joinを使えばシンプルに終わったのに、2つのselectに分けて、goで一緒のstructの詰める実装をしてました。 アホの所業ですが、スコアはこの時5000くらい。
kazさんやakiさんの変更も入ってるので、純粋にこれだけで5000に行ったかは分かりません、、、
終焉ノ刻
ここから地獄だった気がします、、、
何も出来ず終わってしまいました。
スコアの推移はこんな感じです。 一瞬6000に行きましたが、それ以降上げることは出来ず。
反省
来年に向けて反省を書いておきます。
デプロイの自動化
毎回git pull
して、go build
してsystemctl restart
してってやってました。
時間の無駄すぎるし、go build忘れてて変更が反映されてない!という状況が何度もありました、(systemclt restart忘れも)
最初に簡単なスクリプトを書いておくべきでしたね。
git pull origin msater # ここ他のブランチにも対応したい cd webapp/go make build cd ../../ sudo systemctl restart isucari.golang....
雑に書いてもこんな感じでしょうか? 10秒もかからなかったです。
これをやっておけば無駄な時間を過ごさなくてよかったです、、、、
conf系のgit管理
my.cnfやnginx.cnfをgit管理するようにしたかったです。
そして、cp my.cnf /etc/my.cnf
のように反映する処理を.shに書いておけば楽に反映、バージョン管理出来たはずなので。
git管理したかった一番の理由はどんな変更をしたのか共有しづらいというのがあります。 akiさんがばんばん変更してくれていたのですが、「ん?スコア上がったけど何したん。failしたけど何したん?」という状態でした。
git管理して、PR出してくれればどういった変更が入ったのか共有できるし、ミスが有った時に誰かが気づく可能性が高くなるので、来年はそうしたいと思いました。 (ブログにもどういった変更をしたのか書けますしね!)
開発環境の整備
akiさんが開発環境をdockerに乗せてくれたんですが、使い方がマジでわからなかったです。 (すみませんakiさん!無駄にしました、、、)
これをうまく使えてればローカルでゴリゴリ開発していけたんだろうと思いますが、ボクは実際にインスタンスにsshしてgit pullして動作確認をしてました、、、
スコアを確認したいときはこの運用でいいんでしょうけど、今の状態だと本番環境で開発しているのと変わらない状態だったので、非効率だったなーと思います、、 (本番デバッグだめ!絶対!)
本番で開発するならするで、インスタンスをチーム内で振り分けておくべきでした。
「いまから1番使いまーす」や「2番今誰か使ってます?」のような確認を取ることが本当に多かったです。
あとは、ベンチマークをテスト代わりにしてたのも良くなかったです(主にボクが) target serverを切り替える手間もありますし、アプリケーションにアクセスして挙動を見れば済む話だったなーと今は思います。
反省まとめ
今回の反省は主に開発の進め方に合ったと思います。
正直、getTransactnionsの外部APIが遅いことや、post buyのトランザクション周りを改善するというのは時間がアレば行けたのではないかなーと思っていて、すぐに終わらせるべきだったn+1改善に無駄に時間を取られてしまいました。
ローカル(または本番で)素直に開発して、デバッグ出来ればぱぱっと終わる作業だったので。
楽に開発できる環境を作らなかったことが一番の敗因だと思いました。
感想
ISUCON楽しかったですが、悔いしかありません。
タラレバを言っても仕方ないですが、デバッグを楽に出来る様になっていれば、もっと 計測 < - > 実装 を繰り返し出来たし、外部APIの並列化やpost buyの改善のための時間と取れた気がしてなりません、、、
いや、言っても仕方ないんだけど、、、
チームメンバーがその気ならまた同じメンツでやりたいなーと思います。
akiさん、kazさん来年もよろしくです!!