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

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

Akatsuki Games Summer Internship 2021 で大規模サービスの開発に参加しました

はじめに

はじめまして、長谷川と申します。

2021/10/4 から 2021/10/29 の4週間、株式会社アカツキゲームスの『八月のシンデレラナイン』(以後ハチナイ)の開発を行うチームにサーバーサイドエンジニアとして参加させていただきました。

普段は趣味やアルバイトでWebとモバイルのアプリ開発をしたり、大学院で交通流や人流の解析の研究をしています。

 

インターン参加まで

アカツキゲームスのことはサポーターズの逆求人イベントで知ったのですが、アカツキゲームスのエンジニアはとにかく楽しそうにものづくりしてるよ〜と人事の方が話されており、興味を持ちました。

選考では主に今までの経験について聞かれ、特に友人と作ったプロダクトの開発をする時に考えていたことについて話したのがウケが良かった気がします。Railsを触るのはかなり久しぶり(&そもそも経験不足)だったので、参加が決定してから他の企業でRailsを使う短期インターンやハッカソンに参加しながら、事前準備をしてインターンに臨みました。

 

取り組んだこと

 

今回のインターンには(授業や研究で途中抜けしながら)4週間参加させていただき、主に以下の4つのタスクに取り組みました。

・管理者ツールの機能追加(Day2~Day3)

・マスターデータのバリデーション

・新規機能開発

・チャプター情報一覧取得APIのレスポンスを高速化

 

上の3つまでは軽く触れる程度にして、今回のインターン期間の約半分をかけて取り組んだ「チャプター情報一覧取得APIの高速化」について詳しく書いていこうと思います。

管理者画面の機能追加

この管理者ツールでアイテムの付与をできるようにする、というのが一つ目のタスクでした。管理者ツールは主に非エンジニアの職能の方が使うのですが、通常ユーザーに対してアイテムを付与するというのは強力な機能なので、手違いが起きないように運用フローを整えてもらう必要がありました。この部分でエンジニア以外の方とコミュニケーションを取ったのですが、こういう体験は技術とはあまり関係がないながら業務以外では体験しづらいのでいい経験になったなと思います。

 

マスターデータのバリデーション

(ハチナイにおける)マスターデータとは、選手の能力やショップの交換レートなどのユーザーの操作によって変化しないデータのことです。これと対になる概念として、獲得した経験値や所持しているアイテムなどのユーザーデータがあります。

ハチナイでは、このマスターデータに対して不正な値をもった選手や、イベントが発生しないようにバリデーションをかけています。不具合を未然に防ぐだけでなく発生した不具合が再度発生しないようにするのもマスターデータのバリデーションの役割です。今回のタスクでは過去に起こった不具合の原因を調査し、その不具合が起きないようにするためにバリデーションを追加しました。不具合の原因の調査は、1つ目のタスクと同じようにエンジニア以外の方とコミュニケーションをとりながら進めていったのですが、コードだけからは読み取れない運用ルールなどを知ることができて、コミュニケーションの大切さを学ぶことができました。

 

新規機能開発

2つ目のタスクが終わった時に「新規機能開発に興味ありますか?」とメンターの方に聞かれ「楽しそう!やりたいです!」と答えると、メンターの方が実際に業務で進めている新規機能の開発の一部分を任せてもらえることになりました。(新規機能については、公開されていない情報をかなり含むので詳細に触れることはできませんが、)テーブル定義、実装方針検討、エンドポイントの決定、API作成、テストを書く、など新規機能開発の一連のフローを経験できました。4つのタスクの中では最もユーザーにとってわかりやすいタスクだったので、自分が開発に参加した機能が実際にリリースされるのが楽しみです。

 

チャプター情報一覧取得APIのレスポンスを高速化

これが一番時間をかけて頑張った、かつ成果を出せたと思っているタスクです。このAPIはメイン画面から、試合ボタンを押した時などに叩かれるチャプター情報の一覧を返却します。実際のゲーム画面で言うと、例えば

 

f:id:t_hase_aktsk:20211101083039p:plain

から試合ボタンを押して

