Google Apps Script : not reliable time triggers - google-apps-script

is it allowed to chain time triggers in Google App script like this :
function doGet(e){ //first invocation by user, HTTP GET
if (e == null || e.parameters == null) {
return ContentService.createTextOutput("EMPTY");
}
saveGetParametersForUser(Session.getUser().getEmail(), e);
//trigger 10 seconds
var timeTrigger = ScriptApp.newTrigger("timeDrivenEvent").timeBased().after(10 * 1000).create();
}
function timeDrivenEvent() { //runs until there are some data in ScriptDB
Logger.log("INVOKED AT " + new Date());
removeAllPreviousTriggers(); //removes old time triggers
var somedata = loadTaskData({email: "" + Session.getUser().getEmail()});
var remainingData = processTaskData(somedata);
if(remainingData == null){
return; //we are finished here
}
removePreviousAndSaveRemainingTaskData(remainingData);
var timeTrigger = ScriptApp.newTrigger("timeDrivenEvent").timeBased().after(10 * 1000).create();
}
First invocation by user doGet()
Until all data are processed script invokes itself with 10 sec intervals (e.g. 2minutes of processing, 10 seconds nothing happens, then again 2 minutes of processing...)
size of processed data is ~ few kilobytes and processing time takes usually 1-2mins.
What happens to me that sometimes script is interrupted and data are not fully processed ! I am not getting any email alerts and nothing is in log or execution transcript - everything looks fine.
I am starting to think that maybe 10 seconds is quite quick to start script method but it`s in the API after all...
Any ideas ?
This is ONLY POSSIBLE solution how to chunk big task into smaller pieces as Google App Script cannot run for more than ~ 5-6 minutes (see quotas).

Having one periodic time trigger worked well as it was recommended in comments.
I just wonder why time trigger chaining didnt worked well ! What principle i did broke that Google App Script didnt like that.
Documentation : https://developers.google.com/apps-script/class_clocktriggerbuilder
method everyMinutes Sets the trigger to be created to fire on an interval of the passed in number of minutes which must be one of 1, 5, 10, 15 or 30.

Related

Slack command to Google Script returning Timeout Error

I setup a custom slash command to store data into a Google Spreadsheet. Everything works perfectly (the script gets triggered and does its magic) except the respond takes too long (more than the given max of 3000ms) and Slack throws me a timeout error.
simplified google script:
function doPost(request) {
//// get data from slack payload
var params = request.parameters;
//// call a function with given parameters
custom_function(params);
////
var output = {"text":"SUCCESS"};
//// respond to slacks POST request
return ContentService.createTextOutput(JSON.stringify(output)).setMimeType(ContentService.MimeType.JSON);
}
Result:
Due to the long execution time of custom_function(); the end return ContentService. ... comes too late (past 3000ms timelimit) = timeout error in slack
Additional Info: I setup delayed responses with UrlFetchApp.fetch(url,options); with the code of custom_function(); - I am receiving those responses in Slack together with the timeout error.
Question: Is there any way I DON'T have to wait until custom_function(); is finished and send some HTTP 200 OK back immediately ? The doPost(); in my case doesn't need anything from custom_function in return to finish so why wait ... ?
THANKS !
You can create a time based trigger to run the code in the future. Specifically the after method of ClockTriggerBuilder can run the code x milliseconds in the future.
https://developers.google.com/apps-script/reference/script/clock-trigger-builder#after(Integer)
function doPost(){
//execute the the script function "myFunction" 100 ms in the future
ScriptApp.newTrigger("myFunction")
.timeBased()
.after(100)
.create();
return ContentService.createTextOutput("OK");
}

Clock Trigger Builder Not calling function when scheduled - Google sheets app Script

I am using the app script provided by Google to access their prediction API through sheets. I am trying to predict thousands of rows at once, however, after 6 minutes the maximum execution time is reached at the code stops.
I implemented a solution that I found using clock trigger builder. Once I run the function it goes for 5 mins, then it stops sets a trigger to recall the function within 2 mins.
The major problem is that the function is not called when scheduled. I see it in the current triggers list, but it never gets called again. Can you please explain why this is occurring.
My intention is to predict as many lines as possible in 5 min then stop set a trigger to call the predict function again within a few minutes start where it left off and continue until ever element has been predicted.
I also need to know how would I store then values in cache so that it would know all the information that it needs when the function is called again.
//This is the function that is used to predict a selection of data
function predict() {
try {
clearOutput();
var startTime= (new Date()).getTime();
var sheet = SpreadsheetApp.getActiveSheet();
var selection = sheet.getActiveSelection();
var instances = selection.getValues();
var project_number = getProjectNumber();
var model_name = getModelName();
var startRow = stRow;
var MAX_RUNNING_TIME = 300000;
var REASONABLE_TIME_TO_WAIT = 60000;
for (var i = startRow; i < instances.length; ++i) {
var currTime = (new Date()).getTime();
if(currTime - startTime >= MAX_RUNNING_TIME) {
var builder = ScriptApp.newTrigger('predict').timeBased().after(REASONABLE_TIME_TO_WAIT);
builder.create();
break;
} else {
var result = predictSingleRow(project_number, model_name, instances[i]);
selection.getCell(i + 1, 1).setValue(result);
}
}
} catch(e) {
Browser.msgBox('ERROR:' + e, Browser.Buttons.OK);
}
}
Few things as to why your code is not functioning as intended:
1) Since you mentioned,"I see it in the current triggers list, but it never gets called again" and looking at your code, I am unsure whether you intended to call the function again after it's execution has completed. If you do, this is because your for loop runs for a while until the length of the instances is obtained. Nothing in the script suggests that the function needs to be run again once it has finished iterating through instances. Refer to this link to see how to Manage Trigger Programmatically.
2) var builder = ScriptApp.newTrigger('predict').timeBased().after(REASONABLE_TIME_TO_WAIT);
This line of your code falls under an if condition which stops the execution for 1 minute (value is 60000). Hence, adding 1 minute to the time since execution started. Nowhere are you resetting the startTime counter to the time after the waiting time since once the value of currTime - startTime has exceeded MAX_RUNNING_TIME, the function will keep calling the if loop for all iterations of the for loop after that. Simply put, if startTime was 9:35 and currTime was 9:40, after waiting for 1 minute the currTime is 9:41 which is still more than the MAX_RUNNING_TIME(5 minutes) because value of startTime still remains 9:35. Resetting it to 9:41 at this point should resolve your problem.
3) Loosing the break in the if loop would probably help fix that as well.
EDIT:
Add a function as shown in the link I mentioned above:
function callTrigger(){
ScriptApp.newTrigger('predict')
.timeBased()
.everyMinutes(30)
.create();
}
Run the function callTrigger once from your editor and you should be good to go. Remember, for minutes you can only pass values 1,5,15 or 30.

Google Spreadsheets: Script to check for completion of ImportHTML

I am trying to scrape data of a website once day automatically. In Google Spreadsheets, i use the =ImportHTML() function to import data tables, and then I extract the relevant data with a =query(). These functions take between 10 and 30 seconds to complete calculation, every time I open the spreadsheet.
I use a scheduled Google Apps Script, to copy the data into a different sheet (where it is stored, so i can run statistics) every day.
My problem is that I am having trouble to make the script wait for the calculations to be finished, before the data is copied. The Result is that my script just copies the error Message "N/A".
I tried just adding a Utilities.sleep(60000);, but it didn't work.
Is it possible to create a loop, that checks for the calculation to finish? I tried this without success:
function checkForError() {
var spreadsheet = SpreadsheetApp.getActive();
var source = spreadsheet.getRange ("Today!A1");
if (source = "N/A") {
Utilities.sleep(6000);
checkForError();
} else {
moveValuesOnly();
}
}
Locks are for this. Look up lock services in the docs. Use a public lock.
Here's how I used Zig's suggestion (combined with my own check loop) to solve my similar problem:
// Get lock for public shared resourse
var lock = LockService.getPublicLock();
// Wait for up to 120 seconds for other processes to finish.
lock.waitLock(120000);
// Load my values below
// something like sheet.getRange("A1").setFormula('= etc...
// Now force script to wait until cell D55 set to false (0) before
// performing copy / pastes
var current = SpreadsheetApp.setActiveSheet(sheet.getSheets()[1]);
var ready = 1;
var count = 0;
while (true) {
// break out of function if D55 value has changed to zero or counter
// has hit 250
if (count >= 250) break;
// otherwise keep counting...
ready = current.getRange("D55").getValue();
if (ready == 0) {count = 400;}
Utilities.sleep(100);
++count;
}
// wait for spreadsheet to finish... sigh...
Utilities.sleep(200);
// Do my copy and pastes stuff here
// for example sheet.getRange("a1:b1").copyTo(sheet.getRange("a3"), {contentsOnly:true});
// Key cells are updated so release the lock so that other processes can continue.
lock.releaseLock();
// end script
return;
}
This has worked fantastic for me, stopped Google's sporadic service from ruining my work!
Thanks goes to Zig's suggestion!

Timed script not working

I have a google script that I would like to run automatically every weekday at 8:11 am. I have set my time zones to make sure that everything is correct, but it never seems to work correctly. I am still weak at scripting. Does anyone see where I might have error in this script?
function myFunction(){
try {
var d = new Date();
if (d.getDay() == 6 || d.getDay() == 0) return;
if (d.getHours() != 08 && d.getMinutes() != 11) return; // This will stop the script from running unless it is 8:11am
} catch (e) {
MailApp.sendEmail("pthompson#ucc.on.ca", "Error report", e.message);
}
}
Thank you,
Paul
To run a function based on time, setup a project trigger (under Resources tab) with the Event "Time-driven". You can even setup a notification.
If you wanted to manage this trigger programmatically you could do something like this:
function createTimeTrigger() {
// Once a day at 8AM, near minute 11 for function 'writeSomething()'
var dailyHourNearMinute = ScriptApp.newTrigger("writeSomething")
.timeBased()
.everyDays(1)
.atHour(8)
.nearMinute(11)
.create();
}
You will notice that this creates a trigger for you that can be viewed under 'Current project's triggers'. It will say between 8AM-9AM. As far as google time events, they work well at getting it within the hour but not on the exact minute. The load in the system can also throw this off. So, the best you can really plan for is, between 8AM - 9AM.

google app script + caching or running script once a day

I have got a function (called f1 let's say) in a script which takes too many time to run (in a spreadsheet, it s use setformula transpose importrange and query..take about 2min to run).
F1 is used to combine 2 spreadsheets and set the result in some columns in the 2nd spreadsheet using setformula().
I scheduled this function to run during the night (with trigger) but everytime someone opens it, the function reruns again (processing the same data).
I try to put a void onload() function but f1 running as well even if i am not calling it.
Is there a way to run a function once a day and not when people open the spreadsheet?
Is there a way to cache the result for an entire day..until the function re run through the schedule?
Or is there a way to copy the value (not the formula contains in the cell) of an entire spreadsheet to another (i did not find this in documentation)?
or , is there any other workaround?
thanks for help
Loïc
If your function is called as a formula, it will be recalculated each time someone opens the spreadsheet and in some other circumstances too.
What you could do is, as you suggested, cache the data and return the cached data immediately. Something on these lines.
var ONE_DAY = 24 * 60 * 60 * 1000;
function f1(parameters, force){
var returnVal = CacheService.getPrivateCache().get('somevariablename');
if (force == true || returnVal == null) {
/* Do your long drawn calculation here */
CacheService.getPrivateCache.put('somevariablename, value, ONE_DAY);
}
else {
return returnVal;
}
}
And call your function in your spreadsheet as
=f1(parameters, false)