LANG SELRCT

コードを書く場所

2019年6月14日金曜日

Google Apps Scriptで重複実行を回避したい(PropertiesService)


スクリプトを実行中は重ねて実行できないようにしたい。

今回ぶつかったケース

  1. トリガーで1分おきに実行する関数を設定していて
  2. その関数の処理が1分以内に終わらないことがあり
  3. 1分後、前回の処理が完了しないうちに次のトリガーが発動して
  4. 意図しない挙動になってしまう


これを回避するためにスクリプトのプロパティを利用しました。

  • 実行中はtrueを入れて重複実行しそうになったら重複実行せず1分待つ
  • 実行後はfalseを入れてすぐに次の関数を実行できるようにしておく


LockService でもできそうな感じでしたが、先に思いついたこの方法で、やりたいことは実現できたのでそのコードを書き残しておきます。



コード.gs
/************************************
初回実行
************************************/
function initialize() {
  mailNotification(ScriptApp.getScriptId(), '処理を開始します: ');
  set_property('running', true);
  set_property('count', 0);
  createTriggerEvery1Minutes();
}

/************************************
トリガーを作成する
************************************/
function createTriggerEvery1Minutes() {
  ScriptApp.newTrigger('run')
           .timeBased()
           .everyMinutes(1)
           .create();
}

/************************************
トリガーで実行する関数
************************************/
function run() {
  if(getProps('running') === 'true') {
    mailNotification(ScriptApp.getScriptId(), '前回の処理が終わっていないので次の1分を待ちます' + new Date());
  } else {
    mailNotification(ScriptApp.getScriptId(), '前回の処理が終わっているので実行します' + new Date());
    set_property('running', true);
    Utilities.sleep(70000);
    var count = parseInt(getProps('count'));
    if(count === 3) {
      deleteTrigger('run');
      mailNotification(ScriptApp.getScriptId(), '処理が完了したよ: ');
    } else {
      set_property('count', count + 1);
      mailNotification(ScriptApp.getScriptId(), '処理を開始するよ: ');
    }
    set_property('running', false);
  }
}

/************************************
完了時にGmailで通知する
************************************/
function mailNotification(script_id, message) {
    var mail = Session.getActiveUser().getEmail();
    var file_name = DriveApp.getFileById(script_id).getName();
    MailApp.sendEmail(mail, message + file_name, 'https://script.google.com/a/c-fo.com/d/' + script_id + '/edit?usp=drive_web');
}

/************************************
指定したトリガーを削除する
************************************/
function deleteTrigger(functionName) {
  var allTriggers = ScriptApp.getProjectTriggers();
  for (var i = 0; i < allTriggers.length; i++) {
    if (allTriggers[i].getHandlerFunction() == functionName) {
      ScriptApp.deleteTrigger(allTriggers[i]);
      break;
    }
  }
}

/************************************
スクリプトのプロパティを読み書きする
************************************/
var ScriptProperties = PropertiesService.getScriptProperties();

function getProps(id) {
  return ScriptProperties.getProperty(id)
}

function set_property(key, value){
  ScriptProperties.setProperty(key, value); 
}

意訳
 


この機能がやること
メールで開始通知します
スクリプトのプロパティのrunningにtrueを入れます
スクリプトのプロパティのcountに0を入れます
トリガーをセットする関数を実行します





この機能がやること
  新しくトリガーでrunという関数を
    時間ベースで
    1分毎に
    作成します





この機能がやること
  スクリプトのプロパティのrunningがtrueなら
    指定したメッセージをメールに送る
  trueでなければ
    指定したメッセージをメールに送る
    スクリプトのプロパティにtrueを入れる
    時間のかかる処理を実行する(この例では70秒間ただただ待つ処理)
    スクリプトのプロパティのcountを見て
    3なら
      トリガーからrunを削除して
      指定したメッセージをメールに送る
    3じゃなければ
      スクリプトのプロパティのcountに+1して入れる
      指定したメッセージをメールに送る

    スクリプトのプロパティのrunnningにfalseを入れる


 



この機能がやること
  実行しているユーザのemailを取得して
  実行しているスクリプトのファイル名を取得して
  Gmailを送る(宛先のアドレス, メッセージ+ファイル名, ファイルのURL)





この異能がやること
  すべてのトリガーを取得して
  トリガーの数だけ繰り返す
    もし実行する関数名が渡されたものと一致したら
      そのトリガーを削除して
      for文から抜ける







スクリプトのプロパティを取得する

この機能がやること
渡されたidの値を返す


この機能がやること
渡されたkeyとvalueでプロパティに保存する




関連記事

1時間毎に実行するトリガーをスクリプトで書きたい
1分おきのトリガーを作って5分経ったらトリガーを全部消す
トリガー(不要になったもの)をスクリプトで削除する
ScriptPropertiesを読み書きしてみる
ユーザのGmailアドレスを取得する
スクリプトからmailを送信する
実行したスクリプトのURLとfunction名をメールの本文に記載する