f:id:t_hase_aktsk:20211101083045p:plain

へ遷移するときに叩かれているAPIです。確かに言われてみれば、プレイしていても結構遅い。と言うわけで、まずは速度計測から始めました。僕の環境でのレスポンスタイムの計測結果が以下でした。

Completed 200 OK in 3957ms (Views: 2452.5ms | ActiveRecord: 251.4ms)

約4000ms。。。!早くしなくちゃ!と思い早速改善に取り組みました。

ViewsとActiveRecord以外の部分で1300msくらいかかってるのは流石に遅すぎない。。?Controller層の処理が怪しそうと思いそのあたりの処理の速度を測定するとビンゴ!チャプターに関連するアソシエーションを取得するpreloadが大量に連なっている所がとても重くなっていました。(僕はpreloadはN+1を解決するためにするんだから早くするためのモンだと思ってましたが、ハチナイくらいのサービスになるとレコードの量がすごいので、気軽にpreloadすると重くなってしまうんだな。。)その中でも特に遅い部分を調査していると、クリアスターの有無を計算する所が重くなっているようでした。ここはマスターデータだけで計算できる部分だったのでキャッシュに持たせるのが有効そうだと考え、早速実装してみると

Completed 200 OK in 2659ms (Views: 1866.2ms | ActiveRecord: 229.5ms)

といきなり30%ほど改善できました!

この時点まだインターンの残り期間が1週間あったので、さらに高速化したい!とさらに調査を進めました。

 

というわけで、遅い処理を特定するためにログを見ていると怪しい箇所をいくつか発見。bulletでは検知できない明示的でないN+1が発生しています。

ここのN+1を解消すると

Completed 200 OK in 2330ms (Views: 1739.6ms | ActiveRecord: 73.0ms)

と少し早くなりました。

また、遅い処理で改善できそうな部分を見ていくと、キャッシュをwriteした後は使われないアソシエーションがpreloadされているのを発見したのでそこのpreloadを削除。ここでインターンの残り期間がほとんどなくなっていたので、PRを短時間できちんと出せるような細々した改善点を探すためにログが汚くなっているところを見ていくと、同じようなN+1や無駄なキャッシュのreadを発見しました。これらもなんとか期間内に解消して少しだけ速度改善。これらは速度改善という点で見ればあまり結果は出なかったですが、ログは綺麗な方が気持ちが良いので達成感はありました。

最終的には

Completed 200 OK in 1871ms (Views: 1554.7ms | ActiveRecord: 28.8ms)

というレスポンスタイムになり、最初の4000msと比べると50パーセント以上の改善になりました!

 

業務を終えて&感想

成果発表

これにてインターンとしての業務は終わり、最後に成果発表会を行いました。

成果発表会では今までお世話になった方々からFBをいただけるのですが、直接は話していなくてもslackで僕のtimesチャンネルを見て、気にかけてくださっていた社員さんたちからも温かいコメントをいただいて、思っていたよりたくさんの方に見守られてこのインターンを走り切れたのだなと温かい気持ちになりました。また、「速度改善というタスクは新規機能開発などと違い、ユーザーに分かりやすいFBをもらえるようなものではないけど、サービスにとってすごく価値があることだからチームの私たちからたくさん感謝を伝えたい」というようなことを言っていただき、嬉しかったし、仕事の価値を認め合える空気があるというのがすごくいい環境だなと思いました。

感想

このインターンでは、人生で初めて大規模な開発に関われたり、色んな職能の方とランチしたりミーティングしたりなど、すごく良い経験をすることができたなと思います。またコードを書くばかりがエンジニアの仕事ではなく、エンジニア以外の職能の方とのコミュニケーション、仕様検討、原因調査などもエンジニアの大切な仕事であることを学べたのはすごく良かったなと思います。

また、メンターの方は自走を促すけど、困ったことがあればすぐに相談に乗ってくださり、僕にとって安心して挑戦できる&成長できる環境を整えてくださいました。この場を借りて改めて感謝を伝えたいです。本当にありがとうございました!

 

 

 

アカツキゲームスのインターンでAPI高速化と新規機能開発に取り組みました

