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

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

初めてのRuby on Railsでユーザ数が大規模なゲームのAPIを高速化した話

自己紹介

はじめまして、大阪工業大学 情報科学部  情報知能学科  3年 伊地知翔也と申します。  
普段は、チームや個人でWEBアプリ開発をしたり画像処理についての研究をしています。  
このサマーインターンに参加する前までは、
私のサーバサイドで経験のある言語は、

  • Golang
  • Python(Flask)

しかありませんでした。
しかしながら、ゲームのサーバサイドに携わってみたいという想いから経験のないRuby on Railsを使用する本インターンを志望しました。

 

事前準備

事前の準備として、Ruby on Railsのチュートリアルを概ね完走しました。

といっても、他の言語でWEB開発の経験あったので、この機能を実現するにはこのメソッドを使えば良いなど、飛ばし飛ばし大まかに学び、準備していました。

インターンの内容

今回、高速化に取り組んだゲームは八月のシンデレラナインというゲームです。

f:id:megumimorishima:20211006171712p:plain

今回取り組んだのは主に2つで、

  • ゲームクライアント側でとある情報の表示、非表示を制御できるようにする

  • とあるAPIの高速化

を行いました。

 

今回は、高速化の方を取り上げたいと思います。

 

高速化前の状況把握

ハチナイのイベントやストーリ選択する際に叩かれる重要なエンドポイントが遅いので高速化しましょうというのが課題です。

 

前任者の調査結果と私の調査により以下2つの問題があることが判明しました。

  1. とあるデータの参照において、N+1問題が発生している。
  2. キャッシュサーバとの通信回数が多すぎる。

 

まず、N+1の解決だ!

 

高速化前では、N+1問題という、Ruby on Railsでやってしまいがちな問題が発生していました。しかしながら、どうやら、何かミスでN+1が起きないように処理できる機構はありましたが、それを利用せずに逐次処理で実装されていました。よって、その機構を利用するよう修正することでこの問題は解消しました。

結果、

f:id:aktsk-ijichi:20210924181955p:plain

N+1の解消結果

こんな感じで、1.1倍の高速化を達成しました。

表の結果は私の開発環境の値であり、本番サーバの速度ではありません。ただし、計算量の評価を行うためには有用です。(倍率で見れば良い)

ところで N+1問題って何?

N+1問題は単純で、とあるユーザIDがあったとして、ユーザに関するデータを

A,B,C,Dというデータの集団(テーブル)に格納するデータベースがあったとします。

各データの集団は

Aのデータの集団では、{ユーザID,何かの情報}が複数格納されています。

B,C,Dも同じように、{ユーザID,何かの情報}が複数格納されていて、

A,B,C,Dには各々別々のデータが入っています。

 

データベースは一般にアプリケーションサーバからは独立した別のコンピュータで動作しているので、アプリケーションサーバからデータベースに対して検索命令を行う必要があります。

 

そうしたときに、ユーザIDだけで、ABCDの各データの集団をまとめ、情報を処理したいときが多々あります。

普通、こうした時は、AのユーザID、BのユーザID、CのユーザID、DのユーザIDで情報を結合して、一挙にして結果を得ますが、高速化前ではこういう仕様にはなっておらず、AからとあるユーザIDの検索を行って、結果を処理し、BからとあるユーザIDの検索、結果の処理....という逐次処理が行われていました。

この逐次処理方式では、一回ごとにデータベースサーバに検索するよう命令するため、

  • データベースとの通信処理
  • データベースの内部処理(SQL命令解釈処理など)

がA,B,C,Dなどのデータ集団の数回=N回行われることとなり、低速の原因となります。

また、逆の場合(ユーザIDがたくさんあって、Aというデータ集団に対してユーザIDごとに検索命令することなど)もあります。

この問題を一般にN+1問題と呼んでいます。

インターン選考ではここが主に聞かれるので注意してください、、、これから受ける方。

これらをA,B ,C,Dに対して、一括で検索するように処理を変えることで対策できます。

また、A,B,C,Dの全データを取得し、データベースサーバではなくて、アプリケーションサーバ側で検索することでも解決できます。(メモリを圧迫する問題がありますが)

 

キャッシュサーバへの問い合わせが多い問題を解消

これはストーリごとの情報をキャッシュサーバに格納していたためです。

ストーリは何十個もあるので、ストーリごとにキャッシュサーバへの問い合わせを行い、逐次処理を行うため、上記のN+1問題に似ている状況が発生します。

解決は簡単で、

  1. 一気に複数のストーリ情報を受け取るよう問い合わせ行う
  2. ストーリを複数に束ねて、情報の格納、問い合わせを行う

方法は2つありますが、結果的に実験してよかった後者を選択し、実装を行いました。

 

f:id:aktsk-ijichi:20210924193300p:plain

後者の実装方法のイメージ

 

 

最終的な速度の計測結果

上のN+1問題の解決とキャッシュサーバ問い合わせ問題の解決を行った実験結果を下記に示します。

 

f:id:aktsk-ijichi:20210924183416p:plain

最終的な高速化の成果

最終的に1.3倍の高速化を達成しました。

この実装を適用することにより、

  • 各アプリケーションサーバ、データベースサーバ、キャッシュサーバの負荷が減少しレスポンスタイムが高速になる
  • さまざまなリソース消費量(メモリ、ネットワーク、CPUなど)が減少する。

ことが期待されます。

 

インターンの感想など

私は今まで趣味でサーバを勉強していたので、業務に携わるのは初めてで、少々不安もあったのですが、メンターの方にどうしてもわからないところがあればビデオ通話で質問するなど親切にしていただき、なんとかこれらの成果を出すことができました。

圧倒的成長...!!!

このゲームの開発プロジェクトのチームの方々にも暖かく見守っていただき、コードのチェック(レビュー)していただくことで、圧倒的成長することができました。

そして、レビューに対して、自分の意見を述べて、レビューした方とコードに関する議論を交わせたことは生涯の糧になると考えています。

 

人生観を変えてくれたサマーインターン

業務でプログラムを書きチームに貢献するという貴重な体験は非常に面白くキャリア観、人生観を揺るがすほどでした。

 

最後に

メンターさん並びにハチナイ開発プロジェクトの皆様、人事の方々、そのほか私に関わってくださった皆様、誠に感謝申し上げます。

まだインターンに参加していない方は、ぜひインターンに参加してみてください!

きっと人生を変える経験ができると思います。