スキップしてメイン コンテンツに移動

GASLockで競合回避

GASLockで競合回避

GAS応用とLockServiceの基本

Google Apps Script(GAS)は、スプレッドシートやドキュメントを自動化するだけでなく、外部APIとの連携や定期実行タスクの構築にも活用できます。GASを実務で応用する際に直面する課題の一つが、複数のユーザーやトリガーが同時にスクリプトを実行したときに発生する競合です。ここで重要になるのが、LockServiceを使った排他制御です。

LockServiceは、スクリプト全体または特定のリソースに対してロックを取得し、同時実行を防止するためのAPIです。主に3種類のロックが用意されており、今回は getScriptLock()tryLock() を中心に解説します。

function example() {
  var lock = LockService.getScriptLock();
  try {
    lock.waitLock(30000); // 30秒待機
    // ここにクリティカルセクション
  } catch (e) {
    Logger.log('ロック取得失敗: ' + e);
  } finally {
    lock.releaseLock();
  }
}

上記のように waitLock() を使うと、ロックが取得できるまで待機しますが、待機時間が長いとユーザー体験が低下します。そこで tryLock() を使うと、ロック取得に失敗した場合に即座に処理をスキップでき、非同期処理の設計に適しています。

排他制御で同時実行対策を実装する

同時実行対策は、データベース更新やファイル書き込みなど、状態を変更する処理で特に重要です。GASでは tryLock() を組み合わせて、以下のように実装します。

function updateSheet() {
  var lock = LockService.getScriptLock();
  if (!lock.tryLock(1000)) { // 1秒でロック取得を試みる
    Logger.log('別のプロセスが実行中です。');
    return;
  }
  try {
    var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Data');
    var lastRow = sheet.getLastRow() + 1;
    sheet.getRange(lastRow, 1).setValue(new Date());
    // 追加の更新処理
  } finally {
    lock.releaseLock();
  }
}

このパターンでは、ロック取得に失敗した場合に即座に処理を終了し、競合回避と安全性を確保します。さらに、ロック取得に失敗した際に再試行ロジックを組み込むことで、より堅牢な設計が可能です。

排他制御を実装する際のポイントは次の通りです。

  • ロック対象は最小限に抑える(例:特定のシートやファイルのみ)
  • ロック保持時間は短くする(処理を分割してロックを解放)
  • ロック取得失敗時のフォールバック処理を用意する

データ整合性と重複防止のベストプラクティス

排他制御を行うことで同時実行による競合を防げますが、データ整合性を保つためにはさらに工夫が必要です。特に、重複防止のロジックを組み込むことで、同じデータが複数回書き込まれるリスクを低減できます。

例えば、スプレッドシートにログを追加する際に、既に同じタイムスタンプと内容が存在するかを確認し、重複があればスキップする処理を入れます。

function logEntry(entry) {
  var lock = LockService.getScriptLock();
  if (!lock.tryLock(1000)) {
    Logger.log('ロック取得失敗。重複チェックをスキップ。');
    return;
  }
  try {
    var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Log');
    var data = sheet.getDataRange().getValues();
    var exists = data.some(function(row) {
      return row[0] === entry.timestamp && row[1] === entry.message;
    });
    if (!exists) {
      sheet.appendRow([entry.timestamp, entry.message]);
    } else {
      Logger.log('重複エントリをスキップ。');
    }
  } finally {
    lock.releaseLock();
  }
}

このように、ロックと重複チェックを組み合わせることで、データ整合性と安全性を高めることができます。また、トランザクションのように複数の操作をまとめて実行する場合は、ロックを取得した状態で全ての操作を完了させ、途中で失敗した場合はロールバック(例:書き込んだ行を削除)を行う設計が推奨されます。

まとめとして、GAS応用においては LockService を活用した排他制御が不可欠です。getScriptLock()tryLock() を適切に使い、同時実行対策、データ整合性、重複防止、競合回避、安全性を確保することで、安定したスクリプト運用が実現できます。

この記事はAIによって作成されました。

コメント