7

Is there any way to increase the 6 minute execution time limit for Google Apps Scripts? I thought that the answer might be the Early Access program for G Business Suite. I might be willing to pay $10 a month for G Business Suite, if I could get into the Early Access program, in order to increase the execution limit from 6 minutes to 30 minutes. But an Advisor from the G Suite Help Forum said that Early Access is a limited program, which means that there is no guarantee that I could get that.

Is there any other way to increase the 6 minute execution limit?

Please note that in this question I am not asking for ideas of how to optimize my scripts to fit within the 6 minute limit. (I may ask that in the future, if the answer to this current question is "No, there is no other way.")

Therefore, appropriate answers to this current question are:

  • "No, there are no other ways to increase the 6 minute execution limit for Google App Scripts."
  • "Yes, there are other ways to increase the 6 minute execution limit, and these are the ways..."
halfer
  • 19,824
  • 17
  • 99
  • 186
Garry Jost
  • 81
  • 1
  • 1
  • 3
  • Thank you very much for your answers, especially the clarification that there is no way to increase the limit. Thanks also for the info re dealing with long running scripts. I have scripts that read from a Google Doc and create Google spreadsheets with approx 1500 rows and 50 columns. I assume that it is more than can be saved in PropertiesService. I've thought in terms of a script where the user specifies start point and how many to process, and then run the script again. So I like your idea of timing it, and using a trigger to restart. Thanks again for your answers, Peter and Vytautas. – Garry Jost Feb 03 '17 at 03:05
  • This comment should be posted on the answer, not on the question. By the way see [How do comment @reply work?](https://meta.stackexchange.com/q/43019/289691) – Rubén Oct 27 '17 at 18:29

3 Answers3

8

You can workaround by using library called GASRetry.

See how to add GASRetry library to your project.

A guide to code changes (you need to apply it to your own specific scenario):

  1. Change the line var thingies = to whatever you want to process. It should ideally be an array.
  2. Add your own code after the //do our work here line
  3. Set a Trigger on the function named outerLoop() to run every x hours/days. It's OK to rename it to something meaningful to you like doProcessWidgets().

Code:

//automatically invoked from outerLoop()'s creation of a new trigger if required to get work done
function outerLoopRepeating() {
  outerLoop();
}
// trigger this function
function outerLoop() {
  try {
    var processingMessage = 'Initialising', isOverMaxRuntime = false, startTime = new Date(), // calc elapsed time
        functionName = arguments.callee.name, repeatingFunctionName = functionName + 'Repeating'; //for logging, triggering
    
    // Deletes all occurrences of the Repeating trigger we don't end up with undeleted time based triggers all over the place
    //add library GASRetry MGJu3PS2ZYnANtJ9kyn2vnlLDhaBgl_dE
    GASRetry.call(function(){ScriptApp.getProjectTriggers().forEach(function(i) {
      if (i.getHandlerFunction() === repeatingFunctionName) {ScriptApp.deleteTrigger(i);}
    });});
    
    Logger.log('========== Starting the "%s" function ==========', functionName);
    
    // Handle max execution times in our outer loop
    // Get start index if we hit max execution time last run
    var start = parseInt(PropertiesService.getScriptProperties().getProperty(functionName + "-start")) || 0;
    
    var thingies = ['stuff to process', 'in an Array',,,,]; //
    for (var i = start ; i < thingies.length; i++) {
      if (Math.round((new Date() - startTime)/1000) > 300) { //360 seconds is Google Apps Script max run time
        //We've hit max runtime. 
        isOverMaxRuntime = true;
        break;
      }
      //do our work here
      Logger.log('Inside the for loop that does the xyz work. i is currently: %d', i);
      var processingMessage = Utilities.formatString('%d of %d thingies: %s <%s>',  i+1, thingies.length, thingyName, thingyId);
      
      //do our work above here
    }
    if (isOverMaxRuntime) {
      //save state in user/project prop if required
      PropertiesService.getScriptProperties().setProperty(functionName + '-start', i);
      //create another trigger
      GASRetry.call(function(){ScriptApp.newTrigger(repeatingFunctionName).timeBased().everyMinutes(10).create();});
      Logger.log('Hit max run time - last iteration completed was i=%s', i-1);
    } else {
      Logger.log('Done all the work and all iterations');
      PropertiesService.getScriptProperties().deleteProperty(functionName + '-start');
      Logger.log('Completed processing all %s things with the "%s" function', thingies.length, functionName);
    }
  } catch (e) {
    Logger.log('%s. While processing %s', JSON.stringify(e, null, 2), processingMessage);
    throw e;
  }
}
Kos
  • 4,890
  • 9
  • 38
  • 42
Peter
  • 5,501
  • 2
  • 26
  • 42
  • 1
    To clarify the answer: there is no way to increase the time limit. The idea is to store where you are in the loop and can we continue with the script. I do urge you to not take this code and just plant it in the script and instead write your own variant as this will not be a suitable format for everyone. I know mine is quite different, as I cannot say how long on average each iteration will take, which is why I calculate the average execution time and compare it to the time left before the 6 minute mark – Vytautas Feb 01 '17 at 05:23
3

For G Suite organizations, as I understand, it's possible to eliminate maximum execution time limit by signing in to Early Access Program. From Quotas for Google Services

Flexible Quotas Early Access

This feature is part of the Early Access feature set. As such only some developers currently have access to it.

Normally, if a script execution exceeds one of the above quotas or limitations, the script execution stops and an appropriate error message is returned. This can potentially leave the script's data in an indefinite state.

Under the flexible quota system, such hard quota limits are removed. Scripts do not stop when they reach a quota limit. Rather, they are delayed until quota becomes available, at which point the script execution resumes. Once quotas begin being used, they are refilled at a regular rate. For reasonable usage, script delays are rare.

If you can't access EAP, there are several workarounds shared on this site. Some workarounds let the script to run until the error occurs, then restart it on the next item to be processed, other scripts just split the work on small enough to avoid the error pieces.

Related Q&A

Community
  • 1
  • 1
Rubén
  • 34,714
  • 9
  • 70
  • 166
0

Below is another quite flexible approach by Inclu Cat (the author of GasTerminal) which also supports long running functions.

The repository for the independent long-run implementation contains e.g. the following example. It demonstrates the separation of initializer (testInitializer), runner (testMain), and finalizer (testFinalizer) below as gas compiled version.

var exports = exports || {};
var module = module || { exports: exports };
Object.defineProperty(exports, "__esModule", { value: true });
//import {executeLongRun, LongRun} from "../LongRun";
var data;
function executeTest2() {
    var params = [];
    params.push("param1");
    params.push("param2");
    params.push("param3");
    // shorten the executable time for testing. (default is 240 seconds)
    LongRun.instance.setMaxExecutionSeconds(1);
    // execute the test
    // you can see the logs in Executions section
    executeLongRun("testMain", 3, params, "testInitializer", "testFinalizer");
}
function testInitializer(startIndex, params) {
    if (startIndex == 0) {
        console.log('*** executeLongRun started. ***');
    }
    console.log("testInitializer(startIndex=" + startIndex + "," + " params=[" + params.join(',') + "])");
    // demonstrate loading data
    data = ["data1", "data2", "data3", "data4", "data5", "data6", "data7", "data8", "data9", "data10"];
}
function testMain(index, params) {
    console.log("testMain(index=" + index + "," + " params=[" + params.join(',') + "])");
    // demonstrate the process
    console.log("  processing " + data[index] + "...");
    Utilities.sleep(1000);
}
function testFinalizer(isFinished, params) {
    console.log("testFinalizer(" + isFinished + ",[" + params.join(',') + "])");
    // demonstrate finalization
    if (isFinished) {
        console.log('--- executeLongRun finished. ---');
    }
}

I like that idea of separation for more complex cases.

GasTerminal allows long running functions as well with a similar approach, but might be overkill for some users who are just looking for long run functionality. A longer discussion of it's workaround can be seen here).

kf06925
  • 371
  • 3
  • 5