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

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

2022年 7月 サーバサイドエンジニアとして働いてきました!

初めまして、2022年の7/4~7/29の3週間、アカツキゲームスのサーバーサイドインターンに参加させていただきました、白木といいます。この記事では、私が取りくんだこと、学んだこと、参加してよかったことについて書かせていただきたいと思います。

自己紹介

名古屋工業大学大学院工学専攻情報工学系プログラムの修士1年の白木です。普段はTLSの研究、Go言語を用いた開発を行なっています。自宅でLinuxを搭載したサーバでアプリケーションを動かしたりもしています。

今回取り組んだこと

今回のインターンで私が配属させていただいたプロジェクトは、Ruby on Railsによるゲームのサーバチームでした。インターン開始前の事前面談で私の経験してみたいことをふまえ、「大規模システムの負荷改善」のタスクに取り組むことになりました。以下がインターン期間に取り組んだことです。

  • 機能改修中のAPIの高速化(N+1問題の改善)
  • 負荷試験
  • ログのDBのidがオーバーフローする前に検知し、slackに通知を投げるシステムの開発

機能改修中のAPIの高速化(N+1問題の改善)

どういったN+1問題が発生していたか

実際にN+1問題が発生したAPIのログを見ると、下記図のような現象が起こっていることが判明しました。

これは、example_func関数の内部のusers.selectでusersテーブルからuserのデータ取得をを行うクエリを1回発行し、そして、ループでn_func関数を呼び、取得したユーザデータと紐付いたarticlesを得るクエリをN回発行する状態示した図です。

どのように解決したか

下記図のような、ループ開始前に、userと紐付いたarticlesをincludeメソッドを用いて予め取得しておくコードを書き、冗長なクエリを除去し、問題を解決しました。

RailsのActiveRecordにはEager loadingと呼ばれる機能があります。これは、予め、アソシエーションしているテーブルのデータをメモリ上にキャッシュすることができます。そのため、ループ開始前に、ループ内部で将来的に使われるアソシエーションをEager loadingすることにより、クエリの発行回数を減らすことができます。今回は、このEager loadingを使いました。

評価結果

この評価結果は、負荷試験環境で計測したものであり、実測値ではありません。クエリの発行回数は、N+1問題解消時にはN+1問題発生時の0.4倍ほどに抑えられていることがわかります。この変更によって、大量の同時リクエスト発生時にも、データベースへの負荷を最小限にとどめることができます。

N+1問題発生時 N+1問題解消時
クエリの総呼び出し回数 279 108
苦労したこと & 学んだこと

ソースコードとログの規模が大きく、処理の中で様々な関数が呼ばれることから、N+1問題の発生している処理が書かれている箇所を探すのに苦労しました。また、メンターのyasuさんとのモブプロを通じたRailsのデバッグ方法・ログの解析方法、N+1問題の対策を講じていたが、コードが複雑になっているが故に一部解決できていなかったのを目の当たりにし、エンジニアが実装時にN+1問題の発生に気付きにくいということも学ぶことができました。

負荷試験

今回、私がN+1問題の解決に取り組んでいたのは新しいバージョンの機能改修の箇所です。実は、この改修はあるAPIの負荷を減らし、パフォーマンスを向上させるものでした。そのため、その機能改修によってAPIが前のバージョンと比べ、適切に直っている、また、新たに負荷懸念となるようなAPIがないか調査をするために、負荷試験を行いました。

調査結果

Locustというツールで負荷試験シナリオを記述し、実施しました。クライアント側から90分間、負荷を与え、その結果をNew Relic、Cloudwatchのメトリクスを見ることで、調査をしました。機能改修箇所は、意図されたパフォーマンスを示し、新たに負荷懸念となるようなAPIは見つかりませんでした。

苦労したこと & 学んだこと

私が、負荷試験を実施したことがなく、負荷試験の勉強をするところから始めました。前バージョンの負荷試験の結果、メンターのyasuさんの助力もあり、なんとか苦労しながらも理解することができました。また、私がAWSの使用経験がなく、AWSが提供するサービスの知識不足により、色々とハマったりもしました。しかし、苦労はしましたが、非常に良い経験ができたと感じています。学生のポケットマネーでは、クラウドサービスの学習に対して投資することが困難な場合が多いです。そのため、AWSのサービスを触ることができたこと自体にも大きな価値があると思いました。

ログのDBのidがオーバーフローする前に検知し、slackに通知を投げるシステムの開発

課題背景

APIを処理するアプリケーションサーバでは、ログをとっています。このログは、APIの解析、障害時の原因究明をするために用いられており、重要な役割を担っています。このログをDBで管理しているのですが、以前、このDBに問題が発生しました。int、unsigned int型でauto increment属性を付与しているテーブルで、idがオーバーフローをし、ログをDBに保存することができなくなるという問題が発生しました。そのため、エンジニアがこの問題に事前に気が付けるように「ログのDBのidがオーバーフローする前に検知し、slackに通知を投げるシステム」の開発が必要になりました。

どのように解決したか

本番環境と接続しているDBで、先述のオーバーフローを検知するrakeタスクは既にあります。そのため、この実装を利用し、ログDBのオーバーフロー検知を実現させます。auto incrementの値を取得して、閾値を超えているかの判定処理を、関数化してログDBにも適用させました。時間が足りず、テストの修正ができませんでした。

苦労したこと & 学んだこと

ログDBをどのように扱うかについて苦労しました。ログDBはRailsのAPIサーバと接続するものでないため、どのように接続させれば良いのかが全くわかりませんでした。しかし、過去にログDBに接続を行う処理が書かれていたため、それを参考にして、解決しました。また、既存の処理を関数化させる際にも、多くの問題を起こしてしまいました。処理を抽象化させる際に、綺麗にさせることを意識していなかったため、非常に汚いソースコードを生成してしました。そのことをレビューで指摘されたため、今後、しっかりと目を向けていきたいと思いました。 また、閾値を決める際に、本番環境のデータの集計するクエリを発行しました。この結果を見た際に、私の想像を遥かに超える数が集計され、インターン期間に携わってきたシステムの規模の大きさに驚きました。

感想

このインターンで当初、取り組みたかった大規模システムに携われることができ、非常に楽しい時間を過ごすことができました。技術的な学びは先述の取り組みの中で多く得られました。手を動かした箇所だけでなく、データベースのシャーディングさせて、アプリケーションを動かしているのを見て、大規模システムを支える技術を見ることもできました。その他の学びとして、アカツキゲームスが採用しているスクラム開発も経験することができました。デイリースクラム、スプリントレビュー、スプリント・レトロスペクティブ、スプリント・プランニングに参加することで、よりスクラム開発への理解が膨らみました。そして、実環境で運用される機能開発をインターンで行うことができ、いい意味で緊張感のある開発を行うことができました。

謝辞

最後に、メンターのyasuさん、そして、技術的なサポート、ブログ執筆のお手伝いをしてくださったプロジェクト関係の皆様、ありがとうございました!