大切なことは全て新卒ISUCONが教えてくれた
はじめに
弊社では毎年新卒研修の一環として、新卒ISUCONを開催しています。
今回は新卒ISUCONまでに準備したこと、本番の流れ、学んだことなどを振り返っていきたいと思います。
新卒ISUCONまでに準備したこと
自分はISUCONに関しては、全くの未経験だったので、チームメンバーの@takoseと2回ほど練習をしました。
会社の方で用意してもらっていたAWSのインスタンス上で、ISUCON6の予選の問題を使い、練習に取り組みました。
ISUCON6 予選問題の解説と講評 : ISUCON公式Blog
systemdの使い方が全くわからなかったり、git管理や権限周りなどの基本的な部分で手こずりながらも、ISUCON経験者の@euglenaに色々教えてもらいながら、チューニングしました。 この段階で、
計測して、ボトルネックを特定することが大切。
ということを、@euglenaから口酸っぱく言われていました。
さらに、普段触らないインフラ周りで戸惑うこともあったので、いくつかスクリプトなども用意しました。
さらに当日の流れや役割分担を ISUCON7 予選突破コードをissue & PR付きで大公開! | Wantedly Engineer Blogを参考に 最初の1時間は
- セットアップ周り→@takose
- アプリケーション周り→@ykamez
という形でざっくり決めました。
本番の流れ
本番は、11:00~17:00が競技の時間で、12:00と15:00に30分ずつ、メンターに相談する時間があるという形でした。 また問題はYahoo! JapanさんのGitHub - yahoojapan/yisucon: Yahoo! JAPAN の社内 ISUCON である Y!SUCON です。を使わせていただきました。
時系列ごとに振り返っていきたいと思います。
はじめの1時間
@takoseにgit管理やツールの導入などをしてもらっている間に、自分は
に取り組むと決めていたのですが、2 に関しては、明確に何をするかまで決めていなかったことに加え、まだ計測ツール(myprofiler, rack-lineprof)を入れられていなかったので、ただコードを眺め、問題の特定までにいたらずに、無駄に時間を過ごしてしまいました。
入れる予定だったスロークエリログを確認するための
GitHub - KLab/myprofiler: Sampling profiler for MySQL
がバージョンの指定が間違っていたため、MariaDBをサポートしておらず、動かないというトラブルにも見舞われました。 またの導入に関しては、他のチームも同様に苦しんだようで、しっかりとドキュメントを読んだチームは正しくバージョン指定を行い、使えていたようです。
このタイミングでまず計測ツールの導入を優先し、あたりをつけてからソースコードを読み始めるべきでした。
後日談として、該当の箇所に関して、PRを作成し、ドキュメントを更新しました。
学び:
- 目標は全て行動できるレベルまで落とし込んでおくこと
- 計測して、あたりをつけてから、動き出す。
- わからない時は、ドキュメントと向き合う。REAMDEが完璧ではない可能性を疑う。OSSはみんなで良くしていくもの。
12:00~15:00
昼のメンターとの相談で、「計測を行い、ボトルネックを特定してから、作業すること」を伝えられ、
- スロークエリログ
- rack-lineprof
を入れ、問題のありそうな箇所を特定したので、二人で別々にアプローチでその箇所に取り組みました。
以下のコードの
get_all_tweets
の部分を@takoseget_user_name
を@yakamez
が取り組んでいました。
| 75 get '/' do 0.5ms 3 | 76 @name = get_user_name session[:userId] | 77 if @name.nil? | 78 @flush = session[:flush] ....... | 83 url = URI.parse "#{ISUTOMO_ENDPOINT}/#{@name}" | 84 req = Net::HTTP::Get.new url.path 1.8ms 1 | 85 res = Net::HTTP.start(url.host, url.port) do |http| 1.5ms 1 | 86 http.request req | 87 end 0.2ms 2 | 88 friends = JSON.parse(res.body)['friends'] | 89 | 90 friends_name = {} | 91 @tweets = [] 226.1ms 92 | 92 get_all_tweets(params[:until]).each do |row| | 93 # todo(kame): htmlifyの中身を、redisに載せたらよさそう。 10.6ms 90 | 94 row['html'] = htmlify row['text'] 0.4ms 90 | 95 row['time'] = row['created_at'].strftime '%F %T' 15.1ms 85 | 96 friends_name[row['user_id']] ||= get_user_name row['user_id'] | 97 row['name'] = friends_name[row['user_id']] 0.7ms 140 | 98 @tweets.push row if friends.include? row['name'] | 99 break if @tweets.length == PERPAGE | 100 end ....... | 104 else | 105 2.6ms 1 | 106 erb :index, layout: :layout | 107 end | 108 end
今思えば、最終的には一つの変更で解決すべき問題だったので、近い箇所を変更する際は、ペアプロをした方が良いなと思いました。
またこのタイミングでお互いの問題意識が以下のようにずれていたにも関わらず、すり合わせを行なっていなかったのも良くなかったと感じています。
お互いの認識: - ツイートを全件取ってきているのがよくない - ループの中で、DBを叩いているのがよくない
学び:
- 問題意識は適宜共有(近い時は、特に。)
- 変更してからではなく、問題意識の段階で、ぶつけて、相互にレビューを行う。
15:00-17:00
二人で別々に作業するものの、いまいち進捗をうめず、この時点でも初期スコアからあまり変わらない状態でした。
そこでメンターから近い箇所に取り組むのであれば、一緒に作業をした方が良いと伝えられ、
- 二人で問題について話し合い、お互いの理解のレベルを合わせること(理解や前提知識がずれていると、議論が進みにくい。)
- さらに、解決策を一緒に話し合い、完全にお互いのやることを明確にした上で動き出すこと
という方針で問題に取り組み、さらに各々の作業が終わったら、片方は手を止め、ペアプロしながら、本番に入れていくという形で進めました。
これが非常にうまくいき、最後にスコアを1500から7000前後まで伸ばすことができました 🎉🎉 黄色い線が自分のチームです。1500->2500->5000->7000と1時間強でスコアを伸ばすことができました。
特に、ペアプロをした効果は大きく、お互いに補完しあいながら、素早く変更を加えることができました。
学び:
- 理解をすり合わせることは大切
- ペアプロすごい
結果発表後
結果発表の後も、最後のベンチを走らせた時に大量に出ていたエラーが気になったので、メンター陣に相談しながら、原因を探ると、nginxのtoo many open files
が原因でした。
そこでその問題を修正すると(nginx.confに一行追記)、なんとスコアが7000前後->22000まで一気に上がりました。
nginxのエラーログが/var/log/nginx/error.og
にはかれている事を知らずに、自分の力では原因の特定をする事はできなかったので、エラーを正しく確認できる状態を作っておくこと(エラーが出る場所の知識、正しくローテートする事)が大切だと痛感しました。
学び:
- 計測やエラーは問題解決のための大きなヒントになるので、正しく見れるようにしておく
振り返り・学んだこと
最終的には、 @euglena @okuyama チームがCTOを抑え、優勝でした。おめでとうございます🎉
全体を通して
競技の期間は、1日だけでしたが、準備も含めて、技術的にも、タスクへの取り組み方的にも学びの多い良い機会になりました。
以下は特に自分が重要だと感じたことです。
- 限られた時間の中で、優先度をつけて、取り組む重要性。
- "推測するな、計測せよ" by @tomoasleep
- 失敗は適切に対処すれば、学びになり、次に活かすことができる。
最後に
特に、準備をしてくださった@south37さん、メンターをしてくださった@tomoasleepさん, @munisystemさんありがとうございました。 来年の開催も楽しみにしています!