Slackアプリ開発で避けて通れない「3秒ルール」とは?タイムアウトと多重実行を防ぐ非同期処理を徹底解説

お疲れ様です。はるさらと申します。
今回はSlack開発で引っかかることがある「3秒ルール」についての記事となります。

Slackのアプリを開発していると、
時々「あれ?処理が終わらないな…」とか、
ユーザーに「よくわからないエラーが出たよ」と言われることがありませんか?

それは、もしかするとSlackが定めている「3秒ルール」のせいかもしれません。
特に外部のサービスと連携したり、少し重い処理をさせようとすると、
このルールが大きな壁となって立ちはだかります。

今回の記事では、この「Slack 3秒ルール」とは一体何なのか、
なぜ問題になるのか、そして経験の浅い方でも実践できる
具体的な解決策まで解説していきます。


Slackアプリ開発を成功に導くための最重要ルール:3秒以内に応答せよ

結論から言うと、Slack 3秒ルールとは、
Slackが外部のサービス(あなたの作ったアプリ等)に対してリクエストを送った際、
必ず3秒(3000ミリ秒)以内にHTTPレスポンスを返さなければならない
という仕様です。

3秒を超えるとどうなる?二つの大きな問題

このたった「3秒」という制限が、開発者にとって非常に厄介な壁となります。
制限時間を超えてしまうと、主に以下の二つの深刻な問題が発生します。

  • ユーザーにエラーが表示される(タイムアウト) 3秒以内にアプリが応答しないと、Slackは「処理が完了しなかった」と判断します。結果として、ユーザーの画面に operation_timeout といったエラーメッセージが表示されてしまい、ユーザー体験(UX)が著しく低下します。
  • 処理が複数回実行されてしまう(多重実行・リトライ) 一部のイベントでは、3秒以内にレスポンスがない場合、Slack側が「リクエストが届かなかったのかな?」と判断し、同じリクエストをもう一度(リトライ)送信してきます。 これにより、ユーザーが一回メンションしただけなのに、アプリの処理が二回走り、「同じ回答が2回投稿されてしまう」といったシステムの信頼性を揺るがす問題が発生するのです。

なぜ3秒という制限があるのか?

そもそも、なぜSlackはこのような厳しい時間制限を設けているのでしょうか?

それは、Slackというプラットフォームの特性上、
チャットのテンポとリアルタイム性を最重要視しているからです。

Slackは、ユーザーがコマンドを打ったり、メンションを送ったりした際に、
すぐに反応が返ってくることを期待しています。

もし、アプリが応答するまでに10秒も20秒もかかっていたら、
ユーザーは待たされてイライラし、Slackの使い勝手を疑ってしまうこともあります。

◆お店の例えで理解する「3秒ルール」

このルールを理解するには、レストランの例えが分かりやすいです。

お客様(Slack)は、お店(あなたのアプリ)に入ってきて注文(リクエスト)をします。 お客様が求めているのは、「3秒以内」に「確かに注文は受け付けましたよ(Status code 200)」という返事です。
もし3秒以内に返事をしなかったら、お客様は「無視された」と思い、もう一度注文を繰り返すか、怒ってお店を出ていってしまう(タイムアウト)のです。
つまり、重い調理(時間のかかるメイン処理)が終わるのを待ってから返事をするのはNGということです。


3秒ルールを攻略!アプリ設計の基本戦略:非同期処理の導入

では、Google Driveへのファイルアップロード、
ChatGPTへの質問、スプレッドシートへの書き込みなど、
どうしても3秒以上の時間がかかってしまう処理をアプリに組み込みたい場合
どうすれば良いのでしょうか?

唯一にして最善の解決策は、「レスポンスを返す処理」と
「時間のかかるメイン処理」を分離する、
すなわち非同期処理を導入する
ことです。

戦略の基本:ACK(アック)を返してメイン処理を別立てにする

