简体   繁体   中英

Exceeded maximum execution time

Here's my code do:

  1. Replicates a spreadsheet and dump it to a gdrive folder (no. of replications depends on the number of employees on my config file). As of the moment, I have 800+ employees.
  2. After replication, each spreadsheet in the gdrive folder are published to html. (i used a loop here)
  3. Get the pubHTML link per spreadsheet and put it in my config file.

I'm getting the exceeded maximum execution time when I'm on the step 3 part which is the to get the pubHTML and put it in my config file.

My code is working as expected. I want to avoid the maximum limit of execution error.

  function replicateCards() {
  var ss = SpreadsheetApp.openById('configfile--xxxx');
  var copyCard = SpreadsheetApp.openById('replicateCard-xxxx');
  var getID = DriveApp.getFileById(copyCard.getId())
  var card = copyCard.getSheetByName("Card");
  var mastersheet = ss.getSheetByName("Mastersheet");
  var getLastRow = mastersheet.getLastRow();
  var destinationFolder = DriveApp.getFolderById('gdrivefolder-xxxx');
  var changeColorToGrayList = card.getRangeList(['C7', 'E7', 'G7', 'I7', 'K7', 'M7', 'O7', 'Q7',
                                                 'C9', 'E9', 'G9', 'I9', 'K9', 'M9', 'O9', 'Q9',
                                                 'C11', 'E11', 'G11', 'I11', 'K11', 'M11', 'O11', 'Q11']);
  var setValueToZero = card.getRangeList(['C8', 'E8', 'G8', 'I8', 'K8', 'M8', 'O8', 'Q8',
                                          'C10', 'E10', 'G10', 'I10', 'K10', 'M10', 'O10', 'Q10',
                                          'C12', 'E12', 'G12', 'I12', 'K12', 'M12', 'O12', 'Q12']);
  for (i = 1; i < getLastRow; i++) {
    var badgeStatus = mastersheet.getRange(i + 1, 5).getValue();
    if (badgeStatus == "") {
      var employeeNumber = mastersheet.getRange(i + 1, 1).getValue();
      var employeeName = mastersheet.getRange(i + 1, 2).getValue();
      copyCard.getRange("H3").setValue(employeeNumber);
      copyCard.getRange("C3").setValue(employeeName);
      SpreadsheetApp.flush();
      if(mastersheet.getRange(1 + i, 5).getValue() != "completed"){
        getID.makeCopy(employeeNumber, destinationFolder);
        mastersheet.getRange(1 + i, 5).setValue("completed");
      }
      //          Logger.log(i);
      SpreadsheetApp.flush();

    }
  }
  var files = DriveApp.getFolderById(SpreadsheetApp.openById("configFile-xxxx").getSheetByName("Config Sheet").getRange("B1").getValue()).getFiles();
  //var fileIter = 1;
  var employeeNumbers = mastersheet.getRange("A2:A").getValues();
  var employeeNumbersTrunc = []
  for(var i = 0; i < employeeNumbers.length; i++){
    if(employeeNumbers[i][0] != "")
      employeeNumbersTrunc.push("" + employeeNumbers[i][0]);
  }
  Logger.log(employeeNumbersTrunc);

  while (files.hasNext()) {
    var file = files.next();
    /*var Found = false;
    for (var j = 0; j < ; fileIter++) {
      if (employeeNumber2[j][0] == file.getName()) {
        Found = true;
      }
    }//*/
    if (employeeNumbersTrunc.indexOf(file.getName())==-1) {
      continue;
    }else if(mastersheet.getRange(2 + (employeeNumbersTrunc.indexOf(file.getName())), 9).getValue() != ""){
      continue;
    }
    try {
      var fileId = file.getId();
      var fileName = file.getName();
      var revisions = Drive.Revisions.list(fileId);
      var lastRevisionId = revisions.items[revisions.items.length - 1].id;
      // get the resource and set the publish parameters
      var resource = Drive.Revisions.get(fileId, lastRevisionId);
      //       Logger.log(resource);
      resource.published = true;
      resource.publishAuto = true;
      resource.publishedOutsideDomain = true;
      // publish to the web
      Drive.Revisions.update(resource, fileId, lastRevisionId);
      SpreadsheetApp.flush();
      var openByID = SpreadsheetApp.openById(fileId);
      SpreadsheetApp.flush();
      var googleDriveSheet = openByID.getUrl().replace("edit", "pubhtml"); // or replace("edit", "pub");
      SpreadsheetApp.flush();
      Logger.log(file.getName());
      Logger.log(employeeNumbersTrunc.indexOf(file.getName()));
      mastersheet.getRange(2 + (employeeNumbersTrunc.indexOf(file.getName())), 9).setValue(googleDriveSheet);
      SpreadsheetApp.flush();
    } catch (err) {
      Logger.log(err);
    }
  }
}

