-
-
Save patt0/8395003 to your computer and use it in GitHub Desktop.
| /** | |
| * --- Continous Execution Library --- | |
| * | |
| * Copyright (c) 2013 Patrick Martinent | |
| * | |
| * Licensed under the Apache License, Version 2.0 (the "License"); | |
| * you may not use this file except in compliance with the License. | |
| * You may obtain a copy of the License at | |
| * | |
| * http://www.apache.org/licenses/LICENSE-2.0 | |
| * | |
| * Unless required by applicable law or agreed to in writing, software | |
| * distributed under the License is distributed on an "AS IS" BASIS, | |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| * See the License for the specific language governing permissions and | |
| * limitations under the License. | |
| */ | |
| /************************************************************************* | |
| * Call this function at the start of your batch script | |
| * it will create the necessary UserProperties with the fname | |
| * so that it can keep managing the triggers until the batch | |
| * execution is complete. It will store the start time for the | |
| * email it sends out to you when the batch has completed | |
| * | |
| * @param {fname} str The batch function to invoke repeatedly. | |
| */ | |
| function startOrResumeContinousExecutionInstance(fname){ | |
| var userProperties = PropertiesService.getUserProperties(); | |
| var start = userProperties.getProperty('GASCBL_' + fname + '_START_BATCH'); | |
| if (start === "" || start === null) | |
| { | |
| start = new Date(); | |
| userProperties.setProperty('GASCBL_' + fname + '_START_BATCH', start); | |
| userProperties.setProperty('GASCBL_' + fname + '_KEY', ""); | |
| } | |
| userProperties.setProperty('GASCBL_' + fname + '_START_ITERATION', new Date()); | |
| deleteCurrentTrigger_(fname); | |
| enableNextTrigger_(fname); | |
| } | |
| /************************************************************************* | |
| * In order to be able to understand where your batch last executed you | |
| * set the key ( or counter ) everytime a new item in your batch is complete | |
| * when you restart the batch through the trigger, use getBatchKey to start | |
| * at the right place | |
| * | |
| * @param {fname} str The batch function we are continuously triggering. | |
| * @param {key} str The batch key that was just completed. | |
| */ | |
| function setBatchKey(fname, key){ | |
| var userProperties = PropertiesService.getUserProperties(); | |
| userProperties.setProperty('GASCBL_' + fname + '_KEY', key); | |
| } | |
| /************************************************************************* | |
| * This function returns the current batch key, so you can start processing at | |
| * the right position when your batch resumes from the execution of the trigger | |
| * | |
| * @param {fname} str The batch function we are continuously triggering. | |
| * @returns {string} The batch key which was last completed. | |
| */ | |
| function getBatchKey(fname){ | |
| var userProperties = PropertiesService.getUserProperties(); | |
| return userProperties.getProperty('GASCBL_' + fname + '_KEY'); | |
| } | |
| /************************************************************************* | |
| * When the batch is complete run this function, and pass it an email and | |
| * custom title so you have an indication that the process is complete as | |
| * well as the time it took | |
| * | |
| * @param {fname} str The batch function we are continuously triggering. | |
| * @param {emailRecipient} str The email address to which the email will be sent. | |
| * @param {customTitle} str The custom title for the email. | |
| */ | |
| function endContinuousExecutionInstance(fname, emailRecipient, customTitle){ | |
| var userProperties = PropertiesService.getUserProperties(); | |
| var end = new Date(); | |
| var start = userProperties.getProperty('GASCBL_' + fname + '_START_BATCH'); | |
| var key = userProperties.getProperty('GASCBL_' + fname + '_KEY'); | |
| var emailTitle = customTitle + " : Continuous Execution Script for " + fname; | |
| var body = "Started : " + start + "<br>" + "Ended :" + end + "<br>" + "LAST KEY : " + key; | |
| MailApp.sendEmail(emailRecipient, emailTitle, "", {htmlBody:body}); | |
| deleteCurrentTrigger_(fname); | |
| userProperties.deleteProperty('GASCBL_' + fname + '_START_ITERATION'); | |
| userProperties.deleteProperty('GASCBL_' + fname + '_START_BATCH'); | |
| userProperties.deleteProperty('GASCBL_' + fname + '_KEY'); | |
| userProperties.deleteProperty('GASCBL_' + fname); | |
| } | |
| /************************************************************************* | |
| * Call this function when finishing a batch item to find out if we have | |
| * time for one more. if not exit elegantly and let the batch restart with | |
| * the trigger | |
| * | |
| * @param {fname} str The batch function we are continuously triggering. | |
| * @returns (boolean) wether we are close to reaching the exec time limit | |
| */ | |
| function isTimeRunningOut(fname){ | |
| var userProperties = PropertiesService.getUserProperties(); | |
| var start = new Date(userProperties.getProperty('GASCBL_' + fname + '_START_ITERATION')); | |
| var now = new Date(); | |
| var timeElapsed = Math.floor((now.getTime() - start.getTime())/1000); | |
| return (timeElapsed > 270); | |
| } | |
| /* | |
| * Set the next trigger, 7 minutes in the future | |
| */ | |
| function enableNextTrigger_(fname) { | |
| var userProperties = PropertiesService.getUserProperties(); | |
| var nextTrigger = ScriptApp.newTrigger(fname).timeBased().after(7 * 60 * 1000).create(); | |
| var triggerId = nextTrigger.getUniqueId(); | |
| userProperties.setProperty('GASCBL_' + fname, triggerId); | |
| } | |
| /* | |
| * Deletes the current trigger, so we don't end up with undeleted | |
| * time based triggers all over the place | |
| */ | |
| function deleteCurrentTrigger_(fname) { | |
| var userProperties = PropertiesService.getUserProperties(); | |
| var triggerId = userProperties.getProperty('GASCBL_' + fname); | |
| var triggers = ScriptApp.getProjectTriggers(); | |
| for (var i in triggers) { | |
| if (triggers[i].getUniqueId() === triggerId) | |
| ScriptApp.deleteTrigger(triggers[i]); | |
| } | |
| userProperties.setProperty('GASCBL_' + fname, ""); | |
| } |
| function testCBL() { | |
| // simulate a trigger on batch process, i.e if you run the batch | |
| // everyday at a particular time | |
| var triggerId = ScriptApp.newTrigger("batchProcess").timeBased().after(60 * 1000).create(); | |
| // clean test by deleting it the initial triggers | |
| Utilities.sleep(90000); | |
| ScriptApp.deleteTrigger(triggerId); | |
| } | |
| function batchProcess() { | |
| // initiate CBL for the function | |
| CBL.startOrResumeContinousExecutionInstance("batchProcess"); | |
| // this is approach is valid is we are looking to process a for loop | |
| // this is because the key start value is "" | |
| if (CBL.getBatchKey("batchProcess") === "") | |
| CBL.setBatchKey("batchProcess", 0); | |
| // find out where we left off (again with a for loop) | |
| var counter = Number(CBL.getBatchKey("batchProcess")); | |
| for(var i = 0 + counter; i < 80; i++) { | |
| // perform batch | |
| Utilities.sleep(5000); | |
| Logger.log("batchProcess_" + i) | |
| CBL.setBatchKey("batchProcess", i); | |
| // find out wether we have been running this iteration for more that 5 minutes | |
| // in which case exit the batch elegantly | |
| if (CBL.isTimeRunningOut("batchProcess")) | |
| return; | |
| } | |
| // if we get to this point, it means we have completed the batch and we can cleanup | |
| // this will also send an email with the last batch key, start and end times | |
| CBL.endContinuousExecutionInstance("batchProcess", "you@example.com", "My Custom Email Title"); | |
| } |
@patt0 - you say, "have a look at the blog posts that describes how this works"
where are the blog posts?
@patt0 - we would very much like to implement this on our geocoding script. would you please have look and advise?
mlucool/geocode-google-sheets#1
@patt0 - I changed the CBL you have provided a bit in my project I am working on. I have forked your gist and edited the code, especially the delete function. Would you mind taking a look at what I've added? Thanks for the code! It is very helpful.
Thank you for this library!
Awesome library, thank you! When I was using it I see that the UserProperties API is deprecated. Are there any plans to work in an alternative solution to this library?
Please i need help on how to use this
This is a really great Library and a great help! I suggest adding
CBL.setBatchKey(fname, i + 1);
in cblTest.js between lines 31 and 32 in order to avoid processing the same data twice.
It looks like this:
if (CBL.isTimeRunningOut("batchProcess")) {
CBL.setBatchKey(fname, i + 1);
return;
}
and of course designating ContinuousBatchLibrary as CBL.
Looks like you will need to update to version 4 for the deprecated userProperties stuff:
https://developers.google.com/apps-script/reference/properties/
Looks like an easy fix of: getUserProperties() then getProperties() or setProperties() on that fetched properties set.
We now get this message when using the library:
UserProperties API is deprecated.
File: Code Line: 54
The API has been marked as deprecated which means that the feature should be avoided and may be removed in the future. Consider using an alternative solution.