ここでいう「ACK」とは、Acknowledge(承認・確認)の略です。

  • まず、Slackからリクエストを受け取ったら、アプリはすぐに(3秒以内に)「リクエストを受け付けたよ!問題ないよ!」という意味のStatus code 200を返します(ACK)。
  • ACKを返す処理は一瞬で終わるので、3秒ルールをクリアできます。
  • その後、時間のかかる本来のメイン処理を別の場所(非同期)でゆっくり実行します。
  • メイン処理が完了したら、Slackの chat.postMessage APIなどを利用して、アプリ側からユーザーに結果を通知すればOKです。

具体的な解決策:LambdaやGAS環境での実践

サーバーレス環境(AWS Lambda、Google Apps Script:GASなど)でSlackアプリを構築している場合、この非同期処理を導入することが、設計上の基本中の基本となります。

  • FaaS環境での王道:Lambda+Lazy Listeners(Slack Bolt)

Slackの公式フレームワークであるBolt for Python/JavaScriptを使用している場合、
Lazy Listenersという機能が非常に強力な味方になります。

Lazy Listenersの仕組み: Lambda関数でリクエストを受け取ります。 まず、フレームワークが裏側で自動的に3秒以内にACKを返します。 その後、時間のかかるメイン処理(Lazy Listenerとして定義した関数)は、別のLambda実行環境で非同期に処理されます。

これにより、Lambdaの制約(HTTPレスポンスを返すと実行環境が破棄されがち)に縛られることなく、3秒以上の処理を安全に実行できます。

  • シンプルな非同期呼び出し(Lambda to Lambda)

Boltのようなフレームワークを使わない場合でも、非同期処理は可能です。

Function 1(受付役):Slackからのリクエストを受け取るLambda関数を定義します。 この関数内で、Status code 200を即座に返し、同時に時間のかかる処理を担う別のLambda関数(Function 2)を非同期で呼び出します。
非同期呼び出しには、AWS SDKを使ってInvocationTypeを ‘Event’ に設定します。 Function 2(実行役):メインの重い処理を行い、完了後にSlackへ結果を通知します。


もし非同期処理が難しければ?リトライ多重実行の回避策

特にGoogle Apps Script(GAS)のように、
非同期呼び出しが難しい環境で開発している経験の浅い方は、
まず「リトライによる多重実行の回避」だけでも対策しておくと、
システムの信頼性が大きく向上します。

リクエストIDをチェックして多重実行を防ぐ(GASなどで有効)

リトライによって多重実行が起こるのを防ぐには、
「このリクエストはすでに処理済みだ」とアプリ側が判断できる仕組みが必要です。

Slackから送られてくるイベントデータには、
そのリクエスト固有のID(例:client_msg_id)が含まれています。

多重実行回避のステップ:

  • アプリがSlackリクエストを受け取ります。
  • リクエストに含まれるIDを、キャッシュ(ScriptProperties、KVSなど)に保存されている「処理済みIDリスト」と比較します。
  • IDがリストになければ: IDをリストに追加し、初めての処理としてメイン処理を実行します。 (GASなどでは、ここで処理に3秒以上かかるとタイムアウトはしますが、多重実行は防げます。)
  • IDがリストにあれば(=リトライである): 何も処理せず、
    即座にStatus code 200を返して終了します。

この対策により、タイムアウトによるエラー表示は防げませんが、
「ユーザーが一回メンションしたのに回答が何度も表示される」といった最悪の事態は避けられます。


まとめ:3秒ルールは非同期設計の「学びの機会」

Slack 3秒ルールは、アプリ開発を始めたばかりの方には少し難しい壁に見えるかもしれません。
しかし、これは「いかにユーザーを待たせずにアプリを動かすか」という、
モダンなWebサービス開発における非同期処理の基本を学ぶ、良い機会とも言えます。

ポイントは「分離」:「素早い返事(ACK)」と「時間のかかる作業(メイン処理)」を分離する設計を意識しましょう。
王道は非同期:可能であれば、LambdaのLazy Listenersや、
Lambda to Lambdaの非同期呼び出しを導入するのが最も洗練された解決策です。

この記事を読んで、Slackアプリの信頼性を上げるための設計のヒントを得ていただけたなら嬉しいです。

どなたかのお役に立てば幸いです。
それではまたー!!