こんにちは! 2021/8/10〜2021/8/27の3週間にわたってアカツキゲームスのサマーインターンに参加させていただきました、細谷と申します。

今回のインターンで取り組んだことや学んだことについてまとめます。

自己紹介

東北大学大学院 情報科学研究科に在学中の修士1年です。研究ではコンピュータビジョンや機械学習を扱っており、趣味でWebフロントエンド・バックエンドを幅広く触っています。

よく書く言語はTypeScript、Python、Golangで、最近は新規Webサービスの開発をしている関係でソフトウェア設計などの学習をしています。

インターンで取り組んだこと

今回私は、『八月のシンデレラナイン』(以後ハチナイ)というゲームの開発チームにサーバーサイドエンジニアとして参加しました。

ハチナイのサーバーサイドは主にRuby on Railsを用いて開発されています。私はRailsでの開発経験がなく、ある程度の予習が必要とされました。そのため、経験があり言語として近い性質を持つPythonとの共通点を捉えつつ、ActiveRecordなどのRails特有の知識を一通り学習してからインターンに臨みました。

APIの高速化

最初に取り組んだのは、「購買部」内にあるアイテム変換機能に関するタスクでした。ここではナインスターや各種変換コインを用いてアイテムを入手することができますが、その際に発生する「現在販売中のアイテム」を取得する処理が重く、アイテム変換への画面遷移が少し遅いという問題がありました。

この処理は具体的には、「どのアイテムを用いて交換できるか(カテゴリ)」という情報と、「各アイテムを何と交換できるか」という情報を取得する2種類のAPIが用いられています。そのため、N個のカテゴリ全てに対してそれぞれで交換できるアイテムを取得すると、全体ではN+1回のAPI呼び出しが行われることになり、無駄が多い状態になってしまっていました。

設計としてはいわゆるRESTfulなAPIでしたが、これらで取得できる情報を1つにまとめることで端末とのやりとりやデータベースへのアクセス回数が減り処理が高速になると考えられます。そこで、既存のAPIとは別に、「アイテム交換で現在販売中のアイテム」全てを取得できるAPIを作成しました。

改善の結果

呼び出す必要のあるAPIを1つにまとめた結果、負荷試験環境において約3.5倍の処理高速化に成功しました。この改善によって、アイテム変換機能への遷移が高速になることが期待されます。

スキルの入れ替えに関する新規機能開発

2021年8月現在の仕様では、各選手ごとに習得できるスキルの個数に上限があり、別のスキルを習得したい場合は既存のスキルのどれかと入れ替えるようになっています。この際、付け替えによって外したスキルを再度習得させたいときに、新しく習得する場合と同様にアイテムを消費する必要があります。これは特に、UR選手で投手と野手を切り替えたい際に不便を強いる形となっています。

そこで、「過去に習得したことのあるスキルを任意に入れ替えることができる」ようにスキルの習得に関する仕様の変更が提案されていました。この実装として、選手のスキルごとに「過去に習得したことがあるか」を新たに保存するようにし、したことがあればアイテム消費なしでスキルを入れ替えることができるようにしました。

チーム内でのやりとり

このタスクは同時期にクライアントサイドのインターンで参加していた方と協力して実装しました。また、仕様の変更に関わる部分であったため、詳細な仕様を確認するためプランナーの方ともやりとりを繰り返しました。結果としては、選手育成の画面にてスキルを入れ替える機能を実装し、クライアントサイドとの結合テストまで行うことができました。

※画面は開発中のものであり、実際の仕様とは異なる場合があります

インターン期間内で得られたこと

運営中のゲームタイトルの開発に携わる、ということ

実際にチームのエンジニアの一員として、ハチナイの開発に携われたのは非常に貴重な体験だったと思います。ゲームの体験を壊さないためにも、既存機能の改修には特に慎重になる必要があり、そのような考慮はこれまでの個人開発では得られなかったことだと思います。また、プランナーの方やクライアントエンジニアの方とのやりとりの間で認識の齟齬が生まれないようにうまく言語化することも開発を進める中で得られたことでした。

チームの一員として動く

