ServerSonic2018に参加しました!

表題の通り、先日実施されたServerSonicに参加しました〜

hackerslab.aktsk.jp

公式レポートの発表のあとにブログかいてしまった。。。もっと早く出すつもりだったのに......

2018年8月25日(土)〜 26日(日)にて開催された、Akatsukiのサマーインターンシップ SERVERSONIC2018に参加しました!

とても嬉しいことに優勝し、景品としてPS4 proをいただきました!!!!!! 廃れない程度にいっぱい遊びたいとおもいます!!w

..最後のスコア載せとこ..

TL;DR

8/25~8/26の2日間Akatsuki社のサマーインターンシップServerSinicに参加させていただきました!!!

インターンシップに参加し、

自分の足りないものや、自分の中での好きなことといったものに出会えたのかなと思います。 そこでそれら言語化したく思いブログに書いてみようと思いました。

2人1組のペアになって 八月のシンデレラナイン のプロダクトコード(バグ埋め込み済?)を改善するというインターンシップでした! ISUCONとかに近いのかな。自分はISUCONでたことないのでよくわからないですが

以下のようなことをつらつらと書いていきたいと思います。

  • 簡単に概要
  • 前半の内容
  • 後半の内容
  • どんなことしたの
  • なにが大変だったの
  • どんなこと頑張ったの
  • 知見は?
  • 得られたもの

簡単に概要

以下インターンシップ募集ページより引用

大量トラフィックを処理することが求められるゲームサーバーアプリケーションの性能改善方法を学び、 公開中の『八月のシンデレラナイン』プロダクトコードの性能改善にチーム対抗のコンテスト形式で取り組んでいただきます。

実際に高負荷環境での性能改善に取り組んでいるエンジニアからの講義や、 社内のエンジニアとカジュアルに交流できる懇親会も開かれます。

優れた改善ができた場合は実際のプロダクトに取り入れる可能性もあり、 優勝チームには賞品が贈られます。

ふるってご参加ください。

> 詳細はこちら:https://aktsk.jp/recruit/new_recruit2020/serversonic2018/

というようなものです。 一言で言えば, 八月のシンデレラナインにCommitしようぜ!!!!!!! というような趣旨。 学生向けにわざと n + 1問題や、cacheをあえてしていなかったり、処理が遅くなるような いじめ 工夫がされているプロダクトコードに対していろいろ頑張っていくコンテストがメインのインターンシップでした!

ゲームAPIソースコードがみれるというだけでも学生にとっては最高な環境にも関わらず。 昨年から始まった?このインターンシップは商品が とても豪華 で今年の優勝者には n社のSw***h, であったり、S社の PS*であったり, みんな大好き 米国Ki***is社のエルゴノミクスキーボードといったものが贈呈される (らしい) (来年はわからない)きっとあるよ!! といった学生が燃え上がる景品が用意されてました!!!!!!!!!!!!!!! (最高!w )

ちなみに八月のシンデレラナインはガチャでSSRが出やすいアプリ(体感)なので個人的に幸せ指数があがるアプリです!!w (試行回数が多いだけではないはず) 余談ですが初日終わった後に10連ガチャ回したらssrが二枚出ました!w。


実際にインターンシップの内容は以下にまとめていきたいと思います。 ざっくりまとめると講義コンテストの二部だてになっていました

前半講義

  • 1つめの講義 ```
  • 開発環境構築
  • k8s入門
  • ゲームサーバのイロハ
  • 負荷削減講座 ```

  • 2つのめ講義 ```

    • プロファイラを使った計測の方法
    • Gemライブラリの最適化の方法
    • (余談)大規模Railsアプリケーションの最適化事例 ```

講義は前半と後半で、インフラ周りの話メインの午前と、後半はRubyでの高速化をする方法を教えていただきました。 (どこまでかいていいんだろう。わからないので簡潔に書く)


前半の講義では主に開発環境とCI環境や実務でのインフラ構成などの話とざっくりとチューニングに関するアレコレを話していただけました。

