当脚本部署为Web应用程序时,锁定文档上的脚本执行

时间:2016-01-19 13:32:26

标签: concurrency google-apps-script google-docs

我的Google Apps脚本部署为网络应用,任何用户都可以访问。它的功能是打开和更改该文档中的文本。

我发送脚本document ID作为查询参数,如下所示:

https://script.google.com/a/macros/s/AKfycbzCP...TnwHUbXxzDM/exec?docId=1_cMN0nuJadBw6QVjKtdEA6eXhE8ubIoxIJai1ticxnE`

Web应用程序打开文档并更改文档中的文本。

function doGet(e){
  var params=e.parameters;
  var doc = DocumentApp.openById(params['docId']);
  ...
  /* change text of the document */
}

问题

现在,当有多个用户试图同时在同一文档上运行应用程序脚本时,Web应用程序无法处理并发和功能中断。

我查看了Lock Service但锁定服务的document lock仅适用于容器绑定脚本,而不适用于网络应用。

然后我尝试使用cache service var cache = CacheService.getDocumentCache();property service var documentProperties = PropertiesService.getDocumentProperties();为文档设置属性,但文档属性和文档缓存在Web应用中返回null仅限于容器绑定脚本,如documentation

中所述
  

如果在包含的上下文之外调用此方法   文档(例如来自独立脚本或Web应用程序),此方法   返回null。

当Google Apps脚本部署为Web应用程序时,是否有任何方法可以处理文档中脚本执行的并发性。 (不是容器绑定)

1 个答案:

答案 0 :(得分:2)

正如@azawaza指出的那样,你应该使用具有适当范围的Lock,并且Script Lock更适合你的场景。这在Does this code lock the onFormSubmit(e) method correctly?

中讨论

如果代码的关键部分足够快,那么在对文档1的另一次更新继续进行时,没有真正关心使用户更新文档2等待;他们不会等很久。类似的东西:

function doGet1(e){
  // Perform any "pre" operations on private
  // or non-critical shared resources.
  var params=e.parameters;

  // Get a script lock, because we're about to modify a shared resource.
  var lock = LockService.getScriptLock();
  // Wait for up to 10 seconds for other processes to finish.
  lock.waitLock(10000);

  ////// Critical section begins   vvvvv

  var doc = DocumentApp.openById(params['docId']);

  // change text of the document here

  doc.saveAndClose();

  ////// Critical section ends     ^^^^^
  lock.releaseLock();

  // Continue with operations on private
  // or non-critical shared resources.

  return ContentService.createTextOutput("Document updated.")
}

特定资源锁

Google Apps脚本锁服务开箱即用,旨在保护代码的关键部分。如果我们想要控制对特定资源(可能是长时间)的访问(例如Google文档),我们可以通过更改我们的内容来修改它"锁定"。

在此示例中,Lock服务保护检查和更新“脚本属性”的关键部分。这些属性有"键"符合我们的docId参数;价值并不重要,因为我们可以使用简单的密钥存在作为我们的测试。

注意:目前,此脚本可能会永久阻止用户" (如果脚本超时),如果另一个脚本无法删除保护其共享文档使用的属性。您希望在生产代码中更加小心。

function doGet2(e){
  // Perform any "pre" operations on private
  // or non-critical shared resources.
  var params=e.parameters;

  // Wait for exclusive access to docId
  var ready = false;
  // Get a script lock, because we're about to modify a shared resource.
  var lock = LockService.getScriptLock();

  while (!ready) {
    // Wait for up to 1 second for other processes to finish.
    if (lock.tryLock(1000)) {
      ////// Critical section begins   vvvvv      

      var properties = PropertiesService.getScriptProperties();

      // If nobody has "locked" this document, lock it; we're ready.
      if (properties.getProperty(docId) == null) {
        // Set a property with key=docId.
        properties.setProperty(docId,"Locked"); 
      }

      ////// Critical section ends     ^^^^^
      lock.releaseLock();
    }
  }

  // We have exclusive access to docId now.

  var doc = DocumentApp.openById(params['docId']);

  // change text of the document here

  doc.saveAndClose();

  // Delete the "key" for this document, so others can access it.
  properties.deleteProperty(docId); 

  return ContentService.createTextOutput("Document updated.")
}

命名锁

我们在前一个示例中使用的逻辑可以封装到Object中,以提供更优雅的界面。事实上,布鲁斯麦克弗森用cNamedLock Library完成了他的Desktop Liberation site。使用该库,您可以实现特定于文档的锁定:

function doGet3(e){
  // Perform any "pre" operations on private
  // or non-critical shared resources.
  var params=e.parameters;

  // Get a named lock.
  var namedLock = new NamedLock().setKey(docId);

  namedLock.lock();
  ////// Critical section begins   vvvvv      

  // We have exclusive access to docId now.

  var doc = DocumentApp.openById(params['docId']);

  // change text of the document here

  doc.saveAndClose();

  ////// Critical section ends     ^^^^^
  namedLock.unlock();

  return ContentService.createTextOutput("Document updated.")
}