開発に携わる間にメンバー、一人一人に信頼が置かれているのとともに、チームやプロダクトのために責任感を持って動いているのだと感じる機会が多かったです。

3週間という短い間でしたが、手厚いサポートに支えられ、成長できる機会をたくさんいただくことができました。ありがとうございました。

エンジニア職種でディナーを開催しました!

はじめに

はじめまして、アカツキゲームスでサーバーエンジニアをしている氏平です。

プロジェクトを跨いだエンジニア職種の交流会を実施したので、紹介します。

リモートワークで感じた問題意識

コロナ渦でリモートワークがメインの働き方が3年目に差し掛かろうとしています。
リモートワークになってからジョインされたメンバーがいらっしゃるが、面識がなく、話したこともないという方が周りに増えていました。

プロダクト・プロジェクト毎にエンジニアチームが分かれており、チーム内では頻繁にコミュニケーションを行っていましたが、チームをまたぐ交流の機会が希薄になっていました。

隔週でエンジニア全体で勉強会や共有などを行うMTGの場はありますが、雑談や他愛のない話をして人柄を知れる場がなかなかありませんでした。

Slack や Zoom だと実務の話になりがちで、目の前の人がどういう思いでどういう働き方をしているのかなどカジュアルにノンバーバルなコミュニケーションが行える場が必要だと思い、今回企画して実施しました。

実施まで

社内の制度として用意されているものではなかったので、自分で問題提起を行い、企画し、予算を見積もり、Enginner Office(エンジニアの人事部門) とやり取りしました。

提案したところ、快諾してもらえたため、当日の段取り含めて準備しました。

当日の様子

エンジニアリングに関わる全ての方を対象にしたかったので、エンジニアだけではなく、エンジニア採用の方や人事の方にも参加いただきました。

 

最後に

会を開いたことによって、自分自身、新しい横のつながりを作ることができ、非常に有意義な時間にすることができました。

今後、新たなコラボレーションが生まれることに期待です。

cocos2dx 3.17でASTCフォーマット使ってみた

こんにちは! Suです。
この記事は Akatsuki Advent Calendar 2022 24日目の記事です。
昨日はいたみんさんの Jenkins Log Parser Plugin の話でした。
色つけてログがみやすくなります。うちのプロジェクトにも導入したいですね〜
読みやすくていい記事でした。勉強になりました!

はじめに

cocos2d-x-3.17.2 はASTCという画像フォーマットをサポートしないので、サポートできるように色々頑張ったことをここに書き残します。
ソフトウェアデコードとハードウェアデコード両方とも実装してみました。

ASTC とは?

ASTCは綺麗で容量もそこそこ抑えられるテクスチャの圧縮フォーマットです。
iPhone6 以降、2015年以降に発売したAndroid端末などは大体使えます。

Block サイズは大きくなるど、綺麗に見えるがサイズも増えます。
ASTC のアルゴリズムはこの文章にて詳しく紹介しません 🙏 

cocos2dx は ASTC サポートしていますが、Axoml という cocos2dx-4.0 から派生したゲームエンジンはサポートしています。それを参考しつづ cocos2dx-3.17 で実装してみます!

ASTCソフトウェアデコード

まず Axoml エンジンから ASTC デコードコードを拝借いたします。

ASTCデコードライブラリ置く場所
ファイル/フォルダ 場所
axmol/astc.h cocos2d/cocos/base
axmol/astc.cpp cocos2d/cocos/base
axmol/thirdparty/astc cocos2d/external

xcodeprojに追加することもお忘れ無く!

 

ここでビルドして問題ないと確認しましょう。

続いて端末が ASTC に対応できるのかの実装です。
他のフォーマットを真似して、CCImage に ASTC の実装を追加します。

bool Image::isASTC(const unsigned char *data, ssize_t dataLen)
bool Image::initWithASTCData(const unsigned char * data, ssize_t dataLen)

を新規追加して、既存の

Image::initWithImageData(const unsigned char * data, ssize_t dataLen)
Image::detectFormat(const unsigned char * data, ssize_t dataLen)

に ASTC の分岐文を追加します。

