Google Cloud Datastore事务加锁失效竞态条件问题咨询
核心结论
你对Datastore(Datastore模式Cloud Firestore)的事务锁机制存在理解偏差,测试代码本身没有结构性错误,这是竞态问题没有消失的根本原因。
事务锁的真实运行规则
- 调用
transaction.run()仅完成客户端事务上下文初始化、和服务端建立事务会话,这个阶段不会对任何实体加锁,因此两个测试事务都能顺利执行完run方法打印日志,完全符合预期。 - 事务内的
get读操作不会加阻塞式排他锁:Datastore事务默认采用快照隔离级别,读操作返回的是事务启动时刻的实体快照,多个并发事务可以同时读取同一个实体,不会互相阻塞。 - 锁校验、锁抢占的逻辑只在
commit提交阶段触发:服务端收到提交请求时,才会检查当前事务修改的实体,在事务执行周期内是否被其他已提交的事务修改过:- 没有冲突就短暂加锁、写入修改、提交完成后立刻释放锁
- 存在冲突就直接给后提交的事务返回
ABORTED冲突错误,不会排队等待前一个事务释放锁
你预期的“第一个事务拿到锁后阻塞其他事务所有操作”是典型的长周期悲观排他锁行为,Datastore本身不提供这类能力,它的锁是提交阶段的短锁,冲突直接返回失败,不会阻塞等待。
测试结果说明
你当前的测试代码里,两个事务几乎同时启动,同时拿到同一个key的快照数据,各自执行save逻辑后等待2秒再提交:
- 两个事务都能顺利执行到commit之前的所有步骤,和你实际拿到的日志完全一致,属于产品的正常表现
- 正常情况下两个几乎同时提交、修改同一个实体的事务,会有一个提交成功,另一个抛出冲突异常。你测试中两个都提交成功,大概率是测试用的实体初始不存在,或是两次写入的实体数据完全一致,没有触发冲突判定
- 你在
main函数里没加await就调用test()的写法是正确的,符合测试并发场景的目的,这部分逻辑没有问题。
竞态问题的正确修复方式
- 不要指望事务帮你阻塞排队并发请求,必须给事务逻辑添加冲突重试机制:当commit抛出冲突错误时,回滚当前事务,重新走完整的「读-改-写」流程,推荐搭配指数退避策略降低重试时的冲突概率。
- 如果你的业务场景必须要求所有操作严格串行执行、完全不接受重试,Datastore满足不了这个需求,需要额外引入分布式锁组件实现串行化控制。绝大多数业务场景下,带重试的事务足够解决读写竞态问题。
测试代码
const ds = new Datastore(); const key = ds.key(["some_kind", "some_id"]); function main() { console.log("call 1"); test(1); console.log("call 2"); test(2); } async function test(n) { const transaction = ds.transaction(); await transaction.run(); console.log("inside transaction ", n); const res = await transaction.get(key); console.log("got token ", n); transaction.save({ key: key, data: res[0] }); console.log("called save ", n); await new Promise(resolve => setTimeout(resolve, 2 * 1000)); console.log('slept ', n); await transaction.commit(); console.log("committed transaction ", n); }
实际运行日志
call 1 call 2 inside transaction 2 inside transaction 1 got token 2 called save 2 got token 1 called save 1 slept 2 slept 1 committed transaction 2 committed transaction 1
不符合实际机制的预期日志
call 1 call 2 inside transaction 2 inside transaction 1 got token 2 called save 2 slept 2 committed transaction 2 got token 1 called save 1 slept 1 committed transaction 1
项目技术栈
- node @16.14
- @google-cloud/datastore @6.6.2
- Cloud Firestore in Datastore mode
内容的提问来源于stack exchange,提问作者grkmk
相关产品推荐
相关产品推荐

