Skip to content

Transaction isolation level not working with R2dbcTransactionManager  #30508

@sasavilic

Description

@sasavilic

Spring boot: 3.1.0 and 3.1.1 (SNAPSHOT)
Database: PostgreSQL

@Service
public class TxTester implements ApplicationRunner {
    private final Logger logger = LoggerFactory.getLogger(TxTester.class);

    private final DatabaseClient databaseClient;
    private final TransactionalOperator rrOperator;
    private final TransactionalOperator nOperator;

    @Autowired
    public TxTester(DatabaseClient databaseClient, ReactiveTransactionManager transactionManager) {
        this.databaseClient = databaseClient;
        this.rrOperator = TransactionalOperator.create(transactionManager, new TransactionDefinition() {
            @Override
            public int getIsolationLevel() {
                return TransactionDefinition.ISOLATION_REPEATABLE_READ;
            }
        });
        this.nOperator = TransactionalOperator.create(transactionManager, new TransactionDefinition() {
            @Override
            public int getPropagationBehavior() {
                return TransactionDefinition.PROPAGATION_REQUIRES_NEW;
            }
        });
    }

    @Override
    public void run(ApplicationArguments args) throws Exception {
        logger.info("Creating table");
        databaseClient.sql("CREATE TABLE IF NOT EXISTS person(id INT PRIMARY KEY, name VARCHAR)").then().block();
        logger.info("Adding sample record");
        databaseClient.sql("INSERT INTO person(id, name) VALUES (1, 'John') ON CONFLICT (id) DO UPDATE SET name = EXCLUDED.name").then().block();

        logger.info("Getting row with select for update");
        var name = databaseClient.sql("SELECT 1")
                .then()
                .doOnSuccess(x -> logger.info("Now updating row in different transaction"))
                .then(databaseClient.sql("UPDATE person SET name = 'Mark' WHERE id = 1").then().as(nOperator::transactional))
                .doOnSuccess(x -> logger.info("Fetching row with FOR UPDATE. Expecting to fail"))
                .then(databaseClient.sql("SELECT * FROM person WHERE id = 1 FOR UPDATE")
                        .map((row, metadata) -> row.get("name", String.class))
                        .one()
                )
                .as(rrOperator::transactional)
                .block();
        logger.info("Got name: {}. This should not happen!", name);
    }
}

Expected is something like (works in spring-boot 2.4.13):

Caused by: org.springframework.dao.ConcurrencyFailureException: executeMany; SQL [SELECT * FROM person WHERE id = 1 FOR UPDATE]; could not serialize access due to concurrent update; nested exception is io.r2dbc.postgresql.ExceptionFactory$PostgresqlRollbackException: [40001] could not serialize access due to concurrent update

I also investigated traffic with wireshark between PostgreSQL and my demo project, there is no isolation level information being set.

Metadata

Metadata

Assignees

Labels

in: dataIssues in data modules (jdbc, orm, oxm, tx)type: regressionA bug that is also a regression

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions