Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/google-apps-script/6.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Multithreading 为什么我在并行调用应用程序脚本API时出现超时和文档丢失错误?_Multithreading_Google Apps Script - Fatal编程技术网

Multithreading 为什么我在并行调用应用程序脚本API时出现超时和文档丢失错误?

Multithreading 为什么我在并行调用应用程序脚本API时出现超时和文档丢失错误?,multithreading,google-apps-script,Multithreading,Google Apps Script,我正在写一个GoogleSheets插件,它将一些数据从一个电子表格复制到另一个电子表格,然后重新格式化。所涉及的数据集通常很大(约10万行),因此为了避免达到6分钟的超时限制,我将数据分成块,然后使用google.script.run客户端调用在每个块上并行运行数据复制功能 在我的样本数据集(约10万行)上,成功复制了前两个要完成的数据块,其余的数据块抛出错误“服务电子表格在访问id为[spreadsheet id]的文档时超时” 下面是它在应用程序脚本仪表板中的外观: 我被超时错误弄糊涂

我正在写一个GoogleSheets插件,它将一些数据从一个电子表格复制到另一个电子表格,然后重新格式化。所涉及的数据集通常很大(约10万行),因此为了避免达到6分钟的超时限制,我将数据分成块,然后使用google.script.run客户端调用在每个块上并行运行数据复制功能

在我的样本数据集(约10万行)上,成功复制了前两个要完成的数据块,其余的数据块抛出错误“服务电子表格在访问id为[spreadsheet id]的文档时超时”

下面是它在应用程序脚本仪表板中的外观:

我被超时错误弄糊涂了,因为:

  • 我已经在包含5000行的数据集上成功运行了脚本
  • 应用程序脚本仪表板显示6分钟前执行失败(更像4-5分钟)
  • 应用程序脚本仪表板日志记录显示失败(超时)的成功执行日志记录。日志记录发生在setValues()操作之后(参见下面的代码);日志记录之后的唯一一件事就是返回,所以我不明白它是如何成功登录然后超时的(我以为应用程序脚本是同步的……但也许我错了?)
  • 我也不确定这些“未捕获”的错误,但它们似乎显示在仪表板上,因为“文档[电子表格id]丢失(可能已删除,或者您没有读取权限”)

    这就是我要复制到的文档,我已经确认它仍然存在于我的驱动器上,我可以打开它并查看成功复制的数据。如果有太多脚本实例试图同时访问文档,文档是否会“丢失”

    我尝试过更小的块大小(1000行和2000行),得到了相同类型的错误

    下面是我的客户端Javascript的外观:

    // This function is the success handler that runs after another function (which grabs the total # of rows
    // from the sheet to be copied, and then creates the new spreadsheet to be copied into) completes
    function dataParamsSuccess(dataParameters) {
          // dataParameters = [busHrs, outputSsUrl, lastRow, maxRows, maxColumns]
          var busHrs = dataParameters[0];
          var outputSsUrl = dataParameters[1];
          var lastRow = dataParameters[2];
          var maxRows = dataParameters[3];
          var maxColumns = dataParameters[4];
          console.log(maxRows);
          console.log(maxColumns);
    
          // Set chunk size
          var chunkSize = 5000; // number of rows in chunk
    
          // Determine number of chunks
          var numChunks = Math.ceil(lastRow / chunkSize);
          var lastChunkSize = lastRow % chunkSize;
          if ((numChunks-1) * chunkSize + lastChunkSize == lastRow) {
            console.log("Math checks out");
          } else {
            console.log("oops, check your math");
          }
    
          // Generate status message
          var statusHtml = numChunks + " chunks to be copied";
          for (i=0; i<numChunks; i++) {
            var chunkNum = i+1;
            var chunkNumStr = chunkNum.toString();
            statusHtml += "<div id=\"chunk" + chunkNumStr + "Status\"></div>";
          }
          document.getElementById("statusMsg").innerHTML = statusHtml;
    
          var startRow = 1;
          // Call copyData once for each chunk
          for (i=0; i<numChunks; i++) {
            var chunkNum = i+1;
            var chunkNumStr = chunkNum.toString();
            var chunkDivId = "chunk" + chunkNumStr + "Status";
    
            if (chunkNum==numChunks) { // if this is the last chunk, chunk size is smaller
              chunkSize = lastChunkSize;
            }
    
            var copyParams = [chunkNum, chunkSize, startRow, outputSsUrl];
            google.script.run
              .withSuccessHandler(copyChunkSuccess)
              .copyData(copyParams);
            document.getElementById(chunkDivId).innerHTML = "Chunk " + chunkNumStr + " copying in progress";
            startRow += chunkSize;
            console.log("startRow: " + startRow.toString());
    
          }
    
          // Haven't gotten to the part where I figure out what to do after all chunks are complete yet
        }
    
    这个答案怎么样

    根据我的经验,即使在使用电子表格服务时,当异步进程发生连续访问时,我也遇到过这样的问题。当时,我使用了锁服务和
    setTimeout
    。但我不确定这种方法是否能解决您的问题。因此,请测试以下修改。在这里,我建议在Google应用程序脚本端使用锁服务,在Javascript端使用
    setTimeout
    。当您的脚本被修改时,它将变成如下所示

    function copyData(copyParams) {
      var lock = LockService.getDocumentLock();
      if (lock.tryLock(10000)) {
        try {
          // copyParams = [chunkNum, chunkSize, startRow, outputSsUrl]
          var chunkNum = copyParams[0];
          var chunkSize = copyParams[1];
          var startRow = copyParams[2];
          var outputSsUrl = copyParams[3];
          var lastRow = startRow + chunkSize;
    
          // Get input and output sheets
          var dataSheet = SpreadsheetApp.getActiveSheet();
          var outputSpreadsheet = SpreadsheetApp.openByUrl(outputSsUrl);
          var outputSheet = outputSpreadsheet.getActiveSheet();
    
          // Copy values
          var values = dataSheet.getRange(startRow, 1, chunkSize, 22).getValues();
          outputSheet.getRange(startRow, 1, chunkSize, 22).setValues(values);
    
          // Logging
          var dataSpreadsheetId = dataSheet.getParent().getId();
          var outputSpreadsheetId = outputSpreadsheet.getId();
          console.log("Chunk " + chunkNum.toString() + " (rows " + startRow.toString() + " through " + lastRow.toString() + ") copied successfully");
          return [chunkNum, startRow, lastRow, "success"];
        } catch(e) {
          return [chunkNum, startRow, lastRow, e.message]; // Return error to client-side; server-side logging is taking too long
        } finally {
          lock.releaseLock();
        }
      }
    }
    
    // This function is the success handler that runs after another function (which grabs the total # of rows
    // from the sheet to be copied, and then creates the new spreadsheet to be copied into) completes
    
    async function dataParamsSuccess(dataParameters) {  // <--- Modified
      const wait = (s) => new Promise(r => setTimeout(r, s));  // <--- Added
    
      // dataParameters = [busHrs, outputSsUrl, lastRow, maxRows, maxColumns]
      var busHrs = dataParameters[0];
      var outputSsUrl = dataParameters[1];
      var lastRow = dataParameters[2];
      var maxRows = dataParameters[3];
      var maxColumns = dataParameters[4];
      console.log(maxRows);
      console.log(maxColumns);
    
      // Set chunk size
      var chunkSize = 5000; // number of rows in chunk
    
      // Determine number of chunks
      var numChunks = Math.ceil(lastRow / chunkSize);
      var lastChunkSize = lastRow % chunkSize;
      if ((numChunks - 1) * chunkSize + lastChunkSize == lastRow) {
        console.log("Math checks out");
      } else {
        console.log("oops, check your math");
      }
    
      // Generate status message
      var statusHtml = numChunks + " chunks to be copied";
      for (i = 0; i < numChunks; i++) {
        var chunkNum = i + 1;
        var chunkNumStr = chunkNum.toString();
        statusHtml += "<div id=\"chunk" + chunkNumStr + "Status\"></div>";
      }
      document.getElementById("statusMsg").innerHTML = statusHtml;
    
      var count = 0;  // <--- Added
      var startRow = 1;
      // Call copyData once for each chunk
      for (i = 0; i < numChunks; i++) {
        count++;  // <--- Added
        var chunkNum = i + 1;
        var chunkNumStr = chunkNum.toString();
        var chunkDivId = "chunk" + chunkNumStr + "Status";
    
        if (chunkNum == numChunks) { // if this is the last chunk, chunk size is smaller
          chunkSize = lastChunkSize;
        }
    
        var copyParams = [chunkNum, chunkSize, startRow, outputSsUrl];
        google.script.run
          .withSuccessHandler(copyChunkSuccess)
          .copyData(copyParams);
    
        if (count == 10) {  // <--- Added
          console.log("wait");
          await wait(5000);
          count = 0;
        }
    
        document.getElementById(chunkDivId).innerHTML = "Chunk " + chunkNumStr + " copying in progress";
        startRow += chunkSize;
        console.log("startRow: " + startRow.toString());
    
      }
    
      // Haven't gotten to the part where I figure out what to do after all chunks are complete yet
    }
    
    此解决方案的流程如下所示

    function copyData(copyParams) {
      var lock = LockService.getDocumentLock();
      if (lock.tryLock(10000)) {
        try {
          // copyParams = [chunkNum, chunkSize, startRow, outputSsUrl]
          var chunkNum = copyParams[0];
          var chunkSize = copyParams[1];
          var startRow = copyParams[2];
          var outputSsUrl = copyParams[3];
          var lastRow = startRow + chunkSize;
    
          // Get input and output sheets
          var dataSheet = SpreadsheetApp.getActiveSheet();
          var outputSpreadsheet = SpreadsheetApp.openByUrl(outputSsUrl);
          var outputSheet = outputSpreadsheet.getActiveSheet();
    
          // Copy values
          var values = dataSheet.getRange(startRow, 1, chunkSize, 22).getValues();
          outputSheet.getRange(startRow, 1, chunkSize, 22).setValues(values);
    
          // Logging
          var dataSpreadsheetId = dataSheet.getParent().getId();
          var outputSpreadsheetId = outputSpreadsheet.getId();
          console.log("Chunk " + chunkNum.toString() + " (rows " + startRow.toString() + " through " + lastRow.toString() + ") copied successfully");
          return [chunkNum, startRow, lastRow, "success"];
        } catch(e) {
          return [chunkNum, startRow, lastRow, e.message]; // Return error to client-side; server-side logging is taking too long
        } finally {
          lock.releaseLock();
        }
      }
    }
    
    // This function is the success handler that runs after another function (which grabs the total # of rows
    // from the sheet to be copied, and then creates the new spreadsheet to be copied into) completes
    
    async function dataParamsSuccess(dataParameters) {  // <--- Modified
      const wait = (s) => new Promise(r => setTimeout(r, s));  // <--- Added
    
      // dataParameters = [busHrs, outputSsUrl, lastRow, maxRows, maxColumns]
      var busHrs = dataParameters[0];
      var outputSsUrl = dataParameters[1];
      var lastRow = dataParameters[2];
      var maxRows = dataParameters[3];
      var maxColumns = dataParameters[4];
      console.log(maxRows);
      console.log(maxColumns);
    
      // Set chunk size
      var chunkSize = 5000; // number of rows in chunk
    
      // Determine number of chunks
      var numChunks = Math.ceil(lastRow / chunkSize);
      var lastChunkSize = lastRow % chunkSize;
      if ((numChunks - 1) * chunkSize + lastChunkSize == lastRow) {
        console.log("Math checks out");
      } else {
        console.log("oops, check your math");
      }
    
      // Generate status message
      var statusHtml = numChunks + " chunks to be copied";
      for (i = 0; i < numChunks; i++) {
        var chunkNum = i + 1;
        var chunkNumStr = chunkNum.toString();
        statusHtml += "<div id=\"chunk" + chunkNumStr + "Status\"></div>";
      }
      document.getElementById("statusMsg").innerHTML = statusHtml;
    
      var count = 0;  // <--- Added
      var startRow = 1;
      // Call copyData once for each chunk
      for (i = 0; i < numChunks; i++) {
        count++;  // <--- Added
        var chunkNum = i + 1;
        var chunkNumStr = chunkNum.toString();
        var chunkDivId = "chunk" + chunkNumStr + "Status";
    
        if (chunkNum == numChunks) { // if this is the last chunk, chunk size is smaller
          chunkSize = lastChunkSize;
        }
    
        var copyParams = [chunkNum, chunkSize, startRow, outputSsUrl];
        google.script.run
          .withSuccessHandler(copyChunkSuccess)
          .copyData(copyParams);
    
        if (count == 10) {  // <--- Added
          console.log("wait");
          await wait(5000);
          count = 0;
        }
    
        document.getElementById(chunkDivId).innerHTML = "Chunk " + chunkNumStr + " copying in progress";
        startRow += chunkSize;
        console.log("startRow: " + startRow.toString());
    
      }
    
      // Haven't gotten to the part where I figure out what to do after all chunks are complete yet
    }
    
    流量:
  • 10名工人被派往谷歌应用程序脚本端
  • 发送10个工人后,它等待5秒钟
  • 在谷歌应用程序脚本端,收到了10名工作人员。这些都是在锁服务下处理的
  • 5秒钟后,在Javascript端,接下来的10个工人被发送
  • 在此循环中,脚本将运行

    谷歌应用程序脚本端: 请修改
    copyData
    ,如下所示

    function copyData(copyParams) {
      var lock = LockService.getDocumentLock();
      if (lock.tryLock(10000)) {
        try {
          // copyParams = [chunkNum, chunkSize, startRow, outputSsUrl]
          var chunkNum = copyParams[0];
          var chunkSize = copyParams[1];
          var startRow = copyParams[2];
          var outputSsUrl = copyParams[3];
          var lastRow = startRow + chunkSize;
    
          // Get input and output sheets
          var dataSheet = SpreadsheetApp.getActiveSheet();
          var outputSpreadsheet = SpreadsheetApp.openByUrl(outputSsUrl);
          var outputSheet = outputSpreadsheet.getActiveSheet();
    
          // Copy values
          var values = dataSheet.getRange(startRow, 1, chunkSize, 22).getValues();
          outputSheet.getRange(startRow, 1, chunkSize, 22).setValues(values);
    
          // Logging
          var dataSpreadsheetId = dataSheet.getParent().getId();
          var outputSpreadsheetId = outputSpreadsheet.getId();
          console.log("Chunk " + chunkNum.toString() + " (rows " + startRow.toString() + " through " + lastRow.toString() + ") copied successfully");
          return [chunkNum, startRow, lastRow, "success"];
        } catch(e) {
          return [chunkNum, startRow, lastRow, e.message]; // Return error to client-side; server-side logging is taking too long
        } finally {
          lock.releaseLock();
        }
      }
    }
    
    // This function is the success handler that runs after another function (which grabs the total # of rows
    // from the sheet to be copied, and then creates the new spreadsheet to be copied into) completes
    
    async function dataParamsSuccess(dataParameters) {  // <--- Modified
      const wait = (s) => new Promise(r => setTimeout(r, s));  // <--- Added
    
      // dataParameters = [busHrs, outputSsUrl, lastRow, maxRows, maxColumns]
      var busHrs = dataParameters[0];
      var outputSsUrl = dataParameters[1];
      var lastRow = dataParameters[2];
      var maxRows = dataParameters[3];
      var maxColumns = dataParameters[4];
      console.log(maxRows);
      console.log(maxColumns);
    
      // Set chunk size
      var chunkSize = 5000; // number of rows in chunk
    
      // Determine number of chunks
      var numChunks = Math.ceil(lastRow / chunkSize);
      var lastChunkSize = lastRow % chunkSize;
      if ((numChunks - 1) * chunkSize + lastChunkSize == lastRow) {
        console.log("Math checks out");
      } else {
        console.log("oops, check your math");
      }
    
      // Generate status message
      var statusHtml = numChunks + " chunks to be copied";
      for (i = 0; i < numChunks; i++) {
        var chunkNum = i + 1;
        var chunkNumStr = chunkNum.toString();
        statusHtml += "<div id=\"chunk" + chunkNumStr + "Status\"></div>";
      }
      document.getElementById("statusMsg").innerHTML = statusHtml;
    
      var count = 0;  // <--- Added
      var startRow = 1;
      // Call copyData once for each chunk
      for (i = 0; i < numChunks; i++) {
        count++;  // <--- Added
        var chunkNum = i + 1;
        var chunkNumStr = chunkNum.toString();
        var chunkDivId = "chunk" + chunkNumStr + "Status";
    
        if (chunkNum == numChunks) { // if this is the last chunk, chunk size is smaller
          chunkSize = lastChunkSize;
        }
    
        var copyParams = [chunkNum, chunkSize, startRow, outputSsUrl];
        google.script.run
          .withSuccessHandler(copyChunkSuccess)
          .copyData(copyParams);
    
        if (count == 10) {  // <--- Added
          console.log("wait");
          await wait(5000);
          count = 0;
        }
    
        document.getElementById(chunkDivId).innerHTML = "Chunk " + chunkNumStr + " copying in progress";
        startRow += chunkSize;
        console.log("startRow: " + startRow.toString());
    
      }
    
      // Haven't gotten to the part where I figure out what to do after all chunks are complete yet
    }
    
    HTML和Javascript端: 请修改
    dataparamsccess
    ,如下所示

    function copyData(copyParams) {
      var lock = LockService.getDocumentLock();
      if (lock.tryLock(10000)) {
        try {
          // copyParams = [chunkNum, chunkSize, startRow, outputSsUrl]
          var chunkNum = copyParams[0];
          var chunkSize = copyParams[1];
          var startRow = copyParams[2];
          var outputSsUrl = copyParams[3];
          var lastRow = startRow + chunkSize;
    
          // Get input and output sheets
          var dataSheet = SpreadsheetApp.getActiveSheet();
          var outputSpreadsheet = SpreadsheetApp.openByUrl(outputSsUrl);
          var outputSheet = outputSpreadsheet.getActiveSheet();
    
          // Copy values
          var values = dataSheet.getRange(startRow, 1, chunkSize, 22).getValues();
          outputSheet.getRange(startRow, 1, chunkSize, 22).setValues(values);
    
          // Logging
          var dataSpreadsheetId = dataSheet.getParent().getId();
          var outputSpreadsheetId = outputSpreadsheet.getId();
          console.log("Chunk " + chunkNum.toString() + " (rows " + startRow.toString() + " through " + lastRow.toString() + ") copied successfully");
          return [chunkNum, startRow, lastRow, "success"];
        } catch(e) {
          return [chunkNum, startRow, lastRow, e.message]; // Return error to client-side; server-side logging is taking too long
        } finally {
          lock.releaseLock();
        }
      }
    }
    
    // This function is the success handler that runs after another function (which grabs the total # of rows
    // from the sheet to be copied, and then creates the new spreadsheet to be copied into) completes
    
    async function dataParamsSuccess(dataParameters) {  // <--- Modified
      const wait = (s) => new Promise(r => setTimeout(r, s));  // <--- Added
    
      // dataParameters = [busHrs, outputSsUrl, lastRow, maxRows, maxColumns]
      var busHrs = dataParameters[0];
      var outputSsUrl = dataParameters[1];
      var lastRow = dataParameters[2];
      var maxRows = dataParameters[3];
      var maxColumns = dataParameters[4];
      console.log(maxRows);
      console.log(maxColumns);
    
      // Set chunk size
      var chunkSize = 5000; // number of rows in chunk
    
      // Determine number of chunks
      var numChunks = Math.ceil(lastRow / chunkSize);
      var lastChunkSize = lastRow % chunkSize;
      if ((numChunks - 1) * chunkSize + lastChunkSize == lastRow) {
        console.log("Math checks out");
      } else {
        console.log("oops, check your math");
      }
    
      // Generate status message
      var statusHtml = numChunks + " chunks to be copied";
      for (i = 0; i < numChunks; i++) {
        var chunkNum = i + 1;
        var chunkNumStr = chunkNum.toString();
        statusHtml += "<div id=\"chunk" + chunkNumStr + "Status\"></div>";
      }
      document.getElementById("statusMsg").innerHTML = statusHtml;
    
      var count = 0;  // <--- Added
      var startRow = 1;
      // Call copyData once for each chunk
      for (i = 0; i < numChunks; i++) {
        count++;  // <--- Added
        var chunkNum = i + 1;
        var chunkNumStr = chunkNum.toString();
        var chunkDivId = "chunk" + chunkNumStr + "Status";
    
        if (chunkNum == numChunks) { // if this is the last chunk, chunk size is smaller
          chunkSize = lastChunkSize;
        }
    
        var copyParams = [chunkNum, chunkSize, startRow, outputSsUrl];
        google.script.run
          .withSuccessHandler(copyChunkSuccess)
          .copyData(copyParams);
    
        if (count == 10) {  // <--- Added
          console.log("wait");
          await wait(5000);
          count = 0;
        }
    
        document.getElementById(chunkDivId).innerHTML = "Chunk " + chunkNumStr + " copying in progress";
        startRow += chunkSize;
        console.log("startRow: " + startRow.toString());
    
      }
    
      // Haven't gotten to the part where I figure out what to do after all chunks are complete yet
    }
    
    //此函数是在另一个函数(获取总行数)之后运行的成功处理程序
    //从要复制的工作表,然后创建要复制到的新电子表格)完成
    异步函数dataparamsccess(dataParameters){//newpromise(r=>setTimeout(r,s))//