▼クリックでコードを表示する

 

最後に HellowWorldScene に astc 画像を表示するボタンを作成します。
画像はみなさんご存知のレナさんにしました。
下記の ARM 社の astc-encoder を使って PNG から ASTC に変換しました。

▼クリックでコードを表示する


下記はソフトウェアデコードがかかった時間です。
ASTC 8x8のサイズ圧倒的に優秀ですが、
やはり遅いですね...ハードウェアでコード実装しないと使えないの気がします。

ASTCソフトウェアデコード
解像度 フォーマット サイズ 時間
512 png 386KB 約35ms
512 astc 4x4 226KB 約270ms
512 astc 8x8 70KB 約300ms
1024 png 1.4MB 約80ms
1024 astc 4x4 1.1MB 約892ms
1024 astc 8x8 266KB 約930ms
1960 png 4.1MB 約185ms
1960 astc 4x4 3.8MB 約3100ms
1960 astc 8x8 963KB 約3210ms

※時間は10回平均、cacheなし

動画はこちらです

ASTCハードウェアデコード

まず最初の問題は、GLES 3.0 から ASTC ldr が対応していますが、cocos2dx-3.17 が GLES 3 に対応しないです。昔一時 GLES 3.0 対応させたですが、どうやら一部 Android シミュレーターがクラッシュするとパフォーマンスの問題で revert されたみたいです。

実験として、ios を GLES 3.0 に対応させます。

▼クリックでコードを表示する

 

手元の端末 iPhone 8 で対応できる ASTC フォーマットを調べると、ldr しか対応できないので、今回はそれを対応させてみます。

GL_KHR_texture_compression_astc_ldr 

ちなみに、このサイトで対応しない端末が調べます。

端末が ASTC に対応できるのかをチェックするため、CCConfiguration に isSupportsASTC() を追加します。

▼クリックでコードを表示する

 

続いて ASTC 4x4、8x8 のPixelFormat を追加します。
それそれの internalformat は この仕様書 に書かれてます。

COMPRESSED_RGBA_ASTC_4x4_KHR 0x93B0 COMPRESSED_RGBA_ASTC_8x8_KHR 0x93B7

▼クリックでコードを表示する

 

bpp(BitsPrePixel) は Wikipedia で確認できます。

Block footprint Bit rate
4×4 8.00
8×8 2.00

主に下記 CCTexture2D.cpp の PixelFormat マップに ASTC の項目を追加することです。

static const PixelFormatInfoMapValue TexturePixelFormatInfoTablesValue[]

▼クリックでコードを表示する

 

最後にCCImage.cppにハードウェアデコードの処理を追加すればOK!

▼クリックでコードを表示する

 

では時間を測ってみましょう〜

ASTCデコード
解像度 フォーマット サイズ ソフトウェア時間 ハードウェア時間
512 png 386KB 約35ms 約35ms
512 astc 4x4 226KB 約270ms 約3ms
512 astc 8x8 70KB 約300ms 約2ms
1024 png 1.4MB 約80ms 約80ms
1024 astc 4x4 1.1MB 約892ms 約5ms
1024 astc 8x8 266KB 約930ms 約3ms
1960 png 4.1MB 約185ms 約185ms
1960 astc 4x4 3.8MB 約3100ms 約17ms
1960 astc 8x8 963KB 約3210ms 約6ms

※時間は10回平均、cacheなし

爆速じゃん!!!
Fooooooo 🎉🎉🎉

動画はこちらです

Github repo もありますー


最後に

ハードウェアデコードがすごかった...
Androidも試したいですね 🤔
もし今後他のフォーマットを追加したい時、もっとちゃんとしたコード書かないと思いますが、大体同じやり方でできると思います。
自分のメモもなりましたし、この記事読んだ君の役に立てればいいですね〜

明日は Sieben.L さんのドライブと関連した内容です。楽しみですね!

アカツキでは一緒に働くエンジニアを募集しています。
カジュアル面談もやっていますので、気軽にご応募ください。
https://hrmos.co/pages/aktsk/jobs?category=1220948640529788928

