Queried Claude on best practice to lock a function to prevent concurrent collision. Here was his solution:
/**
* Wrapper function that implements a lock to prevent concurrent execution
* @param {function} fn - The function to be executed with locking
* @param {number} timeout - Maximum time in seconds to wait for lock (default: 300)
* @param {string} lockName - Unique name for the lock (default: function name)
* @return {function} Wrapped function with locking mechanism
*/
function withLock(fn, timeout = 300, lockName) {
return function (...args) {
// Get script lock
const lock = LockService.getScriptLock();
const actualLockName = lockName ||
fn.name;
try {
// Try to acquire lock
const acquired = lock.tryLock(timeout * 1000);
if (!acquired) {
throw new Error(`Could not acquire lock for ${actualLockName} after ${timeout} seconds`);
}
// Execute the wrapped function
return fn.apply(this, args);
} catch (error) {
Logger.log(`Error in ${actualLockName}: ${error.message}`);
throw error;
} finally {
// Always release the lock in finally block
try {
lock.releaseLock();
} catch (e) {
// Log but don't throw - the main error is more important
Logger.log(`Error releasing lock for ${actualLockName}: ${e.message}`);
}
}
};
}
// Example usage:
function processSensitiveData() {
// Your sensitive operation here
SpreadsheetApp.getActiveSheet().getRange('A1').setValue(new Date());
}
// Wrap the function with a lock
const safeProcessData = withLock(processSensitiveData, 60, 'processData');
/**
* Function that can be triggered by multiple users/events
*/
function triggerFunction() {
try {
safeProcessData();
} catch (error) {
if (error.message.includes('Could not acquire lock')) {
// Handle lock timeout
Logger.log('Process already running, please try again later');
} else {
// Handle other errors
throw error;
}
}
}
Any thoughts? Goo? Bad? Problem areas?
Keith