【发布时间】:2017-05-11 16:50:47
【问题描述】:
在我的 Google Apps 脚本应用程序中,有一个地方可以生成唯一的递增订单号。为此,我使用了内置的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(800)。
他还建议在释放锁的时候,提前调用Utility.sleep(300),这样脚本就有足够的时间来更新属性。
他认为发布是在属性更新之前发生的。
我将尝试实施他的建议,因为它们不会造成伤害,但我想听听关于这个问题的任何其他想法。
【问题讨论】:
-
感谢桑迪的建议。目前,如果我无法获得
tryLock的锁,我会等待一秒钟,然后再试一次,一遍又一遍。我仍然能够知道锁是否有效,因为tryLock返回一个布尔值,指示是否获得了锁:developers.google.com/apps-script/reference/lock/… -
就个人而言,我不喜欢使用 PropertiesServices 进行快速访问和编写。总是有问题,您可以尝试使用电子表格来生成您的订单号。我认为您的代码在使用锁方面没有任何问题。但是,要指出的是,有序列号是在线系统中的一个缺陷/风险(Spoofing risk),在订单号后添加一个随机字符串可以降低风险并保持订单号的唯一性
-
我同意你同事的观点,并猜测 scriptProperties.getProperty 会返回一个缓存值。因此,在您释放锁之后,可能有一段时间该值实际上尚未写入。额外的睡眠有点可怕,但可能会有所帮助,有帮助吗?
-
我不记得它是否有多大帮助,抱歉!在此之后不久,我们最终迁移到了一个新系统,其中生成唯一订单号已经融入了框架的较低级别部分。