MongoDB 事务:WriteConflict 错误

ami*_*pra 3 transactions mongodb spring-boot

环境

  • MongoDB:(阿特拉斯 4+)
  • 驱动程序:4.2.3
  • 我们正在使用 Spring Boot Data MongoDB (2.5.4)

我们使用回调 API 来使用 MongoDB 事务。Core API 也会发生同样的情况。

样本

    private ThreadLocal<MongoOperations> sessionMongoTemplate;

    try (final ClientSession session = getClientSession()) {
       this.sessionMongoTemplate = ThreadLocal.withInitial(() -> getMongoOperations().withSession(session));

       session.withTransaction(() -> {
           // do some work
           // call a method which does (sessionMongoTemplate.get().insert(entity);) 
           // (this throws WriteConflict Error)
       });
   } 
Run Code Online (Sandbox Code Playgroud)

错误

com.mongodb.MongoCommandException:命令失败,错误 112(WriteConflict):'WriteConflict 错误:此操作与另一个操作冲突。请重试您的操作或多文档事务。” 在服务器 abc.mongodb.net:1234 上。完整响应为 { "errorLabels": [ "TransientTransactionError" ], "ok": 0, "errmsg": "WriteConflict 错误:此操作与另一个操作冲突。请重试您的操作或多文档事务。", "code ": 112, "codeName": "写入冲突"

查询:
我们已经将 MongoOperations/Template 与 ClientSession 关联起来。该会话打开了一个交易。但是,当代码使用 SessionScoped MongoOperations 时,它会失败,就好像它不知道打开的事务一样。

我们能不使用这种方法吗?

根据 交易主体中的https://docs.mongodb.com/v4.2/core/transactions-in-applications/中的示例 ,我们使用了 MongoClient 并且工作正常。我们可以不使用SessionScoped MongoTemplate吗?

bar*_*ker 5

所以,经过一番挖掘后,我认为这就是正在发生的事情......

对于数据库操作(命令,例如插入或更新),如果不在 ACID 事务中,冲突操作(示例中的第二个会话)将在当前操作提交后阻塞并重试。请记住,除了 ACID 事务外,MongoDB 还具有文档级原子性。

但是,对于多文档事务,行为有所不同。如果遇到 writeConflict,它会立即失败。该策略的设计方式是允许您的应用程序检测到这种情况,然后等待 X 毫秒并重试 N 次,其中 X 和 N 是您的舒适度。

您设置的参数与第二个会话将阻塞和等待多长时间无关,但与允许系统在建立锁定之前等待多长时间有关。默认值为 5 毫秒。您的示例显示 3 秒(3000 毫秒)。大概您指的是会话号 2 上的锁 - 您允许它获取锁所需的时间。问题在于,在尝试获取锁之前会检测到 writeConflict,因为在 ACID 事务中,预计应用程序而不是驱动程序执行等待。