在我的Google Apps脚本应用程序中,我可以生成一个唯一的递增订单号。为此,我使用了内置LockService
和PropertiesService
。
我将一个数字存储为脚本属性。当我需要一个新的订单号时,应用程序将获取当前值,递增它,并保存下一次的新值。为了确保运行应用程序的两个人没有获得相同的编号,对脚本锁或mutex的访问权限放在脚本锁中。
这很好用,每天都有数百个电话通过该功能。但在过去一个月中,两个用户最终得到了相同的订单号。
// 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(800)
。
他还建议在释放锁时,事先调用Utility.sleep(300)
,这样脚本就有足够的时间来更新属性。
他认为发布是在房产更新之前发生的。
我会尝试实施他的建议,因为他们不会受到伤害,但我想听听有关这个问题的任何其他想法。
答案 0 :(得分:1)
这实际上是一个比想象中更简单的问题。问题在以下代码部分中:
prevValue = Number(scriptProperties.getProperty(propId));
scriptProperties.setProperty(propId, prevValue + 1);
newValue = Number(scriptProperties.getProperty(propId));
您将属性设置为prevValue + 1,然后立即将newValue分配给相同的属性。在您的代码中,这两行是同步执行的,但是setProperty方法本质上是异步的。在极少数情况下,在Googles服务器上写入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()
函数用于确保在释放锁之前更新属性。可能有些过大,但是您要确保该函数确保在下一个用户运行propId之前已正确更新propId,并且isTimeUp()
函数可确保在Internet小故障写入PropertiesService的情况下没有无限循环(如果我不会在编写本书的最后就懒惰,如果更新超时我也会回滚任何效果。但是现在我们开始陷入困境;)