Summary of Code
I have a Google Apps Script project that is used by around 80 users within a specific domain, however the app is executed by me (ie Publish
> Deploy as web app
> Execute the app as
: Me
).
One of the functions of the script is to populate a Google Sheet from a custom form (using HTML Service
) and then notify myself and the submitting user (who is identified through the use of a simple login system and cookies).
It has been working fine for about 6 months, however on 1-2 occasions the notification email has been sent but the Google Sheet entry has not appeared.
I am thinking this might be due to concurrent use of the script (as two notification emails had the same timestamp) and have recently learned of Lock Service.
I am using this post to ensure I have the correct understanding of Lock
and how to implement it in order to prevent entries not appearing in the Google Sheet due to concurrent script usage.
Implementation
The pseudo code of my scenario is:
Code.gs
var active_spreadsheet = SpreadsheetApp.openById("bbb");
// BEGIN - start lock here
var lock = LockService.getScriptLock();
try {
lock.waitLock(30000); // wait 30 seconds for others' use of the code section and lock to stop and then proceed
} catch (e) {
Logger.log('Could not obtain lock after 30 seconds.');
}
var active_sheet = active_spreadsheet.getSheetByName("ENTRIES");
var new_start_row = active_sheet.getLastRow() + 1;
// Do lots of stuff - ie apply dynamic background colors based on previous entries colors, define the target range and set values, set data validations
SpreadsheetApp.flush(); // applies all pending spreadsheet changes
lock.releaseLock();
// END - end lock here
return;
Questions
01) Are the implementations of LockService
, getScriptLock()
, waitLock()
and releaseLock()
correct?
02) Is it recommended to use SpreadsheetApp.flush()
, and if so is the implementation above correct?
Terminology (for reference)
from: https://developers.google.com/apps-script/reference/lock
Lock:
A representation of a mutual-exclusion lock.
LockService:
Prevents concurrent access to sections of code.
The Lock class has 4 methods:
hasLock()
Boolean, Returns true if the lock was acquired.
releaseLock()
void, Releases the lock, allowing other processes waiting on the lock to continue.
tryLock(timeoutInMillis)
Boolean, Attempts to acquire the lock, timing out after the provided number of milliseconds.
waitLock(timeoutInMillis)
void, Attempts to acquire the lock, timing out with an exception after the provided number of milliseconds.
The LockService class has 3 methods:
getDocumentLock()
Lock, Gets a lock that prevents any user of the current document from concurrently running a section of code.
getScriptLock()
Lock, Gets a lock that prevents any user from concurrently running a section of code.
getUserLock()
Lock, Gets a lock that prevents the current user from concurrently running a section of code.
In the above pseudo code, once the script doesn't get a lock it will still proceed to run the code. Is that the intended behavior? It is a better practice or option to throw a server busy message to the user. Like so:
var active_spreadsheet = SpreadsheetApp.openById("bbb");
// BEGIN - start lock here
var lock = LockService.getScriptLock();
try {
lock.waitLock(30000); // wait 30 seconds for others' use of the code section and lock to stop and then proceed
} catch (e) {
Logger.log('Could not obtain lock after 30 seconds.');
return HtmlService.createHtmlOutput("<b> Server Busy please try after some time <p>")
// In case this a server side code called asynchronously you return a error code and display the appropriate message on the client side
return "Error: Server busy try again later... Sorry :("
}
// note: if return is run in the catch block above the following will not run as the function will be exited
var active_sheet = active_spreadsheet.getSheetByName("ENTRIES");
var new_start_row = active_sheet.getLastRow() + 1;
// Do lots of stuff - ie apply dynamic background colors based on previous entries colors, define the target range and set values, set data validations
SpreadsheetApp.flush(); // applies all pending spreadsheet changes
lock.releaseLock();
// END - end lock here
return;
Hope that helps!