先週 ニフティクラウド mobile backend を採用していただいた、素晴らしいゲームアプリがリリースされました。
もの悲しげ世界観、ロボット同士の殺し合いを見つめながら、孤独な宇宙へメッセージを送る...シルエットのやさしい見た目とは裏腹にハードな設定の「ひとりぼっち惑星」。リリース後数日で徐々に人気が加速し、ついにはストアの一位に到達するなど、非常に好調なスタートを切ったのですが...
6/26(日)の夕方、急激なユーザー増加によりニフティのサーバーに巨大な負荷がかかってしまったため、サービスを一時停止する措置となってしまいました。ユーザーの皆さまにはご迷惑をおかけする形になり、申し訳ありません。
その舞台裏と原因、対策についてレポートいたします。
大ヒットによる高負荷状態の発生
私は春から ニフティクラウド mobile backend のエヴァンジェリストとして様々なゲームアプリ開発者へ紹介を行っておりまして、「ひとりぼっち惑星」の作者、ところにょりさんもその一人でした。(もっとも、ところにょりさんは以前から ニフティクラウド mobile backend を使っていたのですが....)
実はリリースのすぐ後の頃に、ところにょりさん宛にインタビューの打診をしていました。ユニークなメッセージ機能は ニフティクラウド mobile backend の「データストア」機能によって実現されていたので、ぜひ活用事例として広めたい、と考えていたからです。(インタビューは後編で公開します)。
そんなタイミングでの一時停止の知らせでしたので、急いでニフティクラウド mobile backendのテクニカルサポートと連絡を取りながら、原因究明に努めました。そして1日半後の6/28(火)昼過ぎ、ついにサービスの復旧をしていただくことができました。
#ひとりぼっち惑星 無事復旧できました! ただ、こえの送受信機能を使用するには、両OSともver1.1.1の最新アップデートに対応して頂く必要があります。ver1.1.0以前のバージョンではこえの送受信はできませんので、よろしくお願いいたします。
? ところにょり (@tokoronyori) 2016年6月28日
ichijoさん並びにニフティサポートの方々には本当に泣いて抱きつきたいくらい感謝しています!ありがとうございました!
? ところにょり (@tokoronyori) 2016年6月28日
ところにょりさんは少し前から、ユーザーの増加に伴い有償のエキスパートプランに加入していました。 一時停止をうけてテクニカルサポートが日曜の夕方には対応し、まずは改善案を提示、月曜にはソースコードをお預かりして詳細調査。夜には原因が判明したため、どんな手段がいちばん良いかを検討・相談して、午前中に反映。というハイペースのサポートになりました。アプリ側の実装改善と、ニフティクラウド mobile backend のオプション機能を追加利用していただくことにより、ゲームの内容を変えることなく復旧しました。
現在では同様のデータ量でも、安定した動作が可能となっています。
重くなってしまった要因:count処理
ユーザー数が非常に多くなったとはいえ、メインで扱っているデータは数百字の文字列です。これは「ボイス」という、ゲーム中のメッセージ送受信システムに当たる部分なのですが.....重くなってしまった原因は、データ送受信の流量やAPIコール数ではありませんでした。ニフティクラウド mobile backend の中にため込まれたデータを検索する処理に、データ件数に応じて重くなってしまう部分があったのです。
ニフティクラウド mobile backend では、データストア機能を使って保存するデータの種類を「クラス」としています。そして、そのクラスの件数を取得するために「CountAsync」メソッドが存在しています。
http://mb.cloud.nifty.com/doc/current/sdkguide/unity/query.html
「count」の処理は、クラスの中から対象となるオブジェクトの数を数え上げる処理のため、数え上げるオブジェクトの数が多くなるに従って負荷が増加していきます。ユーザー数が爆発的に増えたことでこの処理が増大し、本アプリのAPIコールがいわゆる「スロークエリ」という状態になっていました。
通常規模の個人開発アプリであれば問題ないのですが、ストア1位まで上り詰めると、同時接続数も検索するデータ量も途方もないものになってしまいます。今回の復旧が早かった理由として、iOS/Androidで配信直後だった最新バージョンでは、すでにこのcount処理を無くした修正が入っていました。
元々はアップデートでユーザーさんを徐々に移行する計画だったようですが、旧バージョンを利用できなくして一気に最新版に乗り換えさせる形にしてもらいました。アップデート版(ver1.1.1以降)では、countを使用しない代わりに、order(ソート)を使用して必要なデータを取得する処理に代わっています。
http://mb.cloud.nifty.com/doc/current/sdkguide/unity/query.html
さらなる高速化:インデックス機能
復旧後もより良いレスポンスでゲームを楽しんでいただくため、order処理をさらに高速化するべく、追加にて「インデックス機能」をご提案しました。
http://mb.cloud.nifty.com/customize.htm#index
データストアで頻繁に実施される検索・取得処理やソート処理がある場合、データストアの検索対象となるフィールドに対してインデックスを追加しておくと、処理が効率化され、レスポンスタイムを早くすることができます。 利用方法としては、実は管理画面でクラスを指定し、どのフィールドにインデックスを付与するか設定するだけです。「クラス」以外にも、「会員クラス」にインデックスを指定することもできます。
利用方法は下記にまとまっています。 http://mb.cloud.nifty.com/doc/current/dashboard/addIndex.html
こちらはカスタマイズプランになりますので、有償の機能になります。
というわけで、今回は
- アプリ実装面においての効率化(count処理→order処理)
- orderを高速化する有償オプション機能を追加
の2通りの対策で、負荷を抑えることに成功しました。
アプリ側の事前の対策
今、ニフティクラウド mobile backend をご利用のゲームを開発中の皆さまには、countの処理はクラスに登録された全オブジェクトなど、たくさんのオブジェクトを数える使い方は多用しないようにしていただければと思います。countを使う場合は、まず検索条件などを付けることで、そもそも数え上げるオブジェクトの数を減らす対策が有効です。
countはインデックス機能を追加して検索が速くなった場合でも、クラス内容の確認がある関係上、オブジェクトの数が増加するとパフォーマンスが落ちてしまうのでご留意ください。
またインデックス機能ですが、無料プランにおいても活用の術があります。 すでにシステム側でインデックス済みのフィールドが存在し、それらは何も設定しなくとも、高速な検索が可能になっています。
インデックス済みのフィールド一覧:
データストアの場合、objectIdを使った検索であれば高速に処理することができます。
ニフティのこれからの対策
今回の事例をオープンにしたのは、同じ方針の利用方法を考えているユーザーさんが同じ状況にならないように...と考えてのことです。(ところにょりさんのご協力に感謝いたします)
mobile backendは、ご利用されるアプリによってその活用方法も多種多様になります。今回の件も含めて、実装時の注意点についてまだまだドキュメントでご案内できていない部分もあるかと考えていますので、こちらは鋭意改善に努めたいです。
またニフティでは、負荷上昇を可能な限り未然に検知し、改善をご提案する取組みもしていますが、異常な負荷が発生した場合は、そのアプリへのサービス提供を一時停止し、今回のようにすぐに改善に向けたテクニカルサポートを実施するようにしています。日本のサービスなので当たり前ですが、開発者さんへ日本時間で日本語のテクニカルサポートを行い、今後も一刻でも早くサービスが再開できるよう努めます。
というわけで、後編はところにょりさん自身へのインタビュー記事となります! 大ヒットゲームアプリ「ひとりぼっち惑星」の裏側~後編~
※記事中のサンプルコード、説明の図式は2016年6月29日現在のものです。
そんな苦労のあった「ひとりぼっち惑星」を是非遊んでみてください!