在查询事务中的文档时,只使用特定的id (其他所有id都可以)时,会遇到这个Mongo异常:
Exception occured: org.springframework.data.mongodb.MongoTransactionException:
Query failed with error code 251 and error message
'Given transaction number 1 does not match any in-progress transactions. The active transaction number is -1' on server mongo-1:27017;
nested exception is com.mongodb.MongoQueryException:
Query failed with error code 251 and error message
'Given transaction number 1 does not match any in-progress transactions. The active transaction number is -1' on server mongo-1:27017
at org.springframework.data.mongodb.core.MongoExceptionTranslator.translateExceptionIfPossible(MongoExceptionTranslator.java:136)
at org.springframework.data.mongodb.core.MongoTemplate.potentiallyConvertRuntimeException(MongoTemplate.java:2902)
at org.springframework.data.mongodb.core.MongoTemplate.executeFindMultiInternal(MongoTemplate.java:2810)
at org.springframework.data.mongodb.core.MongoTemplate.doFind(MongoTemplate.java:2555)
at org.springframework.data.mongodb.core.ExecutableFindOperationSupport$ExecutableFindSupport.doFind(ExecutableFindOperationSupport.java:216)
at org.springframework.data.mongodb.core.ExecutableFindOperationSupport$ExecutableFindSupport.oneValue(ExecutableFindOperationSupport.java:128)
at org.springframework.data.mongodb.repository.query.AbstractMongoQuery.lambda$getExecution$4(AbstractMongoQuery.java:153)
at org.springframework.data.mongodb.repository.query.AbstractMongoQuery.doExecute(AbstractMongoQuery.java:126)
at org.springframework.data.mongodb.repository.query.AbstractMongoQuery.execute(AbstractMongoQuery.java:101)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:618)
MongoDB是一个切分的集群,对一个完全相似的集群(在另一个测试环境中)执行类似的操作,但没有出现异常。
另一个信息是,此异常总是在MongoDB套接字异常之后,如下所示:
Got socket exception on connection [connectionId{localValue:9, serverValue:11760}] to mongo-1:27017. All connections to mongo-1:27017 will be closed.
发布于 2021-07-15 12:06:07
我们的项目中有一个类似的问题:一些测试失败了251‘给定的事务号*不匹配任何正在进行的事务’。我们编写了一个测试来重现这个问题,然后对它进行了调试。我百分之九十肯定我们找到了这个问题。
因此,假设如下:
当我们使用spring事务时,当第一个请求被发送到数据库时,实际的事务就会启动(不管是什么样的请求--查询、更新等等)。因此,当第一个请求与任何其他请求并行发送时,但是无法在它们之前完成,并且他们认为事务已经启动(但没有),那么我们就会看到这个错误发生了。“事务x+1不存在。最后一个事务是x”。得出这个结论的原因是以下日志序列(我们在测试执行期间启用了跟踪日志):
命令失败,错误251 ( ...
注意,在第一个日志中,我们是如何使用"startTransaction":true,而在第二个日志中,我们没有。然而,第二个请求首先完成了,因此它出错了。如果第二请求在第一请求开始和第一请求完成之间启动,例如:第一请求启动,第二请求启动,第一请求完成,第二请求完成,则也会发生此错误。
测试代码( kotlin):
服务:
@Service
class TestService {
@Autowired private lateinit var itemRepository: ItemRepository
@Transactional
fun parallelQuery(): Mono<Void> {
return Flux.fromIterable(listOf("d1", "d2"))
.flatMap {
itemRepository.findById(it)
}
.then()
}
}
测试本身:
internal class MonoZipTest: BaseTest() {
@Autowired private lateinit var testService: TestService
@Test
fun test() {
val mono =
Flux.fromIterable(1..1000)
.flatMap {
testService.parallelQuery()
}
.then()
StepVerifier.create(mono)
.expectError(MongoTransactionException::class.java)
.verify()
}
}
结论:确保在交易中对mongodb的第一次请求不会与该交易中的任何其他请求并行进行。
发布于 2020-10-04 11:19:26
一个小小的免责声明:这不是一个正确的答案,因为我不确定它是否能帮助原来的海报;而且,我也没有适当的解释这个效果。我只是想让人们知道这个效果,通常我会用一个评论来表达,但是它太长了,不能发表评论。我发这篇文章是希望它能帮助人们解决他们的问题。
我们最近也看到了一个类似的例外:
Caused by: com.mongodb.MongoQueryException: Query failed with error code 251 and error message 'Given transaction number 198 does not match any in-progress transactions. The active transaction number is 197' on server aaa.bbb.ccc:27017
at com.mongodb.operation.FindOperation$3.onResult(FindOperation.java:822)
我无法显示代码,所以我将尝试解释它的功能。
在没有反应的情况下,情况会是这样的:
X = getX()
Y = saveAndReturnY()
pair = (X, Y)
processPair(pair)
这个逻辑是实现的,它工作得很好(我没有显示工作代码版本,它不相关)。此代码在事务下运行。
然后,我重构了这段代码,以简化它。结果看起来是这样的:
getX() // Mono<X>
.zipWith(saveAndReturnY()) // Mono<Tuple<X, Y>>
.flatMap(this::processPair)
使用这段代码,我们观察到了上面的异常。它不是100%发生的,大约是50/50:有时发生,有时不发生。这只发生在MongoDB地图集上。我们的集成测试使用相同版本(4.2.9)的MongoDB在Docker容器(通过测试容器)中工作,运行良好,我们从未在那里重现问题。
长话短说:事实证明,如果我们用zipWith()
替换zipWhen()
,问题就解决了:
getX() // Mono<X>
.zipWhen(x -> saveAndReturnY()) // Mono<Tuple<X, Y>>
.flatMap(this::processPair)
不同的是,使用zipWith()
时,订阅同时发生在两个被压缩在一起的Monos上,即Mono<X>
和Mono<Y>
,因此它们“并行”执行。但是对于zipWhen()
,它们严格按顺序执行:首先从Mono<X>
中获取X
,然后订阅Mono<Y>
。在原始代码中(在重构之前),它以相同的方式工作(顺序的,或者更好地说是因果的)。
我不知道为什么会这样,我们还在调查。也许是因为这种并行性而出现了一些问题。或者,在MongoDB事务中有一些我还不理解的东西,当并行性开始时,就会产生这样的异常。
无论如何,在我们的例子中,消除并行性解决了这个问题。尝试分析您的代码,看看是否有一些操作符(如Mono.zipWith()
或Flux.flatMap()
)订阅了多个Publisher
,从而产生了这样的“并行性”效果。
发布于 2022-01-14 12:03:26
我的答案可能对使用猫鼬做nodejs的人有用。
我也遇到了同样的错误。经过研究发现,这个问题是由写作冲突引起的。您可以在mongosh中运行db.currentOp()
来检查。
此命令的文档:https://docs.mongodb.com/manual/faq/concurrency/#how-do-i-see-the-status-of-locks-on-my--instances-
如果存在一些写冲突,则在事务中进行mongo重试操作,直到冲突得到解决,在我的示例中,需要超过60秒(事务完成的默认最大时间),因此它被中止。
关于写冲突的好文章:https://blog.clairvoyantsoft.com/mongodb-transaction-management-884f82f62767
https://stackoverflow.com/questions/64084992
复制相似问题