ISUCON9予選を1日目上位3位で通過しました

今年もISUCON本戦に行きます!

こんにちは。
チーム「おisu〜」のnagatechです。
チーム名は所属するサークルの挨拶「おいす〜」と「isucon」をかけてます。

この記事はISUCON9予選の参加記になります。(ブログを書くまでがISUCON9予選なので…)

おそらくチーム内でブログを書くのが僕しかいないので頑張って書いていきたいと思います。

チームメンバーは、
  • takashi 大学4年生 Golangバリバリ書けるマン
  • fuji 大学2年生 Golangバリバリ書けるマン
  • nagatech 大学4年生 最近やっとGolangが書けるようになった

の同じサークルの学生3人チームです。

自分がインフラとリーダーを引き受けて、チームメンバー2人にいかにアプリ側を快適に書いてもらうかが自分の中での目標でした。(達成できたかな?)

結果はすでにタイトルでネタバレしていますが、最終スコア22580で、予選1日目の上位枠で本戦出場が決定しました!!! 去年も本戦には出場できたのですが、学生枠のギリのギリって感じだったのが、今年は学生チーム関係なく予選上位枠で本戦出場を決めることができて1年間の成長を感じています。すごいぞ自分。チームメンバーが強いからなだけの気もするけど。

予選本番前にやったこと

参加記を書く前に、予選前にやったことを紹介しようと思います。

ざっくりいうと、所属しているサークルで本番さながらに部内ISUCONをやりました。

詳しくはこちらの記事にまとめていますので、興味がある方は是非ご覧になってください。 ここで初動の確認やチームの分担、終盤の方針などを固めることができたのはかなり良かったと思います。

何度も言いますが、社会人に比べて技術はないけど時間はあるのが学生の強みなのでそこを有効活用しないと学生チームは勝てないと思っています。

参加記

自分は今回もインフラ担当だったのでアプリ側のコードに直接手を加えておらず、曖昧な部分もありますがご了承の上見てもらえると嬉しいです。

あとだいぶ懺悔が入ります。

当日朝

最近早起きを心がけているので7:30に起床成功。

大学の講義室を借りて3人で集まってやりました。

10分の競技開始遅れのアナウンス。
1度でも運営側にまわると苦労が分かります。

10:10〜11:10 インスタンス構築

今回戦犯レベルにやらかしたとこです。

レギュレーションを確認すると、3台までインスタンスを競技に使って良いということだったので、ISUCONやるぞやるぞやるぞと3台一気にインスタンスを立てました。(10:20くらい)

作った3台のインスタンスにログインしようとしてできなくて、ここでキーペアの登録とRAMロールの設定を飛ばしていたことに気づきました。
rootのパスワードも勝手に設定されて知らないのでssh接続できず…泣く泣く作ったインスタンスは全部削除(リリース)しました。

(追記: rootのパスワード再設定とRAMロールの追加はAlibaba Cloudのコンソール上からできたらしいです。普通に知らなかったのと、その時はめちゃくちゃ焦ってたので全削除しました)

ここで第二の問題が。
セキュリティリスクの検知でインスタンスが立てられなくなりました。

すぐさま運営に連絡して対応待ち。
その間なにもすることがなく、すでにお通夜ムードになってて自分は泣きそうでした。
せめて仕様書もインスタンス内ではなくgistに公開されたレギュレーションに含まれていればアプリの仕様書を読むことで時間を有効活用できたのに…

なにはともあれすぐに対応してもらい、10:40にインスタンスが作成できるようになりました。
その節は運営にご迷惑をおかけしました。本当にありがとうございました。

つかの間に第三の問題が。
インスタンスの起動にめちゃくちゃ時間がかかりました。

その間サポートチャットを覗いていましたが、みなさん時間がかかってるようで待つしか…状態になってました。
チームメンバーがイライラしない人たちで良かったです。本当に。

11:10くらいに1台目のインスタンスが立ち上がり、ベンチを回して正の点数が取れることを確認しました。
初期スコアは2210でした。(ちなみにGo実装です)

ここまででちょうど1時間です。(つまり自分のミスで競技時間が1時間減った)

本戦では絶対にやらかしません。逆に本戦に向けたいい教訓になったかもしれないです。

11:30 (前半戦) 各種計測ツール導入 & 改善開始

kataribe、pprof、myprofilerを導入して計測をはじめました。
takashiがモニタリングツール用のパソコンを持ってきてくれました
ここでチームの役割分担について紹介しておくと、
  • nagatech 全体の指揮・意思決定・インフラ・デプロイまわりの管理
  • takashi オンメモリ戦略などがっつり書き換える系の実装担当
  • fuji N+1問題など確実に積み重ねていく系の実装担当

という感じで競技をやっていました。
このチーム編成はISUCONをやっていく上ではかなり有効だったと思っています。

