lim*_*uan 8 mongodb spring-data-mongodb
在事务中查询文档时,仅使用特定 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)
Run Code Online (Sandbox Code Playgroud)
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.
Run Code Online (Sandbox Code Playgroud)
小智 12
我们的项目中也遇到了类似的问题:一些测试失败,并显示 251“给定的交易号 * 与任何正在进行的交易不匹配”。我们编写了一个测试来重现该问题,然后进行调试。我 90% 确信我们找到了问题。
所以假设如下:
当我们使用 spring mongo 事务时,实际事务在第一个请求发送到数据库时启动(无论哪种请求 - 查询、更新等)。因此,当第一个请求与任何其他请求并行发送,但无法在它们之前完成,并且它们认为事务已启动(但实际上尚未启动)时,我们会看到此错误发生。“交易x+1不存在。最后一笔交易是x”。得出这个结论的是以下日志序列(我们在测试执行期间启用了跟踪日志):
请注意,在第一个日志中我们有"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()
}
}
Run Code Online (Sandbox Code Playgroud)
测试自身:
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()
}
}
Run Code Online (Sandbox Code Playgroud)
结论:确保事务中对 mongodb 的第一个请求不会与该事务中的任何其他请求并行完成。
一点免责声明:这不是一个正确的答案,因为我不确定它是否可以帮助原始海报;另外,我对这种效果没有适当的解释。我只是想让人们知道这个效果;通常我会为此使用评论,但评论太长了。我发布此内容是希望它可以帮助某人解决他们的问题。
我们最近看到了类似的异常:
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)
Run Code Online (Sandbox Code Playgroud)
我无法显示代码,所以我将尝试解释它的作用。
在非响应式设置中,它会是这样的:
X = getX()
Y = saveAndReturnY()
pair = (X, Y)
processPair(pair)
Run Code Online (Sandbox Code Playgroud)
这个逻辑已实现,并且运行良好(我没有显示工作代码版本,它不相关)。该代码在事务下运行。
然后,我重构了这段代码以简化它。它最终看起来像这样:
getX() // Mono<X>
.zipWith(saveAndReturnY()) // Mono<Tuple<X, Y>>
.flatMap(this::processPair)
Run Code Online (Sandbox Code Playgroud)
通过这段代码,我们观察到了上面的异常。它并没有 100% 发生,就像 50/50 一样:有时会发生,有时不会。这仅发生在 MongoDB Atlas 上。我们使用在 Docker 容器(通过 testcontainers)中工作的相同版本(4.2.9)的 MongoDB 进行集成测试,效果很好,但我们从未设法重现该问题。
长话短说:事实证明,如果我们替换zipWith()为zipWhen(),问题就会消失:
getX() // Mono<X>
.zipWhen(x -> saveAndReturnY()) // Mono<Tuple<X, Y>>
.flatMap(this::processPair)
Run Code Online (Sandbox Code Playgroud)
不同之处在于,对于压缩在一起的两个 Mono(即和 )zipWith(),订阅同时发生,因此它们“并行”执行。但对于,它们严格按顺序执行:您首先退出,然后订阅。在原始代码(重构之前)中,它以相同的(顺序的,或者可能更好地说是因果的)方式工作。Mono<X>Mono<Y>zipWhen()XMono<X>Mono<Y>
我不确定为什么会发生这种情况,我们仍在调查。也许这种并行性刚刚暴露出了一些问题。或者 MongoDB 事务中有一些我还不理解的东西,导致在并行性启动时发生此类异常。
无论如何,在我们的例子中,摆脱并行性解决了问题。尝试分析您的代码,看看是否有一些像Mono.zipWith()或Flux.flatMap()那样的操作符急切地订阅多个操作符,从而Publisher导致这种“并行性”效应。
| 归档时间: |
|
| 查看次数: |
9210 次 |
| 最近记录: |