繁体   English   中英

锁定服务并不总是锁定在 Google Apps 脚本中

[英]Lock Service not always locking in Google Apps Script

在我的 Google Apps Script 应用程序中,有一个地方可以生成唯一的递增订单号。 为了实现这一点,我使用了内置的LockServicePropertiesService

我将一个数字存储为脚本属性。 当我需要一个新的订单号时,应用程序获取当前值,增加它,并保存新值以备下次使用。 为了确保运行该应用程序的两个人不会获得相同的数字,对脚本属性的访问被放置在脚本锁或互斥锁中。

这很有效,每天有数百个调用通过该函数。 但是在过去的一个月里,两个用户最终得到了相同的订单号。

// Increment the existing value, and return our new value.
function getPropIncrementViaMutex(propId) {
  try {
    var scriptProperties = PropertiesService.getScriptProperties();
    var lock = LockService.getScriptLock();
    var success = false;
    var prevValue = null;
    var newValue = null;
    var wasSet = null;

    while (!success) {
      success = lock.tryLock(500);
      if (!success) {
        Utilities.sleep(1000);
      } else {
        prevValue = Number(scriptProperties.getProperty(propId));
        scriptProperties.setProperty(propId, prevValue + 1);
        newValue  = Number(scriptProperties.getProperty(propId));
        lock.releaseLock();
        wasSet = (newValue === (prevValue + 1));
      }
    }
    if (wasSet) {
      return newValue;
    } else {
      throw new Error("Error incrementing order number. Previous Value: " + prevValue + " New Value: " + newValue);
    }

  } catch(e) {
    if (lock) {
      lock.releaseLock();
    }
    throw e;
  }
}

我在这里做错了吗? 问题出在谷歌那边吗?

一位同事建议将lock.tryLock(500)的锁定时间lock.tryLock(500)时间,例如lock.tryLock(800) 他还建议在释放锁的时候,提前调用Utility.sleep(300) ,这样脚本就有足够的时间来更新属性。 他认为释放是在属性更新之前发生的。

我将尝试实施他的建议,因为它们不会造成伤害,但我想听听关于这个问题的任何其他想法。

这实际上是一个比想象中更简单的问题。问题出在以下代码段中:

    prevValue = Number(scriptProperties.getProperty(propId));
    scriptProperties.setProperty(propId, prevValue + 1);
    newValue  = Number(scriptProperties.getProperty(propId));

您将属性设置为 prevValue + 1,然后立即将 newValue 分配给相同的属性。 在您的代码中,这两行同步执行,但 setProperty 方法本质上是异步的。 在极少数情况下,向 Google 服务器上的 PropertiesService 写入数据可能会出现延迟。 同时,您的代码继续执行。 这可能会导致在上一行中实际更新 propId 之前分配 newValue。

解决这个问题是一个微不足道的解决方案:

function isTimeUp(startTime, milliSeconds){
  var now = new Date();
  return now.getTime() - startTime.getTime() > milliSeconds
}
.
.    
.
prevValue = Number(scriptProperties.getProperty(propId));
newValue = PrevValue + 1;
scriptProperties.setProperty(propId, newValue);
var start = new Date();
// run while loop for a maximum 30000 milliseconds = 30 seconds, to prevent endless loop
while (Number(scriptProperties.getProperty(propId))) != newValue &&
!isTimeUp(start, 30000)){};
.
.
.

直接从代码中的 prevValue 分配 newValue,而不是从属性。 不再有异步问题。
while语句和isTimeUp()函数是为了确保在释放锁之前更新属性。 这可能有点矫枉过正,但是您可以确保该函数在下一个用户运行它之前正确更新了isTimeUp() ,并且isTimeUp()函数确保在写入 PropertiesService 的互联网故障时不会出现无限循环(如果我是写到最后时不会变得懒惰,如果更新超时,我也会回滚任何效果。但现在我们开始陷入困境;)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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