今回の環境は k6という負荷テストツールをつかってRailsアプリケーションに対して負荷をかけるものです。 (https://qiita.com/szk3/items/c1172ef3d182d7fe6868)

k8sのコンテナの中にk6の負荷テストがあるpodがあり、それとは別にRailsアプリケーションのpodがあり、Railsコンテナ、Mysqlのコンテナ, Memcachedのコンテナ, Redisのコンテナが用意されているものです。 ローカル環境はDocker for Mack8sを起動し, CIはGKEを使っているそうです。 docker使ってるのに開発環境の構築難しいの本末転倒のような気がしてしまった。仕方ないw 講義担当者が今回のインターン用の環境などを用意してくださっていたようで、 この環境で楽しんで遊んでください。というようなメッセージがありましたwww

普段RedisやMemcacheといったものを独学の環境で実装したことがないため mysqlとの違い、それぞれの用途わけやメリット・デメリットを聞きました。 個人的にRedisとMemcacheって同時に使わないと勝手に思い込んでいたので別の用途として共存するという選択肢を改めて知れました。


後半の講義は rubyのGem ( gem 'rack-lineprof', github: 'kainosnoema/rack-lineprof' ) ( https://k0kubun.hatenablog.com/entry/2014/09/22/020942 )

Sinatraアプリのプロファイリングにはrack-lineprofが便利

とかいてありますが、Railsでもちゃんと使えました! lineprofを使うと1つ1つのメソッドや処理(Enumerable#map)などの実行速度を把握することができ、 処理速度を読み込み遅いやつがlogが赤色や黄色になる便利なgemでした。

また、Gemライブラリの最適化では、Rubyオープンクラスの拡張、モンキーパッチの当て方の話 ( https://qiita.com/kidach1/items/b1672f1c16e2d15f2d9c ) 参考記事さがしてたらアカツキの記事というw

rubyのメソッドの読み込み順序や、クラスのよみこみ順序といったメタプログラミング入門を話していただけました (なんとなくわかったような気がしたような気がする) 最近efecteve rubyを読みはじめたのでかろうじて理解ができたような気がするのですが実際にオープンクラスの拡張などを実際にしていないので勉強しようと思います。

また実際の業務の際にパフォーマンスチューニングをする際の心得や、注意事項。 どんなことを注視してログを見ているのか。このツールが便利、等の話をしていただけました!

闇雲にソースコード変えるくらいなら実測値から原因を絞り出す、とのこと。 本当に遅い部分というのはとある処理のなかのボトルネックを潰せば結構早くなるとのことでした。 (ボトルネックを意識することの重要さをかなり語っていた。。。)

のような講義を初日の夕方くらいまでしていました。


後半の内容(ここからコンテスト)

後半はインターンの募集要項にあるように (以下抜粋) 公開中の『八月のシンデレラナイン』プロダクトコードの性能改善にチーム対抗のコンテスト形式で取り組んでいただきます。 というコンテストです!

内容はいたってシンプルでGithubにPushしたらCIが回って点数を各チームのSlackチャンネルに投げてくれるというもの。 初期値が33000くらいで始まりこの数値を締め切りまでに最速にしたら勝ちというものです。

余談ですが、コンテスト中にCIのバグを踏み抜いた(?)チームがあるらしく 途中でAPIのテストが追加されましたw。

自分が変更したコードも見事↑によってコケたので大変だった。w

どんなことしたの(改善手法について簡単に)

自分はまずLineProfの内容を精査し明らかにボトルネックっぽいものをGithubISSUEにしてました。 チームメンバ(以下K氏)にはサーバーログを見てもらい N + 1レンダリングに時間かかっている箇所、ボトルネックっぽい場所をログ実際にテスト走らせながら確認するなどの作業をコンテスト開始から1時間?くらいしてました。

本来であればLineProfの結果を最後まで確認し精査をちゃんとするべきであるのに、時間に追われていることもあり、 3/5くらいのエンドポイントくらいしかちゃんと見ておらず、その先は 目グレップ していましたw。(単純にこの段階でログの追い方が下手だった。。。。。)

とりあえず初日はソースコードの概要把握 (これがしんどい) とDBのindex調査、 n + 1の改善をメインに担当と、 K氏がRails経験が浅かったので彼が実装したい内容(SQL)をRailsに翻訳することをしていました。 自分のRails力がなさすぎて一緒にググったりしてしまったり。。。(tsurai)

初日はそれぞれ一つづつくらいのPRを投げて終わった気がする。

帰り道や、自宅で1日を振り返っているとLineProfの見方がおかしかったんじゃないかと思い始めたので翌朝出社と同時に改めて調べ直しました。

今までは全体的に目グレップしていたのですが、ちゃんと各エンドポイントごとにログファイルを出し分けて その中で問題になってる箇所を目grepして APIのエンドポイントとどのメソッドが遅いのか問題点を探し出しました。 また同時にbulletのログも確認するようにし同様にメソッドレベルで問題点をまとめました。

このファイルを元にあらためてぼんやりながめていると
・あるAPIn + 1しまくっていることに気づいたり、
レンダリング速度が異常におそいAPIあったり。
・なんかしらんけどSQL死ぬほど吐いてたり。
といったやばい状況だということに改めて気づいてしまいました。

自分が n + 1対応と、レンダリング周りを調査しつつ、
SQLが爆発している箇所をk氏にお任せしました。(大変そうだった箇所をお願いしてしまった。)

ただ、分担してから少しすると、k氏の担当箇所が想像以上に大変そうかつ、Railsの手法をしってないと難しそうということにきづいたのでモブプロっぽい感じで二人モニター見ながら作業をするようにしてました。
(多分インターン生ぼくだけモニター使ってなかったw)基本的に役割分担してやってるのが多かったのかな。

SQLが爆発してたのは
- 単純に n + 1の countクエリを投げていたってこと
- メモ化をしておらず毎回クエリをぶん投げているため死ぬほど重くなっていたこと
この2点が原因でした。 (記憶の中では)

このPRだけで結構点数がのびたのでチャレンジしてくれたk氏には感謝です。。。。。

大変だったPRは↑がメインだった記憶があります。 あとは
ひたすら n + 1対応の includesをしたり(めっちゃ書いたw)
includesをいろいろしすぎてうごかないから preloadとearger_loadでごにょごにょしていい感じにしたりした。
キャッシュできそうな変数はひたすらメモ化した。
Enumerable#selectはActiveRecordのWhereに変えたりしてました。

Jbuilderのテンプレートのよみこみが遅かったので eachしてpartialを読み込んでいる箇所を直接殴り書きにしたり(若干早くなった)

ruby before: json.samples partial: 'samples/sample', as: :sample after: samples.each do |sample| sample.*** end

以下記事を参考に無理やりgsubしたり(ほとんど変わらずw)

https://blog.freedom-man.com/jbuilder-n1render/

records = render partial: 'api/hoge/record', collection: @records json.hoge JSON.load("[#{records.gsub('}{', '},{')}]")

のようなことやpartialの引数はcollection使うと良いとか書いてあったので使ったりしたり。

みたいな地道なチューニングをひたすらしていました。


最後に結果発表と同時に 田中さん(CTO)から、順位に限らず参加者からの優れたコミットの発表(優秀コード部門、ネタ部門など)があり、そのコードを書いた参加者が意図を話して、2日間を振り返りました。

自分が頑張った箇所などが発表されると嬉しいですし、周りのチームとのアプローチの違いなどもしれてとても面白かったです。

なにが大変だったの

げーむAPIならではのシャーディングとか。
そもそも速度改善ってあまり意識したことがないのでボトルネックさがすのがつらい。
複雑なDB構成を確認するの大変w
Railsの書き方をすると簡単にデータ取得できるのに n + 1になったりw
あとは単純に1日2日で理解が追いつくような規模のアプリではなかったので今改善しようとしている箇所の影響範囲などを調べるのが難しかったです。

どんなこと頑張ったの

やったこととしては前述した通り

  • ひたすら n + 1対応の includesをしたり(めっちゃ書いたw) includesをいろいろしすぎてうごかないから preloadとearger_loadでごにょごにょしていい感じにしたりした。
  • キャッシュできそうな変数はひたすらメモ化した。

    といったようなチューニングにおける基礎的な部分とボトルネックを探すことを頑張っていました。

特に初日のログの追い方がわるかったこともあり2日目は
- それぞれのエンドポイントごとのログに分ける。
- おそくなってる部分を確認し、そのメソッドに問題があるのか前後処理に問題があるのか切り分ける。

→その上で改善するべきかどうかを判断するなどをしていました。

ゲームロジックに関わってくる処理は比較的重い処理になりがちな印象をうけ、 できる限り複雑なロジックには触れず(影響範囲が測れないため)、ピンポイントで改善できそうな箇所を探し、改善をしていました。

感じたことなど

チューニングは難しそう。という先入観がとても強かったのですが、実際にやってみると いまの自分の知識であってもある程度は可能なんだなということをしれました。 まだまだ改善できるような箇所はあったと思うのですがどこをどう改善すればよいのか見当が付いていないため、仮にあと1日あったとしてもスコア的にはあまり変わらないのかな。。とか思っていたり。 圧倒的なRails力・Ruby力不足です。

また、想像以上にボトルネックって探しにくいんだなというのが率直な感想でした。 newrelicのような可視化ツールの偉大さを改めて感じつつも、ログを見ることの重要さをも感じました。 基本的な情報はすべてログにあるとおもうのでそこから必要なものを知るのは事前知識とログ解析力と目グレップ力なのかなと。 ログの中から必要な情報を探しだすことの大変さとそれの重大さを改めて知るきっかけになったのはとてもよかったです。

まだまだ本質的な部分はわかっていないのですが、基本的なチューニングの先は Railsのベストプラクティスに従いつつもRubyでのメタプログラミング的なことをしてアプリケーションへの最適化をすることが最適解なのかな。というのが個人的な感想です。 基本的な思想はRailsWayに従いModelのConcernなどを利用しつつもクラスの拡張などを利用し既存メソッドを書き換えて処理を軽くするといったことができるのかな。。?

単純に自分自身のRailsへの理解であったり、Rubyへの理解の甘さが今回のスコアがもっと伸びなかった原因なのかなと思っています。 もう少しRubyの深い部分について学んで行こうとおもいました。

最後に

githubへのpushをトリガーにCIが回ることで自分の作業がプラス方面になっているのかマイナス方面になっているのかがすぐにわかるシステムでとてもやりやすかったです。 点数という数値化されたものだったのでそれが1000あがるだけでよっしゃ!!となるのでそれがとてもモチベーションとなっていました。

とても楽しかった!!

このブログは誰宛のものなのだろう。 自分の感想なのか..

インターン終わってから一週間たってしまい気づいたら9月に。。なっていた。早い。 自分がやってたことなどの記憶が曖昧になりつつこの記事を書いてしまっていてなんともいえない気持ちになったw

今回学んだことを活かすためにisucon2018に申し込んだので次はアプリケーションレイヤだけでなくミドルウェア周りも調査できるのでいろいろ頑張りたいと思います。