受付番号の最後が10のとき、2つの処理が同時にその値を読むと、両方が次の番号を11にしてしまいます。書き込みだけを順番にしても、読み取りが同時なら同じ問題が残ります。番号を読むところから保存するところまでを、同じロックの内側で行います。
ここでは、同じGASプロジェクトの共有設定に置いた受付番号を、1つずつ進める例を使います。フォームの保存処理全体ではなく、共有番号の発行だけが対象です。欠番がないことや、別プロジェクト・手動編集との競合まで保証する仕組みではありません。
スクリプト全体で同じロックを使う
公式のLockServiceでは、getScriptLockは利用者をまたいで同じスクリプト内の処理を制御します。getUserLockだと別の利用者が同時に進められるため、全員で共有する番号には目的が合いません。
ロックのオブジェクトを取得しただけでは、まだ確保されていません。LockのtryLockで取得を試み、取れなければ番号を読まずに止めます。確保できた場合だけ、最後にreleaseLockで解放します。
練習用プロジェクトのスクリプトプロパティに、practice.lastNumberを文字列の0として登録します。本番の番号を入れないでください。設定が消えたときに0から再開すると重複につながるので、未設定ならエラーにする設計です。
読み取りから保存までを囲む
番号は練習用に1〜9999とし、R-0001の形へ整えます。9999の次に0へ戻す処理は入れません。
function issuePracticeNumber(lock, store) {
if (!lock.tryLock(1000)) throw new Error('番号を発行中です。結果を確認してから再試行してください');
try {
const raw = store.getProperty('practice.lastNumber');
if (raw === null || !/^(0|[1-9]\d*)$/.test(raw)) {
throw new Error('最後の受付番号を確認してください');
}
const current = Number(raw);
if (!Number.isSafeInteger(current) || current < 0 || current >= 9999) {
throw new Error('受付番号の上限を確認してください');
}
const next = current + 1;
store.setProperty('practice.lastNumber', String(next));
return 'R-' + String(next).padStart(4, '0');
} finally {
lock.releaseLock();
}
}
function previewReceiptNumber() {
const APPLY = false;
if (!APPLY) {
console.log('番号は発行しません。練習用の設定を確認してください');
return;
}
console.log(issuePracticeNumber(
LockService.getScriptLock(), PropertiesService.getScriptProperties()
));
}
既定のAPPLY:falseでは発行しません。練習用の設定が0なら、trueへ変えて1回実行した後の番号はR-0001、保存値は1になる想定です。次の実行はR-0002になります。待機時間の1000ミリ秒はこの例で決めた値です。
失敗したときに番号を戻さない
保存後、呼び出した画面へ番号を返す前に処理が止まる可能性があります。その場合、番号が進んでいても利用者は結果を受け取れません。保存値を単純に1つ戻すと、別の受付へ同じ番号を再発行するおそれがあります。
欠番を許容できない業務や、再試行で同じ受付を特定したい業務では、受付IDと発行済み番号の対応を別に保存する設計が必要です。この関数だけで、受付の重複登録と番号の重複発行を一緒に解決したとは考えないようにします。
同じ番号を使うすべてのGAS処理が、同じスクリプトロックを通ることも条件です。シートの手動編集や別プロジェクトの処理は、このロックでは止まりません。保存先の設定を手で初期化しない運用も合わせて決めます。
確認した動きと残る確認
ローカルの代用ロックと保存先で、連続発行、ロック取得失敗、未設定、不正な値、上限、保存失敗時の解放を確認しました。Google上で同時起動した結果や、プロパティ保存の障害は未実行です。最初は練習用のコピーで、番号と保存値の対応を確かめてください。
共有設定の読み書きはPropertiesService、番号を付けた後の処理状態は在庫・台帳更新の記録と分けて整理できます。
