【问题标题】:Does this code lock the onFormSubmit(e) method correctly?此代码是否正确锁定了 onFormSubmit(e) 方法?
【发布时间】:2015-08-26 13:04:00
【问题描述】:

我有一个 Google 电子表格脚本函数 onFormSubmit(e),只要有人提交我的表单,触发器就会调用它。

在函数中,我创建模板文档的临时副本,并根据提交的表单值在其中搜索和替换。 然后将该临时副本转换为 pdf 并通过电子邮件发送到多个电子邮件地址,然后删除。

现在我已经读到这里的锁定可能是一个问题,所以我决定获取一个LockService.getScriptLock(),并将我的脚本包装在一个锁中,首先尝试获得一个锁 30 秒 lock.waitLock(30000),然后释放脚本末尾的锁lock.releaseLock() 以防止并发问题。

当有人提交表单时,脚本本身似乎永远不会运行超过 13 秒。

所以总而言之,代码看起来有点像这样:

function onFormSubmit(e) {
    // Get a public lock on the script
    var lock = LockService.getScriptLock();
    try {
      lock.waitLock(30000);  // Wait for 30 seconds

      // copy template, search and replace etc...

      lock.releaseLock()

    }catch(e) {
      Logger.log('Could not obtain lock after 30 seconds.');
      MailApp.sendEmail("admin@something.com", "Could not obtain lock after 30 seconds.");
    }
}

还有什么我应该做的吗?我锁定太多还是太少?我记得在我的 Java 时代,将全局应用程序会话上下文锁定太久是一种罪过。还有如何制作模板副本,并打开模板副本进行搜索和替换,这是否需要某种锁定?

【问题讨论】:

    标签: google-apps-script google-sheets locking google-forms


    【解决方案1】:

    根据您所描述的性能时序,您可能会发现您可以摆脱所展示的功能。至少,直到由于您无法控制的因素而导致处理时间延长的那一天。

    你的锁处理的基本结构是好的,但是你在try..catch里面做的太多了。请注意,任何异常都会被捕获,但您的处理仅限于 waitlock() 超时异常,因此您应避免在 try 块中生成任何其他可能产生异常的语句强>。

    您应该如何重构您的函数:

    function onFormSubmit(e) {
      // Perform any "pre" operations on private
      // or non-critical shared resources.
      var sheet = e.range.getSheet();   // for example, accessing event object
    
      // Get a public lock on the script
      var lock = LockService.getScriptLock();  // Choose appropriate scope
      try {
        // just attempt to get the lock here - nothing else that may
        // throw an exception.
        lock.waitLock(30000);  // Wait for 30 seconds
      }catch(e) {
        // Handle lock exception here
        Logger.log('Could not obtain lock after 30 seconds.');
        MailApp.sendEmail("admin@something.com", "Could not obtain lock after 30 seconds.");
      }
    
      ////// Critical section begins   vvvvv
    
      // operate only on shared modifiable data
    
      ////// Critical section ends     ^^^^^
      lock.releaseLock()
    
      // Continue with operations on private
      // or non-critical shared resources.
    
      // Ensure the lock is released before exiting.
      if (lock.hasLock()) {
        throw new Error("Lock violation");
      }
      else {
        return;
      } 
    }
    

    Lock Service 提供了一种隔离critical section 代码的方法。临界区是我们控制对共享资源的访问的地方,这些共享资源必须是稳定的读写。这里的原理可以总结如下:

    • 尽可能推迟进入临界区。 任何可以在临界区之外执行而不依赖于它的操作都应该事先完成。这不是一个硬性规定,但根据我的经验,我发现它支持下一个目标的纪律,迫使你(或下一个开发人员)预先分析新的代码行是属于还是属于的关键部分。
    • 为锁选择合适的范围。这将取决于封装在关键部分的资源操作的类型。
    • 一旦进入关键部分,请尽快退出。通过将自己限制为仅依赖于“共享可修改数据”(也称为“关键资源”)的操作来实现这一目标。
    • 限制在临界区之后执行的操作。 同样,这是一个纪律。在关键部分之外,我们应该再次管理私有或非关键共享资源。但在这里我们进一步限制自己,仅限于依赖于关键部分发生的操作。我们的目标是在退出临界区后尽快退出函数。
    • 释放锁,并仔细检查。 如果我们在从函数返回时不小心留下了锁,只有在执行脚本退出或被杀死时(例如 6 分钟后)才会释放。这是通过在我们的releaseLock()hasLock() 验证检查之间结束后关键部分代码来完成的。

    我记得在我的 Java 时代,将全局应用程序会话上下文锁定太久是一种罪过...

    在 Google Apps 脚本中,我们没有这个概念;没有全局应用程序会话。脚本可以异步执行,可能为一个用户提供多个实例,也可能为其他用户提供多个实例。在这种环境中,关注点转移到仅在适当范围进行锁定,以限制锁定的可能影响。

    ...如何制作模板副本,然后打开模板副本进行搜索和替换,这是否需要某种锁定?

    • 复制模板不需要关键部分,因为我们从稳定的共享资源(模板)中读取数据以创建(可能)私有资源。 (除非模板本身可以被脚本修改。)
    • 打开该私有副本以进行搜索和替换 - 不重要,不需要锁定。

    【讨论】:

      猜你喜欢
      • 2020-02-25
      • 1970-01-01
      • 2011-08-29
      • 2010-11-22
      • 1970-01-01
      • 2023-03-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多