はじめに
日頃は描画周りでShader書いたりサウンドプログラム書いたり敵のAI作ったり
マルチプレイのネットワーク基盤整えたりしているUnityエンジニアのCovaです.
今回は業務中にSlackとクラウド環境で色々処理を行わせたい時に重い処理を行うと
強制的にSlack側でタイムアウトになる問題に遭遇したのでその回避方法を掲載しておきます
TL;DR
Slack → CloudFunction と連携して動かす時に重い処理(DB操作や非同期処理など) をするときは重い処理を Cloud Pub/Sub
経由で 別のCloudFunction
を発火させてそちらで対応しよう
経緯
業務で Slack とCloudFunction を連携して SlackのBOTメンションや スラッシュコマンドをトリガーに色々処理をサーバレスな仕組みで構築していたところ、このようなエラーに遭遇しました.
タイムアウトエラーと思いきや、実はCloudFunction側で行なっていた重たい処理(DB操作など) は裏で成功しているという挙動をしていて調べたところ、公式で「レスポンスは3000ms 以内に200を返せ」とのお達しでした.
https://api.slack.com/interactivity/shortcuts/using#shortcut_response
さすがに業務で使うコマンドが毎回TimeOut の表示が出ると、ユーザー(メンバー)側から絶対に毎回問い合わせが来るためなんとかしようと思いました.
解決にいたるまで
事前調査
こちらの記事にて試行錯誤の経緯が紹介されていましたが Python + Slack (+ CloudFunction )
ならSlack.BOLTのLazy Listener を使えば遅延実行ができて解決するとのことでしたが、今回は「SlackCommand 」なども使っている都合上 Slack.BOLT は使えませんでした。
公式からのサポート
Twitter で 愚痴った 悩みをツイートしたところ、なんと公式のSDK開発者の方からリプライを頂けました.
はじめまして!(急にすみません)3 秒以上かかる処理は、Bolt の lazy listener がやっているように非同期実行に逃すしかないんですよね。。Google Cloud であれば Cloud Run の always-on などを検討されるとよいかもしれません。お役に立てば幸いです! https://t.co/4vpoFuRboY #SlackDevJP
— Kazuhiro Sera (瀬良) (@seratch_ja) September 21, 2022
なるほど Cloud Run か... ってこれコンテナ起動だからサーバレスサービスじゃダメじゃん!
公式からのサポートその2
以下のようなPub/Sub を用いて重い処理を別関数で実行させれば問題ないとのことでしたので実際に作ってみました
実装について
特段難しいことは行っておらず、公式のサンプルを組み込めば実装可能です
Pub/Sub について
https://cloud.google.com/pubsub/docs/publish-receive-messages-client-library?hl=ja
落とし穴
- 当たり前ですが、1つ目のCloudFunctionで publish をできるようにするために
Pub/Sub 管理者
の権限をサービスアカウントに付与しないと Permission Denied で実行できません(1敗) - Subscribe する側の引数の形式はSlackのSlackCommand受取とは異なるので確認しないと引数エラーになります (1敗)
# EndPoint for Slack Command
def slack_command_endpoint(request: flask.Request):
...
# EndPoint for Subscribe
def subscribe_endpoint(event, context):
...
結果
ちゃんと1つ目のCloudFunction 経由で1つ目のBOTメッセージが,
重い処理が実行されたあとに2つ目のBOTメッセージが返ってきてTimeOut は表示されませんでした.
ということでSlack (BOT Mention / SlashCommand) とCloudFunction で連携するときは「処理が3000msで終わるかどうか」で構成が変わるので注意しましょう