前半(15時くらいまで)にやったこととしては、
  • キャンペーンについての把握(大事)
  • ShipmentServiceURL, PaymentServiceURLをオンメモリに
  • categoryのオンメモリ化
  • getNewCategoryItems内のN+1解消
  • postRegister内のハッシュの計算コストをmincostに変更
  • 外部APIの呼び出しを抑制
  • mysqlのチューニング(MaxConnections・BufferPoolSize)
  • 複数台構成(Nginx+App・DB・App)に変更
  • POST /loginだけ3台目にプロキシ

といった感じです。

ここまでやってスコアは5650でした。
外部APIの呼び出しあたりで502(Bad Gateway)がエラーとして返ってきてるというベンチマークのMessageで、「外部APIが詰まってるのかな〜」という推測でtakashiが外部APIの呼び出しを抑制してくれる実装をしてくれたのですが、キャンペーンレベル0でスコア5500前後、レベル1だとスコアも上がらないし、たまにfailするという状態が続きました。

15:30 (後半戦) failの原因特定・得点が跳ね上がる

原因としては、外部APIが詰まってエラーが返ってくるのではなく、nginxで「Too many open files」にひっかかっていることでした。

ずっと外部API側の制限だと思っていたのでnginxの設定が原因だとは思わず、発見が遅れてしまったのは反省点の1つです。

ともあれ、OSのファイルディスクリプタの上限を引き上げて、nginxのworker_rlimit_nofileの設定をいじってベンチを回すことで、得点が12490まで上がりました。また、キャンペーンレベルを引き上げてもfailすることはなくなりました。

これで全体暫定4位まで上がりました。
予選上位枠を意識しはじめるようになったのはここらへんからです。
記念スクショ
後半(17時くらいまで)にやったこととしては、
  • getTransactions内のN+1解消
  • getTransactions内でgetUserを呼ばないようにする(セッション情報から引っ張ってくる)
  • nginxのチューニング
  • DBに(とりあえず雑に)インデックスをはる

という感じです。

この時点でスコアが19000点前後を安定してとれるようになっており、全体3位でした。
にがり(とーふとふ)が落ちたことにより暫定2位になった記念スクショ
予選上位枠はともかく、本戦出場ラインはすでに超えているだろうと判断し、早い段階でコードに大きな変更を加えない方針でいくようにしました。

ただ、解析とコードリーディングは続けるようにし、最後まで諦めない姿勢は保ちました。(結局自分の判断でコードの改変は止めたのですが)

17:20 (終盤戦) 再起動試験・ベンチマークガチャ

時間的な余裕もあったので慎重に再起動試験をやりました。
慎重に再起動試験をやるnagatechの図
インスタンスの状態チェックも同時にやりました。
(もしダメだったらどうしてたんでしょうね、インスタンスを立てた直後にやるべきでした。)

残りの時間はDBのインデックスについて、ベンチマークを回しながらチューニングをしていました。

余談ですが、ベンチマークのPOST /initializeinit.shが毎回呼ばれるという仕組みは去年の予選でも同じ構成だったので、書き換えるべき場所は瞬時に把握できました。
ここら辺は事前にISUCON模擬をやっててよかったと感じたところです。

20000点を超えたらやめるという方針にしていたので、17:55にスコア22580を記録した時点で競技を終了しました。

18:10 終了・感想戦

最終的なグラフはこんな感じです。
競技が終わった後は、別室でやっていた同じサークルの他のチームと集まって感想戦チャットを見つつオフラインでも感想戦をやりました。

にがり(とーふとふ)とかいう人の予選中のスコアの動向は追っていたので、まあ1位確定だろうみたいな話をしてたら感想戦チャットで運営の方が「いま運営で見えている1位は1人チームです」というのをぽろっと漏らしたのでうわあ強いなあという感じで盛り上がりました。

(追記: チームにがりは1日目1位通過でしたが、本戦を辞退しました)

にがり(とーふとふ)は自作Makefileで自動化できるところは全部自動化することで一人でも闘えるようにしていたようです。

詳しくは彼がブログを書いてくれると信じています。
(今年の1月から更新が無いけど…)

19:30 結果発表

ちょうどとーふとふとご飯を食べにいくために大学構内を歩いている時でした。
結果欄に自分のチームの名前があるのを見て外であるにもかかわらず叫んでしまいました。

そのままお店で祝賀会をしました。

やった〜〜〜〜〜

感想 & 反省

リーダーとしてはうまく本戦出場に導けた気がしますが、エンジニアとしてはポンコツであり続けた予選でした。

実は上に書いていないだけでインスタンスの起動失敗の他に、
  • .gitをroot権限で破壊する
  • インデックス設定時にDBを破壊する
  • 設定ファイルをtypoしてサービスが起動しなくなる

など、普段やらないミスを本番でやらかしてしまったのは自分の中で反省ポイントです。

これらをリカバリできる力がついたといえば成長なのですが、ミスはできるだけ無くした方がいいので本戦では気をつけます。

何はともあれ予選通過できてよかったよかった。

おしまい

2日目の結果を見たら学生オンリーのチームが4チームしかいない…

全体5位だし優勝ワンチャンあるのでは?という気持ちになってたりなってなかったりします。
頑張るぞ〜〜〜