Akatsuki Hackers Lab | 株式会社アカツキ(Akatsuki Inc.)

Akatsuki Hackers Labは株式会社アカツキが運営しています。

アカツキのサーバーサイドインターンに参加しました

初めまして、2021年の9/6 ~ 9/24の3週間、アカツキのサーバーサイドインターンに参加させていただきました、竹下といいます。この記事では、私が取りくんだこと、学んだことについて書かせていただきたいと思います。

自己紹介

慶應義塾大学 総合政策学部の学部3年生で、普段は個人や仲間内、アルバイトなどでWebサービス開発を行っています。使う言語などに強いこだわりがあるわけではないですが、Rustで開発をすることがひそかなマイブームです。 過去には趣味でオレオレWAFを実装したりしていました。

今回取り組んだこと

今回のインターンで私が配属させていただいたプロジェクトは、Ruby on Railsによるゲームのサーバーチームでした。その中で、アップデート後に処理が重くなってしまったエンドポイントについて、速度改善をするというタスクにチャレンジしました。

エンドポイントの役割としては、スマホゲームではおなじみの「ミッション機能(一定の条件をクリアすると報酬が貰える)」の一部です。デイリーミッションやコンプリート系のミッションなどといった、いつのまにか達成しているようなミッションを含めてミッション進捗を確認するために叩かれます。

課題だった点

まず、実際に稼働中のサーバーを監視しているNew Relicのダッシュボードから発生しているトランザクションを見てみました。すると、他がリクエスト当り平均1 ~ 10回程度だったのに対し、一箇所だけ平均90回近く発生しているところがあり、このボトルネックを解消することにしました。

該当部分のソースコードを確認したところ、以下のようなモデルの構成になっていました。

f:id:taigen-takeshita:20210923234059p:plain
クラス図

該当部分で行っていたこととしては、ユーザーのミッションデータごとに対象クエストを完了した数をカウントして更新するという処理でした。この際にRailsのActive Recordのcountメソッドを使用していたため、ミッションのユーザーデータの数だけカウントクエリが呼びだされてDBアクセスが増えていました。

やったこと

毎度クエリすることを防いであげたかったので、事前に必要なデータをすべて取得しておき、カウントはRubyのEnumerable#countメソッドを使って行う方針で改善を行うことにしました。

元々パラメータをHashで渡してあげるインターフェースが進捗を進めるメソッドに用意されていたので、共通部分で事前取得を実行し、パラメータに事前に取得したデータを流してあげることにしました。

結果

ある手順でAPIを叩き、該当する箇所でレスポンス全体にかかった時間と、DBアクセスにかかった時間を計測し、比較しました。また、ユーザーがミッションをすべて達成した状態についても計測しました。(ユーザーの状態を初期状態から破壊的に遷移させていくため、対象となるミッションの数が変動してしまうことから、前提をそろえるために手順を決めています)

結果は以下の表のようになりました。

f:id:taigen-takeshita:20210924020427p:plain
計測結果表

苦労した点・学んだこと

ソースコードの読み込み

僕はそんなに多くのゲームを遊ぶ方ではないので、ゲームのデータ処理の内、どんなところをサーバーでやっているのかといったことはあまり想像したことがありませんでしたが、実際に見てみると、特にミッション部分では「クエストを○個クリアしたら〜」「フレンドが○人になったら〜」「他のミッションを○個クリアしたら〜」などのように複雑なデータ処理がされています。更に、ゲームを運用していくにつれてミッションは多様になっていきますから、それにあわせてモデルが増えていくことになります。

関わらせていただいたプロダクトは長年運用されているものでしたので、コード量も多く、把握するので一苦労でした。さらに、一通り実装しきれたと思っていても把握していなかった部分のテストに落ちてしまうということもありました(T_T)

コード量が多く複雑なシステムになっていけばいくほど、そのコードの正常系、異常系の動作を保証するためのテストの役割は大きいなと感じました。大きな価値を提供するプロダクトを作る上では、テストコードをしっかり書けるように勉強しないといけないなと感じました。

高速化と可読性の両立

無駄な処理を省くために処理を共通化させる一方、共通部分にクラス特有の処理がある状態は避けてクラスにさせるようにするということを同時にやらなければなりませんでした。普段の小さなアプリケーションの開発では当り前のようにクラスの処理の責任範囲を意識していましたが、同時に共通化、高速化といった観点が加わるだけでこんなにも難しくなるんだなと感じました。

ユーザー数が多くなるにつれて小さな確率で発生するエラーも広範囲に作用するようになったり、小さな速度の低下が機会損失につながっていくというお話しを伺いました。やはり開発規模が大きくなるとそれだけ同時に考えるべきことが増えるのだなと感じました。

高速化と可読性を両立するのは難しい作業でしたが、決して相反するものではなく、ロジックを整理しながら順序立てて作業することが大事だと感じました。また、意外と解決策はシンプルな方法だったりするので、日々触れている技術は深く知る努力をしたいなと思いました。(特に今回はRubyのビルドイン関数にお世話になりました)

その他、インターンで経験したこと

社員の方とのオンラインランチ・面談

人事の方々やサーバーチームの方々、それから僕の希望で運用の方とオンラインランチの時間をとらせていただきました。趣味の話やチームの過去の話で盛りあがったのは勿論、運用の方とは今まで知らなかった運用の仕事の内容などを中心に、普段は聞けない貴重な話を聞かせていただきました。

また、VP of Engineeringの湯前さんと面談のお時間を頂き、エンジニア組織の改善を通してプロダクトの質を引き上げた過去や、ユーザーを獲得したプロダクトが次に追求するべき価値はなにかといったことを聞かせていただきました。僕自身Webサービスを運営した経験から、成熟したプロダクトが次にどうするべきなのかという問いを持っていたのですが、大きなユーザーを抱えるということは、それだけ小さな取りこぼしもないシステム開発・運用が求められるという考え方を伺い、サービスを運営する上での価値観として大切にしたいなと感じました。

ちなみにですが、しっかり朝から働いたこともあり、お昼にこのような時間を設けていただいたおかげで、久しぶりに一日三食摂りましたw

スクラム開発

スクラム開発という体制では何をするのか、何がいいのかといったことは知識としては知っていましたが、余り規模の大きいプロジェクトに関わった経験がありませんでした。今回は初めてスクラムに入らせていただき、3週間の中でスクラムイベントにも色々と参加させていただきました。

実際にやってみて知識から実践に変った経験がとても学びになりました。また、上述の湯前さんとの面談の際にも、スクラムによってメンバー同士のコミュニケーション量をデザインすることで、エンジニアが開発に集中し、アウトプットを増やすことができたという経験談ベースでのお話を伺い、とても理解が深まりました。

最後に

メンターの方、プロジェクトやチームの皆さん、一緒にお話頂いた方、人事の方々、アカツキのみなさんのお陰で大変多くの学びを得ることができました。 3週間という短い時間ではありましたが、本当にありがとうございました!