📔 PlayFab の API 制限に引っかかった

# はじめに

[PlayFab で CloudFunction](https://docs.microsoft.com/ja-jp/gaming/playfab/features/automation/cloudscript-af/) を利用しているときに API 制限に引っかかってしまいました。負荷テストをした際に初めて気づいたのですが、公式ページにも言及が無かったため発覚が遅れてしまいました。そのため、PlayFab に依存していた機能を部分的に外す必要が出てきてしまい苦労しました。

本記事では、上記のような事態に陥る方を減らすため、API 制限に気づくまでの軌跡を辿りながら、PlayFab の CloudFunction を利用する際の注意点について、記事として残しておきたいと思います。

# PlayFab の API 制限に引っかかった要因

PlayFab の CloudFunction を利用すると、**PlayFab 経由で独自 Web API を実行することが可能になります。また、CloudFunction 経由で独自 Web API を実行すると、PlayFab ユーザ情報が含まれたパラメタが含まれた状態でリクエストが飛んでくるため、その情報を利用することでサーバーサイドで PlayFab の操作を行うことが出来るようになり大変便利です。**

そのため、あるプロジェクトでは PlayFab CloudFunction を Azure Function や AWS Lambda のような FaaS を使っている感じで利用しておりました。_そして、その利用の仕方は誤りであったことに後々気づきます..._

## 負荷テストを実装するフェーズで CloudFunction を大量に叩いてみる

PlayFab の CloudFunction を実行するにあたり利用した PlayFab の API は [`Server-Side Cloud Script - Execute Function`](https://docs.microsoft.com/en-us/rest/api/playfab/cloudscript/server-side-cloud-script/executefunction?view=playfab-rest) というものになります。

同接 2000 人想定で負荷テストのシナリオを実装することが求められていたため、その通りシンプルに 2000 件同時に `Server-Side Cloud Script - Execute Function` を実行するシナリオを Gatling で組んでみました。すると、**何回やっても数十件以上は必ずエラーが発生していることが分かりました。**

```bash
# Gatling で負荷テストを実行した際に 50件失敗している様子
================================================================================
---- Global Information --------------------------------------------------------
> request count                                       2000 (OK=1950   KO=50    )
> min response time                                    320 (OK=320    KO=354   )
> max response time                                  14459 (OK=9723   KO=14459 )
> mean response time                                   998 (OK=934    KO=3485  )
> std deviation                                       1510 (OK=1304   KO=4310  )
> response time 50th percentile                        545 (OK=543    KO=656   )
> response time 75th percentile                       1085 (OK=1077   KO=7209  )
> response time 95th percentile                       2243 (OK=2029   KO=10210 )
> response time 99th percentile                       7947 (OK=7775   KO=14353 )
> mean requests/sec                                    100 (OK=97.5   KO=2.5   )
---- Response Time Distribution ------------------------------------------------
> t < 800 ms                                          1393 ( 70%)
> 800 ms < t < 1200 ms                                 367 ( 18%)
> t > 1200 ms                                          190 ( 10%)
> failed                                                50 (  3%)
---- Errors --------------------------------------------------------------------
> status.find.is(200), but actually found 400                        50 (100.0%)
================================================================================

```

正直 2000件程度の API アクセスであれば、何の問題もなく負荷テストが通ると考えていたので、この結果には驚きました。**原因は何なのか調べたところ、Azure Function で PlayFab ユーザ認証を行うために利用していた [`Authentication - Validate Entity Token`](https://docs.microsoft.com/en-us/rest/api/playfab/authentication/authentication/validateentitytoken?view=playfab-rest) で 503 エラーが発生していることが分かりました。**

少ない API 実行件数で負荷テストを実行する場合は問題ないのですが、**件数が一定数超えたタイミングで 503 エラーが返却されるようになってしまいます。しかし、たまに同じ件数を実行しているはずなのにスムーズに全件 API 実行に成功することもありました。これは何らかのレートリミット等に引っかかっているのかも知れないということで調査したところ、次の事実が判明しました。**

## CloudFunction は FaaS の用途には適さない

どうやら PlayFab 公式フォーラムの [ある投稿](https://community.playfab.com/questions/30828/server-api-limitations.html) によると、**PlayFab の Server API を呼び出す際は 10秒間に 1000回という制限があるようでした。** そして、この制限を突破するには商用のための契約をした後にインスタンス割当に関する交渉をすることで可能になるかも知れないとのことでした。

> Servers are rate limited to 1,000 calls per 10 seconds. What jital is highlighting that the per-player rate should be no more than a few times a minute. A server can call at a higher rate, as it is calling for a lot of users, potentially. If you need a higher limit than 1,000 per 10 seconds, you'll want to talk to our sales team about getting on an Enterprise contract so that we can work with you on custom limits. There's an option on the Contact Us form on the main site to message them, if you want to go that route.

つまり、**普通に PlayFab を利用している限りはプランをアップグレードしようと制限に引っかかるということが分かりました。** また、[今年 1月に投稿された内容](https://community.playfab.com/comments/48674/view.html) を見るに 10秒以内に 5000以上のユーザーがログイン/登録できたとあり、もう API の 10秒間に 1000回呼び出し制限は撤廃されたのかを聞いているユーザがいたのですが、まだ撤廃されていないと返信されていたので偶然だったようでした。

ちなみに私も上記が気になったので、**セッションごとにレートリミットのかかり方が変わるのか検証するために異なるユーザ情報を用いてリクエスト 2000件を並列に実行してみましたが、503 エラーは変わらず返却され続けていたので、少なくとも私の手元の環境では効果はなさそうでした。**

> No, the rate limits on the Client and Server API calls has not changed. However, the rate limits are currently enforced on a per-server basis. And since the service runs a great many servers for load balancing, it is possible to exceed those limits from time to time.

原因の調査中 PlayFab は EC2 の us-west-2 リージョンでリクエストを受けていそうなことが分かったのですが、そのアクセス先のインスタンスがロードバランサによって分散されているため、レートリミットの制限がインスタンス先により、時と場合によって制限されるかどうかが決まってくるのかもしれないとのことでした。

以上のことから、**PlayFab の CloudFunction については Azure Function や AWS Lambda のような FaaS のような用途には使わず、あくまでもアプリケーションで補助的に利用するための独自スクリプトを動かす程度に留めて利用するのが正解なように感じました。**

**PlayFab のユーザ認証情報である SessionTicket や EntityToken を利用することで、認証周りの実装部分を省くことが出来るかも知れないと思い期待していたのですが、それは別の BaaS を使うか IaaS で自前で作るのが良さそうでした。**

# おわりに

API の 10秒間に 1000回呼び出し制限については明示的にドキュメントに記載があるわけでもなかったため、気づくことが出来ずプロジェクト終盤で気づくという事故が起きてしまったのですが、私と似たような境遇に陥る人が少しでも減るようにと記事を書いてみました。

とはいえ、少し調べれば出てくるような制限だったので純粋に調査不足だったなあと反省しました。。CloudFunction はとても便利ですが、利用する際は API の呼び出し制限等用法には十分お気をつけてご利用くださいませ。

PlayFab が便利な BaaS であることに疑いの余地は無いので今後も利用すると思いますが、知見を貯めつつ効果的に使えるよう勉強していきたいと考えております。また何か知見を得たら随時ブログ記事に書き溜めていきたいと思います。

# 参考リンク

- [Azure 関数を使用した PlayFab CloudScript \- PlayFab \| Microsoft Docs](https://docs.microsoft.com/ja-jp/gaming/playfab/features/automation/cloudscript-af/)
- [Server\-Side Cloud Script \- Execute Function \(PlayFab CloudScript\) \| Microsoft Docs](https://docs.microsoft.com/en-us/rest/api/playfab/cloudscript/server-side-cloud-script/executefunction?view=playfab-rest)
- [Authentication \- Validate Entity Token \(PlayFab Authentication\) \| Microsoft Docs](https://docs.microsoft.com/en-us/rest/api/playfab/authentication/authentication/validateentitytoken?view=playfab-rest)
- [Server API limitations \- Playfab Community](https://community.playfab.com/questions/30828/server-api-limitations.html)
- [Server API limitations \- Playfab Community](https://community.playfab.com/questions/30828/server-api-limitations.html?childToView=48674#comment-48674)