You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Google Cloud Datastore事务加锁失效竞态条件问题咨询

核心结论

你对Datastore(Datastore模式Cloud Firestore)的事务锁机制存在理解偏差,测试代码本身没有结构性错误,这是竞态问题没有消失的根本原因。

事务锁的真实运行规则

  • 调用transaction.run()仅完成客户端事务上下文初始化、和服务端建立事务会话,这个阶段不会对任何实体加锁,因此两个测试事务都能顺利执行完run方法打印日志,完全符合预期。
  • 事务内的get读操作不会加阻塞式排他锁:Datastore事务默认采用快照隔离级别,读操作返回的是事务启动时刻的实体快照,多个并发事务可以同时读取同一个实体,不会互相阻塞。
  • 锁校验、锁抢占的逻辑只在commit提交阶段触发:服务端收到提交请求时,才会检查当前事务修改的实体,在事务执行周期内是否被其他已提交的事务修改过:
    • 没有冲突就短暂加锁、写入修改、提交完成后立刻释放锁
    • 存在冲突就直接给后提交的事务返回ABORTED冲突错误,不会排队等待前一个事务释放锁

你预期的“第一个事务拿到锁后阻塞其他事务所有操作”是典型的长周期悲观排他锁行为,Datastore本身不提供这类能力,它的锁是提交阶段的短锁,冲突直接返回失败,不会阻塞等待。

测试结果说明

你当前的测试代码里,两个事务几乎同时启动,同时拿到同一个key的快照数据,各自执行save逻辑后等待2秒再提交:

  1. 两个事务都能顺利执行到commit之前的所有步骤,和你实际拿到的日志完全一致,属于产品的正常表现
  2. 正常情况下两个几乎同时提交、修改同一个实体的事务,会有一个提交成功,另一个抛出冲突异常。你测试中两个都提交成功,大概率是测试用的实体初始不存在,或是两次写入的实体数据完全一致,没有触发冲突判定
  3. 你在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.02 21:10:09