spring-framework icon indicating copy to clipboard operation
spring-framework copied to clipboard

Inconsistent Transaction and TransactionSynchronizationManager Attributes with Nested Transactions in Multi-Transaction Manager Environment

Open teahyuk opened this issue 6 months ago • 0 comments

Hi !

situation

In a multi-transaction manager environment, when @Transactional annotations from different transaction managers are nested, the attributes of TransactionSynchronizationManager do not align with the current transaction if the inner @Transactional method has a propagation level of SUPPORTS, NOT_SUPPORTS, or NEVER.

Specifically, TransactionSynchronizationManager inherits the attributes (e.g., readOnly status) of the outer/parent's other transaction manager, rather than reflecting the characteristics of the "empty" or "non-participating" transaction created by the inner @Transactional method under these propagation settings. This leads to a mismatch between the expected transaction state and the state reported by TransactionSynchronizationManager.

Steps to Reproduce

Consider the following simplified example:


class TransactionManager1 {

    private TransactionManager2 transactionManager2;

    @Transactional(transactionManager = "manager1", readOnly = true)
    public void transactionFirst() {
       transactionManager2.transactionSecond();
    }
}

class TransactionManager2 {

    // Assuming propagation is one of SUPPORTS, NOT_SUPPORTS, or NEVER implicitly or explicitly
    @Transactional(transactionManager = "manager2", readOnly = false, propagation = Propagation.SUPPORTS)
    public void transactionSecond() { // Changed method name for clarity based on example
        // Inside this method, TransactionSynchronizationManager.isCurrentTransactionReadOnly()
        // is expected to be 'false' (based on manager2's readOnly=false)
        // However, it currently returns 'true', inheriting from manager1.
        assert TransactionSynchronizationManager.isCurrentTransactionReadOnly() : "readOnly must be false";
    }
}

Expected Behavior

When transactionSecond() is invoked, TransactionSynchronizationManager.isCurrentTransactionReadOnly() should accurately reflect the characteristics of the current transaction context established by manager2's @Transactional, irrespective of any parent transaction's attributes.

This is especially critical when the parent transaction originates from a different transaction manager. While it might seem that PROPAGATION_SUPPORTS, PROPAGATION_NOT_SUPPORTED, or PROPAGATION_NEVER shouldn't be affected as they don't create a new transaction, @Transactional attributes like readOnly can be set regardless of whether a physical transaction is actually active. Therefore, these attributes should be applied and reflected independently.

Indeed, if @Transactional(transactionManager = "manager2", readOnly = false, propagation = Propagation.SUPPORTS) were to execute without a parent transaction, TransactionSynchronizationManager.isCurrentTransactionReadOnly() would correctly return false. This demonstrates that @Transactional attributes should be precisely reflected, regardless of the presence of other transaction managers.

Therefore, whether transactionFirst() is invoked or transactionSecond() is called directly, assert TransactionSynchronizationManager.isCurrentTransactionReadOnly() should not throw an exception.

Actual Behavior

However, when transactionFirst() is invoked, an AssertionError is thrown when transactionSecond() is called. Conversely, if transactionSecond() is invoked directly, no AssertionError occurs.

Proposed Changes

In AbstractPlatformTransactionManager.getTransaction(Definition), we've added suspend logic at the very end of the method. This ensures that when a transaction context is established with propagations like PROPAGATION_SUPPORTS, PROPAGATION_NOT_SUPPORTED, or PROPAGATION_NEVER, any previously active transaction (potentially from another TransactionManager) is correctly suspended. This guarantees that TransactionSynchronizationManager accurately reflects the characteristics of the current transaction context, preventing inconsistencies in readOnly status and other attributes.

teahyuk avatar Jun 12 '25 14:06 teahyuk