Fast DDLを使った高速なmigrationを実施してみた話

この記事は Akatsuki Games Advent Calendar 2022 の22日目の記事です。昨日はKazuma Sakamotoさんの「UE5でMMDを踊らせるぞ!」でした。こうした興味などでやってみたいことを実験してみてブログまで書くのは大事ですね!

 

はじめに

はじめまして、アカツキゲームスでサーバーエンジニアをやっています柴原です。今回は自分の担当しているモバイルゲームのプロジェクトにてAurora MySQL lab modeの機能の1つであるFast DDLを使った高速なmigrationを試してみました。

経緯

モバイルゲームの様な運用型のゲームでは、運用年数に比例してユーザデータの量が増えていきます。加えて、新機能追加に伴うテーブルへのカラム追加などが必要になる場面があります。そして、このような構造変更はデータ量に比例して時間がかかります。私の所属するプロジェクトでは、こうしたDBの構造変更の作業はメンテナンス時間を設けて、その時間内で行っています。メンテナンスの時間はゲームをプレイすることができないため、短ければ短いほど良いです。そのため、メンテナンス時間を短くできる可能性のあるFast DDLに着目しました。

Fast DDLとは

Fast DDL は、AWSが提供している機能の一つでALTER TABLE tbl_name ADD COLUMN col_name column_definitionの操作をほぼ瞬時に実行できる機能です。以下3つの制約を満たしている時にFast DDLを利用できます。

  • デフォルト値を持たないこと
  • nullableなカラムであること
  • 末尾挿入であること

しかし、ドキュメントにあるようにAWSは本番DBクラスターでの運用を推奨していません。本番環境で導入を検討する際には注意してください。

Fast DDLが使えるのは、Aurora MySQL version1とversion2(MySQL5.6もしくはMySQL5.7相当)です。Aurora MySQL version3ではFast DDLは廃止され、MySQL8系で導入されたinstant DDLが使用できるようになります。

設定

ゴールとしては、RDSクラスターのaurora_lab_modeの設定パラメータが1になっている状態です。設定手順を以下に示します。

  1. aurora_lab_modeを有効化したDBクラスターパラメータグループを用意します(これはすでに適用しているDBクラスターパラメータグループの値を変更して使用しても問題ありません)
  2. このパラメータグループを該当RDSに適用します(この段階ではまだ適用が終わってません)
  3. パラメータグループを完全に適用するためRDSの再起動を実施します
  4. 念のためにaurora_lab_modeが有効化されているか確認する(Optional: cli経由でも確認できますし、select @@aurora_lab_mode;でも確認できます)

検証してみた

本番のRDSをクローンした環境で実験を行いました。実際のメンテナンスにて行う予定のDB構造の変更に対して、Fast DDLの効果を比較検証しました。今回行うDBの構造変更は、通常のカラムとVirtual Generated カラムの追加です。この2点について検証を行いました。

通常のカラム追加の結果を以下に示します。

  • Fast DDLなし:2時間程かかる
  • Fast DDLあり:2秒 程かかる

結果は一目瞭然ですね。単純な比較ではありますが、3600倍高速になります。(恐らくどんなにデータ量が多くても数秒で終わるような仕組みだとは思います。)

続いてVirtual Generated カラム追加の結果を以下に示します。

  • Fast DDLなし:0.20秒
  • Fast DDLあり:2.80秒

結果としては、多少遅くなりました。この時間差であれば誤差なので、特に気にすることなくこの機能を有効化して作業ができそうなことが確認できました。

まとめ

今回は、Fast DDLを用いた高速なmigrationを検証しました。本番環境での利用にはリスクがあるものの、十分検討に足りうるものであると思います。本記事は、この機能を本番で使うことをお勧めしている訳ではないです。十分な事前検証を行った上で検討してみてください。

 

明日のAkatsuki Games Advent Calendar 2022 は、itmさんの「JenkinsのLog Parser触ってみた」です!お楽しみに!!

 

最後に、アカツキでは一緒に働くエンジニアを募集しています。
カジュアル面談もやっていますので、まずは雑談からいかがでしょうか。

hrmos.co