LANG SELRCT

Google Apps Scriptのコードを書く場所  (新規作成: スプレッドシート | スクリプトエディタ

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/macros/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名をメールの本文に記載する

最新の投稿

Pixel 5でSuicaが使えないときはGoogle PayでSuicaを有効にしてみる

Pixel 5への機種変でSuicaが使えない人向けに書きました。 MISSION Pixel 5でSuicaを使えるようにする SOLUTION 旧端末で機種変更の設定を行う NFCをオンにする Google PayでSuicaを有効にする PROBLEMS 以前...