Affects: 6.0.4 (spring-r2dbc)
When using @Transactional
on a reactive service method, the auto-generated transaction name used by the MSSQL R2DBC driver might be too long in some use cases, leading to the following exception:
org.springframework.transaction.CannotCreateTransactionException: Could not open R2DBC Connection for transaction
at org.springframework.r2dbc.connection.R2dbcTransactionManager.lambda$doBegin$5(R2dbcTransactionManager.java:234) ~[spring-r2dbc-6.0.4.jar:6.0.4]
Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException:
...
Original Stack Trace:
at org.springframework.r2dbc.connection.R2dbcTransactionManager.lambda$doBegin$5(R2dbcTransactionManager.java:234) ~[spring-r2dbc-6.0.4.jar:6.0.4]
at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onError(FluxOnErrorResume.java:94) ~[reactor-core-3.5.2.jar:3.5.2]
...
Caused by: java.lang.IllegalArgumentException: Transaction names must contain only characters and numbers and must not exceed 32 characters
at io.r2dbc.mssql.util.Assert.isTrue(Assert.java:106) ~[r2dbc-mssql-1.0.0.RELEASE.jar:1.0.0.RELEASE]
at io.r2dbc.mssql.MssqlConnection.lambda$beginTransaction$1(MssqlConnection.java:114) ~[r2dbc-mssql-1.0.0.RELEASE.jar:1.0.0.RELEASE]
at io.r2dbc.mssql.MssqlConnection.lambda$useTransactionStatus$15(MssqlConnection.java:426) ~[r2dbc-mssql-1.0.0.RELEASE.jar:1.0.0.RELEASE]
...
package org.example;
@Service
public class MyService {
@Transactional
public Mono<Void> myServiceMethod() {
...
}
}
In this example, the auto-generated transaction definition name would be org.example.MyService.myServiceMethod
. This is actually the jointpointIdentification
as implemented inTransactionAspectSupport.ReactiveTransactionSupport.createTransactionIfNecessary()
.
This transaction name is then transferred to the MSSQL R2DBC driver (see R2dbcTransactionManager.doBegin
).
The driver then sanitises the transaction name into org_example_MyService_myServiceMethod
.
However, it also checks that the length of the transaction name is shorter or equals to 32 characters, so that it can be safely used in the MSSQL statement BEGIN TRANSACTION <name>
(see length restriction here https://learn.microsoft.com/en-us/sql/t-sql/language-elements/begin-transaction-transact-sql?view=sql-server-ver15#arguments).
It looks like this behaviour has been added by the driver for some time already: https://github.com/r2dbc/r2dbc-mssql/issues/183
Would it be possible to make sure that the transaction name is suitable to be use with the MSSQL driver?
I am not sure wether it is spring-r2dbc
or r2dbc-mssql
responsibility though...
The current workaround I use, is to override the R2dbcTransactionManager.createTransactionDefinition
method and abbreviate the transaction name:
public class MssqlFriendlyR2dbcTransactionManager extends R2dbcTransactionManager {
private static final Integer MAX_TRANSACTION_NAME_LENGTH = 32;
public MssqlFriendlyR2dbcTransactionManager(ConnectionFactory connectionFactory) {
super(connectionFactory);
}
@Override
protected io.r2dbc.spi.TransactionDefinition createTransactionDefinition(TransactionDefinition definition) {
return super.createTransactionDefinition(adjustTransactionDefinintionNameLength(definition));
}
private DefaultTransactionDefinition adjustTransactionDefinintionNameLength(TransactionDefinition definition) {
var adjustedDefinition = new DefaultTransactionDefinition(definition);
adjustedDefinition.setName(Optional.ofNullable(adjustedDefinition.getName())
.map(this::shortenTransactionDefinitionName)
.orElse(null));
return adjustedDefinition;
}
private String shortenTransactionDefinitionName(String name) {
var length = name.length();
return length > MAX_TRANSACTION_NAME_LENGTH ? name.substring(length - MAX_TRANSACTION_NAME_LENGTH, length)
: name;
}
}
It is good enough, but I would love to rather see it implemented in the framework instead!
Comment From: mp911de
The 32 chars limitation applies only to the SQL Server driver; Specifically, the SQL server imposes this limitation.
Comment From: jhoeller
Same issue as in #29829. We are not quite sure how to address it in the framework by default. I suppose we could provide a configurable property for the maximum transaction name length, but it seems a shame that this has to be explicitly configured. It would be more transparent if the driver truncated the name internally, according to its own conventions.
Comment From: sbrannen
Closing as duplicate of #29829
Comment From: ghost
Sorry to have missed the existing issue #29829. Thank you all for the quick answers though!