-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Description
MongoDB returns code=112 (WriteConflict) with errorLabels=["TransientTransactionError"] and a retry message.
Spring Data MongoDB translates this to DataIntegrityViolationException (non-transient), which breaks retry policies that rely on Spring exception types.
Spring Data MongoDB: 5.0.1
Observed behavior
MongoDB response:
code: 112 (WriteConflict)errorLabels:["TransientTransactionError"]errmsgcontains:Please retry your operation or multi-document transaction.
MongoExceptionTranslator translates this to org.springframework.dao.DataIntegrityViolationException
Impact
WriteConflict is a concurrency conflict and MongoDB marks it retryable.
Mapping it to DataIntegrityViolationException classifies it as non-transient in Spring and can prevent retries.
./mvnw -pl spring-batch-core -Dtest=MongoDBJobRepositoryIntegrationTests#testParallelJobExecution testFull stack trace
Expand full stack trace
org.springframework.dao.DataIntegrityViolationException: Command execution failed on MongoDB server with error 112 (WriteConflict): 'Caused by :: Write conflict during plan execution and yielding is disabled. :: Please retry your operation or multi-document transaction.' on server localhost:32796. The full response is {"errorLabels": ["TransientTransactionError"], "ok": 0.0, "errmsg": "Caused by :: Write conflict during plan execution and yielding is disabled. :: Please retry your operation or multi-document transaction.", "code": 112, "codeName": "WriteConflict", "$clusterTime": {"clusterTime": {"$timestamp": {"t": 1768999881, "i": 7}}, "signature": {"hash": {"$binary": {"base64": "AAAAAAAAAAAAAAAAAAAAAAAAAAA=", "subType": "00"}}, "keyId": 0}}, "operationTime": {"$timestamp": {"t": 1768999881, "i": 7}}}
at org.springframework.data.mongodb.core.MongoExceptionTranslator.doTranslateException(MongoExceptionTranslator.java:140)
at org.springframework.data.mongodb.core.MongoExceptionTranslator.translateExceptionIfPossible(MongoExceptionTranslator.java:72)
at org.springframework.data.mongodb.core.MongoTemplate.potentiallyConvertRuntimeException(MongoTemplate.java:3175)
at org.springframework.data.mongodb.core.MongoTemplate.executeFindOneInternal(MongoTemplate.java:3044)
at org.springframework.data.mongodb.core.MongoTemplate.doFindAndReplace(MongoTemplate.java:2976)
at org.springframework.data.mongodb.core.MongoTemplate.findAndReplace(MongoTemplate.java:1215)
at org.springframework.data.mongodb.core.MongoTemplate.findAndReplace(MongoTemplate.java:1183)
at org.springframework.data.mongodb.core.MongoOperations.findAndReplace(MongoOperations.java:1171)
at org.springframework.data.mongodb.core.MongoOperations.findAndReplace(MongoOperations.java:1147)
at org.springframework.data.mongodb.core.MongoOperations.findAndReplace(MongoOperations.java:1102)
at org.springframework.batch.core.repository.dao.mongodb.MongoJobExecutionDao.updateJobExecution(MongoJobExecutionDao.java:92)
at org.springframework.batch.core.repository.support.SimpleJobRepository.update(SimpleJobRepository.java:152)
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
at java.base/java.lang.reflect.Method.invoke(Method.java:580)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:359)
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:158)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:370)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:118)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:222)
at jdk.proxy2/jdk.proxy2.$Proxy80.update(Unknown Source)
at org.springframework.batch.core.launch.support.TaskExecutorJobLauncher.launchJobExecution(TaskExecutorJobLauncher.java:259)
at org.springframework.batch.core.launch.support.TaskExecutorJobLauncher.run(TaskExecutorJobLauncher.java:109)
at org.springframework.batch.core.launch.support.SimpleJobOperator.start(SimpleJobOperator.java:201)
at org.springframework.batch.core.launch.support.TaskExecutorJobOperator.start(TaskExecutorJobOperator.java:117)
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
at java.base/java.lang.reflect.Method.invoke(Method.java:580)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:359)
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:158)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:370)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:118)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:222)
at jdk.proxy2/jdk.proxy2.$Proxy126.start(Unknown Source)
at org.springframework.batch.core.repository.support.MongoDBAsyncJobRepositoryIntegrationTests.testJobExecution(MongoDBAsyncJobRepositoryIntegrationTests.java:72)
Caused by: com.mongodb.MongoCommandException: Command execution failed on MongoDB server with error 112 (WriteConflict): 'Caused by :: Write conflict during plan execution and yielding is disabled. :: Please retry your operation or multi-document transaction.' on server localhost:32796. The full response is {"errorLabels": ["TransientTransactionError"], "ok": 0.0, "errmsg": "Caused by :: Write conflict during plan execution and yielding is disabled. :: Please retry your operation or multi-document transaction.", "code": 112, "codeName": "WriteConflict", "$clusterTime": {"clusterTime": {"$timestamp": {"t": 1768999881, "i": 7}}, "signature": {"hash": {"$binary": {"base64": "AAAAAAAAAAAAAAAAAAAAAAAAAAA=", "subType": "00"}}, "keyId": 0}}, "operationTime": {"$timestamp": {"t": 1768999881, "i": 7}}}
at com.mongodb.internal.connection.ProtocolHelper.getCommandFailureException(ProtocolHelper.java:210)
at com.mongodb.internal.connection.InternalStreamConnection.receiveCommandMessageResponse(InternalStreamConnection.java:520)
at com.mongodb.internal.connection.InternalStreamConnection.sendAndReceiveInternal(InternalStreamConnection.java:448)
at com.mongodb.internal.connection.InternalStreamConnection.lambda$sendAndReceive$0(InternalStreamConnection.java:375)
at com.mongodb.internal.connection.InternalStreamConnection.sendAndReceive(InternalStreamConnection.java:378)
at com.mongodb.internal.connection.UsageTrackingInternalConnection.sendAndReceive(UsageTrackingInternalConnection.java:111)
at com.mongodb.internal.connection.DefaultConnectionPool$PooledConnection.sendAndReceive(DefaultConnectionPool.java:757)
at com.mongodb.internal.connection.CommandProtocolImpl.execute(CommandProtocolImpl.java:60)
at com.mongodb.internal.connection.DefaultServer$DefaultServerProtocolExecutor.execute(DefaultServer.java:207)
at com.mongodb.internal.connection.DefaultServerConnection.executeProtocol(DefaultServerConnection.java:112)
at com.mongodb.internal.connection.DefaultServerConnection.command(DefaultServerConnection.java:82)
at com.mongodb.internal.connection.DefaultServerConnection.command(DefaultServerConnection.java:74)
at com.mongodb.internal.connection.DefaultServer$OperationCountTrackingConnection.command(DefaultServer.java:297)
at com.mongodb.internal.operation.SyncOperationHelper.lambda$executeRetryableWrite$10(SyncOperationHelper.java:267)
at com.mongodb.internal.operation.SyncOperationHelper.lambda$withSourceAndConnection$0(SyncOperationHelper.java:131)
at com.mongodb.internal.operation.SyncOperationHelper.withSuppliedResource(SyncOperationHelper.java:156)
at com.mongodb.internal.operation.SyncOperationHelper.lambda$withSourceAndConnection$1(SyncOperationHelper.java:130)
at com.mongodb.internal.operation.SyncOperationHelper.withSuppliedResource(SyncOperationHelper.java:156)
at com.mongodb.internal.operation.SyncOperationHelper.withSourceAndConnection(SyncOperationHelper.java:129)
at com.mongodb.internal.operation.SyncOperationHelper.lambda$executeRetryableWrite$11(SyncOperationHelper.java:252)
at com.mongodb.internal.operation.SyncOperationHelper.lambda$decorateWriteWithRetries$12(SyncOperationHelper.java:308)
at com.mongodb.internal.async.function.RetryingSyncSupplier.get(RetryingSyncSupplier.java:67)
at com.mongodb.internal.operation.SyncOperationHelper.executeRetryableWrite(SyncOperationHelper.java:279)
at com.mongodb.internal.operation.BaseFindAndModifyOperation.execute(BaseFindAndModifyOperation.java:80)
at com.mongodb.client.internal.MongoClusterImpl$OperationExecutorImpl.execute(MongoClusterImpl.java:448)
at com.mongodb.client.internal.MongoCollectionImpl.executeFindOneAndReplace(MongoCollectionImpl.java:754)
at com.mongodb.client.internal.MongoCollectionImpl.findOneAndReplace(MongoCollectionImpl.java:747)
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
at java.base/java.lang.reflect.Method.invoke(Method.java:580)
at org.springframework.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:281)
at org.springframework.data.mongodb.SessionAwareMethodInterceptor.invoke(SessionAwareMethodInterceptor.java:118)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:222)
at jdk.proxy2/jdk.proxy2.$Proxy130.findOneAndReplace(Unknown Source)
at org.springframework.data.mongodb.core.MongoTemplate$FindAndReplaceCallback.doInCollection(MongoTemplate.java:3420)
at org.springframework.data.mongodb.core.MongoTemplate$FindAndReplaceCallback.doInCollection(MongoTemplate.java:3382)
at org.springframework.data.mongodb.core.MongoTemplate.executeFindOneInternal(MongoTemplate.java:3041)
... 33 more
Expected
When MongoDB marks a failure retryable (e.g. TransientTransactionError), translate it to a transient Spring exception
(e.g. ConcurrencyFailureException / TransientDataAccessException) instead of DataIntegrityViolationException.
Open Question
Should the translator use:
errorLabels(primary), and optionallycode=112, orcode=112alone
to decide transient vs non-transient for WriteConflict?
Relates to: spring-projects/spring-batch#5145