🤟

GAS + Slack Slash Commandsのタイムアウト対策

2 min read

タイムアウトとの戦い

/[コマンド名] failed with the error "operation_timeout"

https://api.slack.com/interactivity/slash-commands

Slackはリクエストを投げてから3秒以内にレスポンスがないとタイムアウトしてしまいます。
ですが、処理は停止することはありません。(自分だけにしか見えない形でエラー文は表示されますが...)
なので心配することはありませんが「エラー文見たくない!」という方向けに対策をいくつか紹介します。

GASでは並行処理が難しく、doPostからcallbackをreturnしなければslackにレスポンスを返せません。(Lambdaとかだと処理の最初にcallback関数呼び出してレスポンスを返せばタイムアウトを防げます)

したがって、必ずdoPost内で処理を終了させなきゃいけないので処理の重さによってはタイムアウトが発生することがあります。

[対策その1] (同期実行)3秒以内に処理が終わるように工夫する

とりあえず処理を見直してみます。

  • 外部ライブラリの使用はなるべく控える
  • データ取得・挿入をまとめて処理する
  • 必要のない処理は飛ばす
  • etc...

以下の記事が参考になるかと!

https://officeforest.org/wp/2018/11/24/google-apps-scriptを高速化するテクニックまとめ/

[対策その2] (非同期実行)時間主導型のトリガーを発火させる

対策その1で高速化を図っても、どうしても3秒以内に処理が終わらない場合です。
時間主導型のトリガーを発火させます。

ScriptApp.newTrigger('main') //発火させたいメソッド名
         .timeBased()        //時間主導型のトリガー
         .after(1000)        //ミリ秒で設定
         .create();

上の例では1秒後にトリガーを発火させようとしていますが、実際は1秒後には発火してくれません。
だいたい30秒から1分後に発火されます。
この理由としては、GASのトリガー設定画面から時間主導型の詳細をみてみると秒数まで指定することができないからだと思われます。
ミリ秒で設定させるなら秒数も指定させてくれたって良いのになぁ...
GASのトリガー設定画面

トリガーを生成するとログとして残ります。
ですがスラッシュコマンドを叩かれるたびにトリガーが溜まっていくので、要らなければdelete処理も何処かに置いておきましょう。(ログは残しておくべき)

function deleteTriggers() {
  let triggers = ScriptApp.getScriptTriggers();
  triggers.forEach(function(trigger) {
      ScriptApp.deleteTrigger(trigger);
  });
}

[対策その3] (非同期実行)スプレッドシート更新のトリガーを発火させる

スプレッドシート更新のトリガーを発火させます。
また、対策その2ではSlack Slash Commandsから何か引数を受け取っているとき、引数の保存ができません。なぜならScriptApp.newTrigger()はメソッドに引数を渡すことができません。そのためスプレッドシートに引数を保存して更新トリガーを発火させます。

  1. スプレッドシートを作成して[ツール]→[スクリプトエディタ]をクリック
  2. このスクリプトにdoPost/処理を書く
    • その処理中に受け取った引数を保存するメソッドを書く
function doPost(e) {
  const command = e.parameter.text; //slackスラッシュコマンドの引数
  const sheet = SpreadsheetApp.getActiveSheet();
  sheet.getRange(1, 1).setValue(command); //引数をスプレッドシートに保存 
  // その他処理...
  return ContentService.createTextOutput('hoge');
}
  1. [トリガー追加]から下画像のようにトリガー設定 (例:main関数を選択
    GASのトリガー設定画面
  2. 3でトリガー指定した関数でスプレッドシートから保存したコマンドを取得 (例:main関数で処理
function main() {
  const sheet = SpreadsheetApp.getActiveSheet();
  const command = sheet.getRange(1, 1).getValue(); //保存した引数をスプレッドシートから取得
  //処理の続きを書く...
}

バイバイタイムアウト

今回対策を3つ紹介しましたが、できれば対策1で止まるような実装&処理だとハッピーですね!
doPostが発火したらslackにレスポンスをすぐ投げ返せれば一発解決ですが...
今ところgasでの重ための処理は非同期処理で解決ですね!

Discussion

ログインするとコメントできます