異世界転生してISUCON9に参加しました

TL;DR:

ISUCON9で予選敗退しました。悔しい!!!!!!!!

これはなに

初めまして。皆さんご存じ梅の仁です。

ひょんなことからISUCONに初参加してしまったフロントエンドマン、梅の仁!「フロント改善してやる」と思ってたら、フロントはほぼいじれないしバックエンドが迫ってきた!

果たして持ち前の才能で「あれ?俺、なんかボトルネック解消しちゃいました?」となるのか!?

いきさつ

ISUCONの存在を学友から聞き、興味が湧いていた所に@_nemohemからお誘いを受けました。「わ~い@_nemohemと一緒うれしい参加する~」と伝えた所、@_nemohemが@anqouを連れてきたので、「あれ?2人ともヤバくね?マジ卍」とか思っていました。

私は二人とも面識があったんですけど、@_nemohemと@anqouにはあまり面識がなかったようで、最初の顔合わせの時にげらげら笑った記憶があります。私の認識の中でのチームメンバーはこんな感じ。

わし:フロントエンドマン

担当する予定だったこと

  • それとなくインフラ担当
  • クエリを見つめる担当
  • スピリチュアル・エンジニアリング担当

@_nemohem:アプリや人工知能系などモダンな開発にお強い人

@anqou:C++と言語処理大好きな深海魚鯖管。言語を理解するのにコンパイラを実装するやつ。

二人に任せる予定だったこと

  • Golang

前日譚

ISUCONの過去問を解くまでは、フロントエンドを普段書いているのでせっかくならフロントを改善しようと思っていました。「フロントってそんなに改善ポイントあるのかなあ」と思っていたら、フロントは競技自体に含まれないと発覚。他チームメンバーがGolangを理解しそうで、かつ自分がMySQLぐらいしかわからなかったので、MySQLを主軸としてインフラを担当すると決意しました。

そこからは、MySQL知識に特化すべきと、書籍を数冊読んでみたり、App-Go-Tourをかじったりしました。さらに、ISUCON過去問をvagrantで提供してくださっているrepoがあったので、「vagrantってなんやdockerとはちゃうんか」とアワアワしながら解いたりもしていました。

あと、「インフラ担当は全体の司令塔になるべきだよ」という参加記を読んでいたので、他のチームの過去問の解法を見て、様々な構成を知識として仕入れるようにしていました。しかし私では司令塔になり切れず……アプリをやってくれる二人、割と司令塔いらずだったと思います。

当日

08:30 0次戦

起床成功です!!!!!ISUCON優勝!!!!!

09:40 0.5次戦

現着です!!!!!ISUCON優勝!!!!!

10:00 始動

@_nemohemがインスタンス建てと初期設定、@anqouがマニュアルを読んでいる横で、環境構築とレギュレーションを一読しました。

SSH通ってからは、まずみんなでアプリの動作確認、ベンチマーカーの挙動を確認しました。 「椅子借(?)り」は最高の命名センスだと思います。

アプリを一通り理解したのち、MySQLのログの収集を始め、スロークエリの把握と伝達を行いました。次にER図が欲しいよなあと、MySQL WorkbenchをSSH接続させようとしましたが、過去問のようにうまくいかず断念。

ここらへんでスキーマを眺めてると、mediumblobを使ってるテーブルがあったため「ISUCONの過去問で見たやつだ!」とわくわく。また、@_nemohemが固定データのDBを調査していた気がします。

11:00 インデックス貼り

N+1問題を解決してる人(@anqou)やコード読みをしている人(@_nemohem)の横で、あれやこれやとインデックスを貼り、初期段階でのスロークエリを撲滅。ついでにMySQLのパラメーターの初期設定を投入しました。

あと画像ファイルの配信がshippng.img_binaryなのかを@_nemohemが特定し、DBは関係ない?ぽいと分かったので、nginxの設定をしようとしましたがよくわからず断念。

ガバポイント:そう!インフラ担当と宣いながら!nginxの勉強がおろそかだったのである!

原因としては、/webapp/publicのパス間違えと、locationの指定ミスでした。静的ファイルの場所を突き詰められてなかったのが原因です。このせいで@anqouの負担が2倍になりました。ほんとごめんなさい。

12:00 ごはん

一つ目のN+1が解決の目を見たらしいので、ベンチを回すとなんか動かない。負荷が足りないのかとcampaignを増やしてもらうとMySQL側がToo many connectionsで死んだので、max-connections, wait-timeoutの調整で対応しました。ここらへんは過去問を解いていたのが活きました。

ここで名残惜しいながらもいったん昼食。@anqouと食べたものが一緒だったので、以下@anqouの参加記から引用します。

諦めてご飯を食べに行く。店に入るほどの余裕はなかったのでローソンで冷やし中華を買って食べた。麺がゴムっぽかったが、お腹も減っていたのでまあまあ美味しかった。タレで味がついていれば大抵の物は食えるんだなぁ。みつを。

みつを。

13:00 いつも心にB+tree

