スケーラブルな Slack アプリ開発: API レート制限の処理

サンフランシスコにある Slack 本社の周辺地域では、いつ起きてもおかしくない大地震への備えをすることが求められています。それが起きないことを願っていますが、Slack では万一に備え、大地震対策キットを用意しています。アプリ開発者にとっても、大規模な災害対策をしておくことは大いに役立つでしょう。

Slack の API を含め、どの API を使用している場合でも、予期しない動作が発生する可能性があります。実際にレート制限がかかることはめったにないとはいえ、開発したアプリで、不測のピーク使用量が発生したらどうなるでしょうか?突然大量のユーザーがアプリを利用し、想定を超える量のトラフィックを処理することになったらどうなりますか？

さて、まずは、たくさんの人にアプリを使ってもらえたことを喜びましょう🎉。第 2 に、とてつもない規模の処理が発生する事態を想定したスケーラブルなアプリを開発しているなら、なお理想的でしょう。とはいえ、この記事を読んでレート制限について考慮し始めたとしても、今からでも遅すぎることはありません。

---
# Slack プラットフォームでのレート制限

Slack API では、メソッドごと、またワークスペースごとにレート制限を使用します。例えば、アプリから呼び出す `emoji.list` が多すぎるとレート制限が発生しますが、その場合でもトークンはまだ `chat.postMessage` を呼び出すことができます。また、1 つのワークスペースでアプリにレート制限が発生した場合でも、他のワークスペースでは要求を実行できます。Web API に対するレート制限について考慮しておけばよいでしょう (Events API にも、[1つのワークスペースにつき 1 時間あたり 30,000 回のイベント送信](https://api.slack.com/docs/rate-limits#events_api)というレート制限がありますが、この制限に達することはめったにないはずです)。

プラットフォームは階層的にレートの制限をしているので、ひとつのメソッドをアプリが呼び出せる回数は、メソッドに応じて異なります。例えば、アプリは `files.upload` を 1 分間に約 20 回呼び出せますが、`chat.postEphemeral` は 1 分間に約 100 回呼び出せます。[プラットフォームのレート制限には 4つの階層があります](https://api.slack.com/docs/rate-limits?utm_medium=referral&utm_source=apiblog&utm_campaign=fy20Q102-rate-limits#web)が、意図的にあいまいな定義になっており、ほとんどの場合はアプリが制限を超えることはありません。

Web API の各階層の詳しい内容と、各メソッドがどれに該当するかについては、[API ドキュメント](https://api.slack.com/docs/rate-limits#web) をご覧ください。アプリを作成する際は、レート制限を適切に処理できるよう、レート制限が想定どおりに設定されている必要があります。では、どのようにしたらよいでしょうか。

# レート制限の処理

レート制限を恐れる必要はありませんが、アプリ内でレート制限を想定して適切に処理しておかないと、ユーザーエクスペリエンスが損なわれることがあります。

最初のステップとして、アプリで実行する API 呼び出しの回数を制限する必要があります。頻繁に変更されないデータ (ユーザー情報やチャンネル情報など) の一部は、キャッシュに入れることができます。また、取得しようとしているデータの一部が、アプリで既に受け取ったレスポンスの中に含まれていることもあります。

要求を制限することに加えて、アプリはその発生時にレート制限エラーを処理できる必要があります。[Node SDK](https://github.com/slackapi/node-slack-sdk) を使用すれば簡単です。デフォルトでレート制限が処理されるため、追加のロジックを実装する必要がありません。

Node SDK を利用したくない場合や別の言語やライブラリを使う場合には、次の方法でレート制限エラーを処理することもできます。

- 送信した Web API 要求でレート制限が発生したかどうかを判別します。この情報は、要求を Slack に送信した後に返されるステータスコード内にあります。Slack に対する要求でレート制限が発生した場合、ステータスコード 429 が返されます。

```BASH
HTTP/1.1 429 Too Many Requests
Accept: application/json, text/plain
Content-Type: application/x-www-form-urlencoded
Date: Tue, 29 Jan 2019 18:41:22 GMT
retry-after: 3
{
 "ok": false,
 "error": "ratelimited"
}
```

- その Web API メソッドを再試行できるまで待機します。待機する時間は状況によって異なるため、時間を推測しなくてもいいように、Slack は情報を含んだヘッダーを送信します。これは retry-after というヘッダーで、要求を再試行するまでに待機するべき合計秒数 (ミリ秒ではない) を示します。

Node SDK では、実行するすべての要求に対して キュー を使用しています。クライアントでレート制限が発生した場合は、指定された retry-after の時間だけキューを待機してから、もう一度キューを開始します。以下が、レート制限エラーを処理する基本的な方法を示す擬似コードです。

```JS
let conversationId = 'C123'
// まず、リクエストの送信
let response = sendRequest({ channel: conversationId, text: 'Hello, friend 😄' })
if (response.status == 429) {
 // レート制限エラー！待機時間をチェック
 let retryAfter = response.headers['retry-after'];
 // Web API へのリクエストを待機
 queue.pause()
 // ヘッダーに表示されている残り時間まで待機
 // 大事なことなのでもう一回言うよ。ミリ秒じゃなくて秒だからね
 wait(retryAfter * 1000)
 // リクエストを再開
 queue.resume()
} else {
 // やった〜！レート制限エラーなし！
}
```

このロジックそのものの制限が多いと感じるならば、要求の再試行間隔を指数関数的に遅らせる手法を使うのもよいでしょう。

レート制限では、少しの準備をしておくだけで大きなメリットがあります。エンドユーザーのエクスペリエンスを低下させないよう、万全の準備をして、想定しうるスケールにアプリが対応できるようにしましょう。[ビルトインのレート制限ハンドラーを使用する](https://slackapi.github.io/node-slack-sdk/web_api)にしても、独自の処理をコーディングするにしても、恐るべき 429 が降りかかる際への備えをしておくようお勧めします。

何かお困りの場合は、いつでも開発者サポート (developers@slack.com) にご相談ください。

---

Slack のプラットフォームの今後の展開に関心をお持ちの場合は、開発者向けのプラットフォームロードマップをご覧ください。

原文 [Handling Rate Limits with Slack APIs](https://medium.com/slack-developer-blog/handling-rate-limits-with-slacks-apis-f6f8a63bdbdc) by [Shane DeWael](https://twitter.com/shanedewael)