MongoDB事务:WriteConflict错误



环境

  • MongoDB:(Atlas 4+)
  • 驱动程序:4.2.3
  • 我们使用的是Spring Boot Data MongoDB(2.5.4)

我们使用回调API来使用MongoDB事务。核心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)
});
} 

错误

com.mongodb.MongoCommandException:命令失败,出现错误112(WriteConflict):"WriteConflict:此操作与另一个操作冲突。请重试您的操作或多文档交易。'在服务器abc.mongodb.net:1234上。完整的回应是{"errorLabels":["TransientTransactionError";],"ok":0,"错误消息":"WriteConflict错误:此操作与另一个操作冲突。请重试您的操作或多文档交易&";,"代码":112,"代号":"WriteConflict">

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

我们可以不使用这种方法吗?

根据中的示例:https://docs.mongodb.com/v4.2/core/transactions-in-applications/在事务主体中,我们使用了MongoClient,它运行良好。我们能不使用SessionScoped MongoTemplate?

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

对于数据库操作(命令,如insert或update),如果不在ACID事务中,则冲突操作(示例中的第二个会话)将在当前操作的提交之后重试。请记住,在ACID事务之外,MongoDB具有文档级原子性。

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

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

最新更新