GASトリガーの6分制限と並行実行への対処
Google Apps Scriptのトリガーには6分のタイムアウトがあり、長い処理や並行実行時の重複書き込みで予期しない動作が起きる。これを検知・制御する手段がある。
トリガーの6分制限
GASのトリガーは実行時間が6分を超えるとタイムアウトで強制終了される。その時点で途中結果だけが残る場合がある。
例えば、毎分実行する everyMinutes(1) で以下のような処理があると問題になる。
function collectDataAndWrite() {
const data = fetchExternalAPI(); // 2秒
processRecords(data); // 4秒
appendToSheet(data); // 完了
}
この処理が3分以上かかる場合、複数のトリガーが並行実行される。トリガー1が書き込み中にトリガー2が同じシートに書き込むと、データが上書きされる。
並行実行による重複書き込み
トリガーの実行時間が不安定な場合、複数インスタンスが同時に走る。
function unsafeWrite() {
const sheet = SpreadsheetApp.getActiveSheet();
const lastRow = sheet.getLastRow();
sheet.getRange(lastRow + 1, 1).setValue(new Date());
// トリガー1と2が同じ行番号を計算する可能性
}
everyMinutes(1) で毎分実行され、処理が1.5分かかる場合:
- 10:00 トリガー1開始
- 10:01 トリガー2開始(トリガー1がまだ実行中)
- 10:01:30 トリガー1完了
- 10:02:30 トリガー2完了
結果として、トリガー1と2が同じ行に書き込む。
LockServiceで排他制御
GASの LockService は複数のトリガー実行を序列化する。
function safeWrite() {
const lock = LockService.getScriptLock();
if (!lock.tryLock(15000)) {
// ロック獲得失敗時は実行を中止
logError('Previous execution still running');
return;
}
try {
const sheet = SpreadsheetApp.getActiveSheet();
const lastRow = sheet.getLastRow();
sheet.getRange(lastRow + 1, 1).setValue(new Date());
} finally {
lock.releaseLock();
}
}
tryLock(15000) は15秒以内にロック獲得を試みる。獲得できない場合は false を返す。この場合、後続の処理をスキップして実行ログに記録する。
実行ログでの障害検知
GASの実行ログには各トリガー実行の時間と結果が記録される。長時間実行や重複実行はここで検知できる。
function monitorExecutionLog() {
const logger = ScriptApp.getScriptName();
const executions = ScriptApp.getProjectTriggers();
const now = new Date();
const oneHourAgo = new Date(now.getTime() - 60 * 60 * 1000);
// 実行ログをキャッシュから取得
const cache = CacheService.getScriptCache();
const lastCheck = cache.get('last_check');
if (!lastCheck || new Date(lastCheck) < oneHourAgo) {
logExecutionStats();
cache.put('last_check', now.toISOString(), 3600);
}
}
function logExecutionStats() {
const sheet = SpreadsheetApp.getActiveSheet();
const now = new Date();
sheet.appendRow([
now,
'execution_check',
'completed',
Utilities.computeDigest(
Utilities.DigestAlgorithm.SHA_256,
JSON.stringify({ timestamp: now.getTime() })
).slice(0, 8)
]);
}
実行ログシートに記録することで、トリガーが予定時刻に実行されているか、実行時間がどれほどかを監視できる。
everyMinutes vs everyHours の判断
トリガー実行時間が1分以内なら everyMinutes(1) で安全。それ以上かかる場合は everyHours(1) にして実行頻度を下げる。あるいは処理をバッチ分割する。
function createTrigger() {
// 毎分実行:処理時間が30秒未満を確認してから
ScriptApp.newTrigger('quickProcess')
.timeBased()
.everyMinutes(1)
.create();
// 毎時実行:重い処理用
ScriptApp.newTrigger('heavyProcess')
.timeBased()
.everyHours(1)
.create();
}
LockService + 実行ログの組み合わせにより、トリガーの6分制限と並行実行の問題を回避できる。前提は処理時間の測定と、ロック獲得失敗時の明示的なエラー処理。