为什么Google Apps Script的LockService无法完全阻止双击导致的重复提交?
代码存在的核心问题
- 锁等待超时时间设置过短,精度不可靠
waitLock(1)设置的1毫秒超时远远低于Google Apps Script的服务调度最小精度,也无法覆盖网络延迟、实例启动的微小波动,极容易出现两个几乎同时发起的请求都通过了锁等待校验,同时拿到锁执行插入逻辑。建议将超时时间调整到300~1000毫秒,确保并发请求只有第一个能拿到锁。 - 没有主动释放锁,锁持有逻辑不严谨
你当前的代码在操作完成后没有调用lock.releaseLock()主动释放锁,虽然脚本执行结束后锁会自动失效,但如果后续还有其他逻辑运行,会导致锁被无意义持有更长时间,反而提升了后续合法请求被误拦截的概率。另外你加的Utilities.sleep(10)完全没有必要,反而会拉长锁的持有时间,加剧并发冲突概率,可以直接删除。 - 锁的获取时机太晚,缺少幂等校验兜底
你的data变量明显是在获取锁之前就完成读取赋值的,就算锁挡住了一部分并发,也存在两个请求先读取了相同的数据源、再先后抢到锁的情况,这时两个请求都会执行插入逻辑,自然就会出现重复记录。
建议在拿到锁之后、执行插入操作前,增加一层幂等校验:比如查询第6行附近的记录,判断是否已经存在和当前待插入数据唯一标识一致的记录,如果存在直接终止执行,不要插入。 - (可选)锁类型适配问题
如果这个重复插入只出现在同一用户的双击操作场景,可以将LockService.getScriptLock()换成LockService.getUserLock(),针对单个用户的并发请求做隔离,避免影响不同用户的正常操作。
修正后参考代码
//more code above // 锁获取逻辑尽量提前,避免提前读取公共可变数据 var lock = LockService.getScriptLock(); try{ lock.waitLock(500); // 调整超时时间为500毫秒,确保并发请求只有第一个能拿到锁 // 新增幂等校验,根据你的业务唯一标识调整判断逻辑即可 var existingAValue = ss.getRange("A6").getValue(); if(existingAValue === data[0][0]){ return; } // 执行插入逻辑 ss.insertRowBefore(6); ss.getRange("A6").setValue(data[0][0]); ss.getRange("B6").setValue(formatedString); ss.getRange("C6").setValue(data[1][0]); ss.getRange("D6").setValue(data[2][0]); ss.getRange("E6").setValue(data[3][0]); ss.getRange("F6").setValue(data[ref][0]); SpreadsheetApp.flush(); } catch(e){ return; } finally { // 无论执行成功还是失败,主动释放锁 if(lock.hasLock()){ lock.releaseLock(); } } //more code bellow
内容的提问来源于stack exchange,提问作者emolano
相关产品推荐
相关产品推荐

