第4回模擬ISUCONやりました
こんばんは。
昨日から東京に来てるぼくです。
実は明日の8月25日にLINE本社で行われる学生限定のISUCON夏期講習のためにきております。
チーム「仕事ください」の代表として、沖縄の風を吹かしてやろうかなと。
あ、あと先日メルカリでアラレちゃんステッカーを買いました。
.......はい、そうなんです。アラレちゃん大好きマンです。
届いたらPCに貼ってお披露目しよっと。
では、近況報告はこのくらいにして本題の方へ!
第4回模擬ISUCON開催
先にマサの記事はっつけておきます。
そしてごめんなさい。進歩がなかった第3回はなかったことにしますww
一応過去のやつ貼り付けておきます。
第1回
第2回
そして今回の問題はISUCON6予選。
GitHub - isucon/isucon6-qualify
久しぶりにチーム「仕事ください」4人全員集合 + お師匠さん参戦。
もちろん場所はギークハウス沖縄。
ぼくはキャバクラにあるようなソファで参戦しますwww
今回もAzureで立ち上げます。
なのですが、プロビジョニングがうまくいかないのお師匠さんが修正してくれました。
それがこちら。
GitHub - saboyutaka/isucon6-qualify
1.いつもと同じです
はい。ログやらツールやらはいつもと同じです。
上の過去記事で割愛ww
2.仕様がいつもと違うぞ
今までの仕様はアプリケーション1つ。
けど今回は3つもありました。
・isuda
viewが表示するメインの部分
・isutar
ふぁぼった時にぽちぽちされる星の挙動を制御する部分
・isupam
keywordをpostする時に不適切な言葉が入ってないかチェックする部分
これら3つをsystemctlで起動させて初めてスタートします。
今までになかった構成なので少し戸惑う。
あと初期実装はPerl。
なので実装言語をRubyに切り替える必要あった。
3.毎回恒例、ボトルネックを探せ!
まずはnginxのログでボトルネックを探します。
もちろんalpで整形したログをね。
※すいません、またもやキャプチャ忘れました
特に重いのが
get " / "
たしか4000msもかかってた。
クエリも13ぐらいだったのでN + 1もなさそう。
よって最初のボトルネックとなるのは、どうやらトップページとなりそうです。
4.rack-mini-profiler登場
アプリケーションのチューニングを行う時、ぼくらはいつもローカル環境でいじいじします。
そこでいつも使っていたのがrack-lineprof。
GitHub - kainosnoema/rack-lineprof: Rack middleware for easy line-by-line profiling using rblineprof
とあるリクエストに対して、どの処理に何秒かかっているかがわかるので一発でボトルネック探すことができる。
これはめっちゃ便利なのですが、今回はお師匠さんのススメで、
rack-mini-profilerというgemを使います。
まぁ好みだとは思うんですが、ぼくはmini-profiler、マサはlineprofでやります。
この時点で
・ぼくはnginxを見ながらマサとアプリーションコードの解析
・diskとディックス君でDB周りとその他もろもろ
こんな感じでやっております。
ちなみにスコアは0で、初期スコアのまんま。
5.スコア0地獄脱出
今回のボトルネックはトップページです。
マサとコードの解析をやりましたが、前回の反省を踏まえて、
「コードと実際の挙動(ブラウザ)を照らし合わせる」
を意識。
今までよりも丁寧にコードを読んでいった感じです。
プロファイラからもわかるのですが、犯人はこいつ。
def htmlify(content)
keywords = db.xquery(%| select * from entry order by character_length(keyword) desc |)
pattern = keywords.map {|k| Regexp.escape(k[:keyword]) }.join('|')
kw2hash = {}
hashed_content = content.gsub(/(#{pattern})/) {|m|
matched_keyword = $1
"isuda_#{Digest::SHA1.hexdigest(matched_keyword)}".tap do |hash|
kw2hash[matched_keyword] = hash
end
}
escaped_content = Rack::Utils.escape_html(hashed_content)
kw2hash.each do |(keyword, hash)|
keyword_url = url("/keyword/#{Rack::Utils.escape_path(keyword)}")
anchor = '<a href="%s">%s</a>' % [keyword_url, Rack::Utils.escape_html(keyword)]
escaped_content.gsub!(hash, anchor)
end
escaped_content.gsub(/\n/, "<br />\n")
end
このhtmlfyは、ユーザーが投稿したワードを全件取得して色々分解して組み立てなおして、最終的にキーワードにしてリンクを作るというもの。(多分)
gsubとかRegexpとかよくわからんが。
まぁ、最終的にキーワードのデータがほしいとのことなので、全件取得する必要がないと判断。
isuconではお決まりのパターンやね。全件取得作戦は。
keywords = db.xquery(%| select * from entry order by character_length(keyword) desc |)
なのでこれを
keywords = db.xquery(%| select keyword from entry order by character_length(keyword) desc |)
こう。
* を keywordに変えただけ。
すると3000msも縮まっていい感じ。
このままベンチ走らせます。
"fail":0の4583点!!
念願のスコア0地獄から抜け出せた。
再度ベンチ走らすとタイムアウトしたりするのでnginxのkeepaliveあげたりと色々いじいじ。
女将さん(nginx)をナイスバディにしてあげて5700点くらいまで上がりました。
6.静的ファイルどうにかしたい
alpでnginxのログを確認します。
jsとかcssが厄介だったのでどうにかしたい。
なのでnginxで対処しようと思いました。
参考にしたのがこれ。
単にexpiresつけるだけだったんだけど、なぜかうまくいかずハマった。
気づいたらタイムアップ。(オワタ....)
終了後にさぼさんからもらったアドバイスとしては「キャッシュ作戦」
7.反省
Done
ToDo
今回のISUCON6予選問題は今までやってきた中で一番ムズかった。
てかよくわからなかったwww
DBはそんな重くないし、N+1問題はないし、とにかく過去の経験がほとんど通用しない今回であった。
よかった点としては、まずスコアが出せたこと。
これは嬉しかった。
落ち着いて丁寧にコード解析していけばいい感じ。
あとは各設定とかかな。
自分のとこは今回素早くできたので手空いたらサポートに回れるようにしたい。
一番の反省は
アプリケーションのチューニング
今回はsinatraが激重だったのでより痛感した感じ。
やっぱりここがっつり触らないと点数伸びないね。
あとは自分の女将さん(nginx)愛が強すぎてずっと女将さんとイチャイチャしてしまうところww
結果論だけど、あのままnginxをもっとチューニングしてもスコア伸びなかったぽい。
もっと女将さんを理解して、「nginxよりもsinatra」っていう優先順位をつけていきたい。
色んな反省点がある中で、今後の取り組み方も見えてきた。
・1人がログファイルやconfなどの各種設定
・2人でソースコードの解析
nginxのログとrubyのprofilerでだいたいのボトルネックは推測できることがわかった。
いつもそうだけど、ログファイルの設定とかconfファイルの設定に時間がかかりすぎるんだよねぼくたち。
なのでここらは1人でやって、残りの2人でアプリケーション本体を触っていくほうがよさそう。
とまぁ、こんな感じです。
肝心のアプリケーション頑張りまする。
最後に、お師匠さんがまとめてくれたisuconで使うツールのまとめ記事をはっつけて置きます。
これでアプリケーション触る以外の逃げ道がなくなったwww
ではでは、今日はこの辺で。