スロークエリをさらに切り詰めると、categoriesがそろそろ無駄になってきていたのでGolang側に乗せてもらうことに賛成する。インデックスがうまく効いてなさそうなクエリをexplainして眺め、あれやこれやと解決方法を試しUsing filesortは殺しきる。Using indexはきもちええんじゃ。

いったん置いておいて、12時ごろの作業の続きとして、fs.file-maxipv6の無効化などのカーネルパラメーターの調整を行う。

14:00 nginxなんもわからん

nginxのキャッシュ、圧縮、パラメーター調整を行う。これによって304がブラウザ上でしょっちゅう帰ってくるようになったが、多分実装ミスの気がしている。あまりスコアが伸びず、nginxが悪いと推測しキャッシュと圧縮を外す。

ガバポイント:推測ではなく計測結果で語るべき。

「15時には複数台構成を導入しよう」と@anqouから提案を受けていたため、ちょっと焦りつつも@_nemohemがインスタンスの用意を始める。

15:00 複数台へ

複数台構成にするにあたって会議。@anqouがnginx,app,dbサーバーを提案し、私がnginx(+app),nginx+app,nginx+dbサーバーを提案。この構成については、ISUCON7予選を突破したチームのものを参考にしてました。nginxレイヤーで画像のアップロードに対応できるのがよいという話になり、構成を決定。

今までのconfファイルなどを突っ込んだrepoを@anqouが用意してくれたため、nginx+DBサーバーを用意し、パラメーターの調整を始めました。

ガバポイント:カーネルパラメーターの設定ファイルをrepoに移すことを提案しそびれる。

16:00 やらかし!!!

nginx-MySQL間の通信がTCPベースでどうもうまくいかず、@anqouに見てもらうように依頼。(ごめんなさい!)

anqou「MySQLってTCP3306よね」

ガバポイント:わし「多分そう」

などと会話しながら、 あれやこれやと試すもどうもつながらない。解決できず悶々とする。

もちろん試すわけですから、mysql -h hoge -u piyo -pコマンドとか、mysqlコマンドとかをいろんなターミナルで叩くわけですよ。ログとかもたくさん出るわけですね?その時梅の仁に激震走る。

$ mysql Can’t connect to local MySQL server through socket ‘/hogefuga/mysql.sock’

ガバポイント:わし「あっあっあっUNIX DOMAIN SOCKETでいいやん!!!!!!!!!!!!!!!!!!!!!」

ここに気づいてからはチームで爆速で修正し、画像のアップロードにも何とか対応してくれました。ブラウザ上で動くようになりました。マジでごめん。

17:00 ラストスパート

クエリを最後まで見つめる。

`status` IN ('on_sale','trading','sold_out','cancel','stop')

とかが確実に不要と気づき、修正すると当該クエリが10倍速くなりご満悦。

ベンチが再起動直後にfailする不安と闘いながらも、無事通ることが確認できたため再起動試験に移行。プロファイルの除去、サービスの再確認、ロギングの削除を行って再起動。数回ベンチを回すと、17:45に過去最高得点が出たので撤退。最終スコアは9670でした。

打ち上げる

モダン焼きを食べながら打ち上げ。感想部屋を見ながら「デッドロックか」「非同期API~~~!!!!!」と叫ぶ。確かにFOR UPDATEあったもんなあ、とクエリを見つめる側から大反省。打ち上げ中に順位の発表があり、10000点ほどがボーダーと知り死ぬほど悔しがる。でも初参加にしてはいい点が取れたよね、と大満足。

私も無料で新宿に行きたい人生だった…椅子から100万円手に入れたかった…(?)と思いながら帰路につくと、9880点がボーダーと変更になりさらに悔しがる。

さらに参考スコアの発表となり、9880点のすぐ下につけていたと分かり泣く。再起動試験に通っていたかは定かではない…けどね!

反省

今回のISUCONではアプリ側の改善の量が多く、いくらインフラ担当と宣えどもGolangの修正を担当すべきでした。例えばN+1問題のクエリをもらい、すぐ解消できるようなクエリを発行するとかならできたでしょうね。アプリ側の人に任せっぱなしなのはよくなかったなあと。

実装速度が遅いのも考え物ですね。タイピング鍛えます。

あとアクセス解析という観点があることを感想戦で知りました。スコアの出し方が「売上のコイン数」であることをマニュアルからちゃんと読み取れば、アクセスの偏りや、売り上げを伸ばすために表示を調整するなど、まだまだ取り組めたことがあるように思います。

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

↑↑↑これとかね…!!!(マニュアルより引用)

あとガバ大杉な!!!!!!!!!!!!!!!!!!ア!!!!!!!!!!!!!!!

次があるなら、まずnginxを理解して参戦したいですね。そしてもっとインフラチューニングができるようになりたい。

チームメンバーと、過去問の情報を提供してくださってる皆様、楽しい大会作ってくださった運営の皆様に感謝し私のISUCON参加記は終了です。お疲れさまでした!