Run the code one time only without the exceeded maximum execution time error. Any suggestions?

The code below is modified from what I've used before. An approach like this allows you to fully execute a batch that alone cannot be done within the execution time limit . By knowing how long a single operation in the batch takes, you can save your progress in the Properties before reaching that limit and programmatically create a trigger to resume processing.

You will need to do a few things to make it work:

  1. You need to modify my getEmployees_() function to accurately get the employees from your config file.
  2. As I mentioned in my comment, you need to refactor your code so that you can execute the replication for a single employee. That code should go in my replicateForEmployee_() function.
  3. Run the determineTimes() function to see how long getting employees & file replication takes.
  4. Insert the results of determineTimes() in the replicate() function.

The output of determineTimes() will be visible in the Executions page (View > Executions). Expand the execution row and you will see the durations as pictured.

执行日志

/**
 * Get all of the employees in the sheet. Assumes row in the sheet is one employee. Adjust the range as needed.
 * @returns {Object[][]}
 */
function getEmployees_() {
  var source = SpreadsheetApp.openById("configfile--xxxx");
  var sheet = source.getSheetByName("Mastersheet");
  return sheet.getRange(1, 1, sheet.getLastRow(), sheet.getLastColumn()).getValues();
}

/**
 * Refactor your current code so that this function will replicate all necessary files for a single
 * employee. The function will be called for every single employee.
 * @param {Object[]} employee - The row representing the employee in your config file
 */
function replicateForEmployee_(employee) {
  // Execute your replication process for a SINGLE employee here
}

/**
 * Run this, and then examine your script executions (View > Executions) to see how long each
 * function takes. The resulting durations should replace the corresponding values in the replicate()
 * function (I would recommend rounding up, so 458 --> 500ms).
 */
function determineTimes() {
  console.time("timeToGetEmployees");
  var employees = getEmployees_(); // Get all of the employees
  console.timeEnd("timeToGetEmployees");

  console.time("replicationDuration");
  replicateForEmployee_(employees[0]); // Execute replication once
  console.timeEnd("replicationDuration");
}

/**
 * Replicates the files. When approaching the time limit, will save location and set a trigger to
 * continue from stopping point. Emails user upon completion.
 * @param {Boolean} isFromTrigger
 * @returns {undefined}
 */
function replicate(isFromTrigger) {
  var employees = getEmployees(); // Get all of the employees
  var timeToGetEmployees = 1000; // Assume 1 second to execute getEmployees()
  var replicationDuration = 500; // Number of milliseconds it takes to execute replication for one employee.
  var maxExecutionTime = 360000 - timeToGetEmployees; // 6 mins - time already used to get employees

  var continueFrom = 0; // If first execution, will be zero. Otherwise, get the index to start from.
  if (isFromTrigger === true) {
    continueFrom = parseInt(PropertiesService.getScriptProperties().getProperty("continueFrom"), 10);
  }
  var lapsedTime = 0; // ESTIMATED amount of time that has lapsed since the replication started

  for (var i = continueFrom; i < employees.length; i++) {
    // Replicate files for the employee
    replicateForEmployee_(employees[i]);

    // Checks how much time you have left
    lapsedTime += replicationDuration;
    if (lapsedTime >= (maxExecutionTime - replicationDuration)) { // warn just before timing out
      PropertiesService.getScriptProperties().setProperty("continueFrom", i + 1); // save the last iterator value + 1
      createAfterTrigger("replicateFromTrigger", 60000); // Start again in one minute
      console.info("About to time out. Last iterator: " + i);
      return;
    }
  }

  deleteAllTriggers(); // Cleanup any lingering triggers
  PropertiesService.getScriptProperties().deleteProperty("continueFrom"); // Clean up properties

  // Send completion email
  MailApp.sendEmail(Session.getEffectiveUser().getEmail(), "Replication Complete", "");
}

/**
 * Should ONLY ever be called from a clock trigger.
 * @returns {undefined}
 */
function replicateFromTrigger() {
  replicate(true);
}

/**
 * Create a new trigger to execute the specified function after a certain amount of time.
 * @param {String} functionName - The name of the function to execute
 * @param {Number} milliseconds - The duration (in milliseconds) after the current time when the trigger should run
 * @returns {undefined}
 */
function createAfterTrigger(functionName, milliseconds) {
  ScriptApp.newTrigger(functionName)
  .timeBased()
  .after(milliseconds)
  .create();
}

/**
 * Deletes all triggers in the current project.
 * @returns {undefined}
 */
function deleteAllTriggers() {
  var triggers = ScriptApp.getProjectTriggers();
  for (var i = 0; i < triggers.length; i++) {
    ScriptApp.deleteTrigger(triggers[i]);
  }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM