[英]Lock Service not always locking in Google Apps Script
在我的 Google Apps Script 应用程序中,有一个地方可以生成唯一的递增订单号。 为了实现这一点,我使用了内置的LockService
和PropertiesService
。
我将一个数字存储为脚本属性。 当我需要一个新的订单号时,应用程序获取当前值,增加它,并保存新值以备下次使用。 为了确保运行该应用程序的两个人不会获得相同的数字,对脚本属性的访问被放置在脚本锁或互斥锁中。
这很有效,每天有数百个调用通过该函数。 但是在过去的一个月里,两个用户最终得到了相同的订单号。
// 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.