こんにちは。ファンリピートの徳丸です。
今回は、GASでタイムアウトを気にせずに処理を実行する方法について、紹介していきます。
GAS(Google App Script)とは?
GAS (Google App Script) は、GmailやGoogle スプレッドシート、Google ドキュメント等のGoogle Workspace のサービスを自動化、拡張、統合する為のクラウドベースのJavascriptプラットフォームです。
スクリプトエディタを使用する事で、繰り返しタスクの自動化や、アプリケーション間の連携等を実現する事が可能です。
GASのランタイムエラーについて
GASにはスクリプトランタイムの最大値が6分で設定されています。
一つのスクリプトの一回の実行に対して、最大6分までしか処理を行うことができません。もし実行時間が6分を超えてしまった場合、処理はエラーを吐いて途中で中断してしまいます。
詳しくは、下記を参照して下さい。
しかし、大きな処理を実行しようとした際、処理の実行時間が6分を超えてしまう事はしばしばあるかと思います。
今回は、このランタイムエラーを避けるためのスクリプトの設計について、紹介していきます。
ランタイムエラーを避けるスクリプト
1.スクリプトの処理を分割
単純ですが、簡単かつ効果的な方法になります。
一つの処理を複数の関数に分割して実行することで、タイムアウトによるランタイムエラーが起こり辛くなります。
例えば、下記のような処理を実行するスクリプトがあると仮定します。
- テーブルのデータを全て読み込み、条件に一致するデータを削除後、全てのデータを編集し、あるデータを追加する
この場合、下記のように処理を分割する事ができます。
- 条件に一致するデータを削除
- 全てのデータを編集
- データを追加
これらの処理を、それぞれ異なる関数で作成します。
1の場合は、
- テーブルのデータを読み込む
- 条件に一致するデータを探す
- 一致したデータを削除する
2の場合は、
- テーブルのデータを読み込む
- 各データを編集する
3の場合は、
- テーブルを参照する
- データを追加する
といった処理の流れになるかと思います。
次に、これらの処理を1から順に処理を行うようにしていきます。
各処理の最後に、次の処理を起動させるための処理を追加します。下記を追加することで、その関数の処理終了後、1分後に次の関数が起動するようになります。
ScriptApp.newTrigger([次に起動したい関数名]).timeBased().after(1000 * 60).create()
これによって、一つのスクリプトでの処理時間が減り、タイムアウトが起こり辛くなります。
しかしこの処理の場合、例えば2の「全てのデータを編集」でデータ件数が多かった際に処理の最中にタイムアウトを起こしてしまう可能性があります。
2.処理時間で処理を分割
こちらは実装が少しだけ複雑になりますが、特に繰り返し処理を行う関数にて有効な方法になります。
処理の最初と、繰り返し処理中に時間を取得して、処理の開始から一定時間以上経過していた際に処理を一時中断することで、仮に処理回数が増えた場合でも、安全に処理を繰り返して実行する事が可能になります。
まず、処理の最初に現在時刻を取得します。
var startTime = new Date();
次に、繰り返し処理の最中に処理の時間が5分以上経過していた際に中断する処理を追加します。
var currentTime = new Date();
var elapsedSeconds = (currentTime - startTime) / 1000; //開始時刻と現在時刻の差分を取得
//時間の差分が300秒を超えている場合、1分後にスクリプトをセットして処理終了
if (elapsedSeconds > 300) {
ScriptApp.newTrigger([同じ関数]).timeBased().after(1000 * 60).create();
return;
}
これで、処理に5分以上時間が掛かった場合、処理を中断して1分後に再び同じ関数を実行するようにできました。
しかし、これでは中断前と同じデータを処理した後、同じように5分経過後、また最初から処理を始める事になってしまいます。なので、処理を途中から再開できるようにスクリプトプロパティを使って処理内容を修正します。
まず処理を中断したデータの行をスクリプトプロパティに保存する処理を追加していきます。
先程追加した繰り返し処理を中断する処理を、下記のように修正します。
この追加した処理では、nextIndexというスクリプトプロパティに、Index(現在処理を行っているデータの番号)を保存しています。
var currentTime = new Date();
var elapsedSeconds = (currentTime - startTime) / 1000;
if (elapsedSeconds > 300) {
PropertiesService.getScriptProperties().setProperty('nextIndex', index); //処理中の行をプロパティに保存
ScriptApp.newTrigger([同じ関数]).timeBased().after(1000 * 60).create();
return;
}
次に処理を行うデータの開始を特定の番号から開始できるようにします。
下記は、処理の最初の行を nextIndex が存在している場合はそのプロパティの値に、無い場合は1番目に設定する処理になります。
var startIndex = Number(PropertiesService.getScriptProperties().getProperty('nextRecordIndex')); //startIndexにnextIndexを設定
if (!startIndex) startIndex = 1; //startIndexが空の時、1を設定
これで、処理を途中から再開できるようになりました。
まとめ
今回は、GASを使って大きな処理を行う際の、タイムアウトによるランタイムエラーを回避する方法について紹介していきました。
今回紹介した方法を適切に組み合わせていく事で、エラーを起こすことなく処理の実行が可能になります。
是非GASの実行時間で困っている際は、こちらを活用してみてください。