Skip to content

It seems like there is a connection leak issue with the entityManager. #3157

@bperhaps

Description

@bperhaps

It seems like there might be a Connection Leak issue when using the NOT_SUPPORTED propagation attribute or creating a transactionless state in Spring Batch and then calling a non-transactional Repository.

The commonality is that, in both NOT_SUPPORTED and BATCH codes, TransactionSynchronizationManager has activeTransaction set to false and the ResourceMap is empty, but TransactionSynchronizationManager.isSynchronizationActive() returns true.

Looking at the example below might make it easier.

@Service
@RequiredArgsConstructor
public class TestService {

    private final TestService2  testService2;
    @Transactional
    public void test() {
        testService2.test();
    }
}

@Service
@RequiredArgsConstructor
public class TestService2 {

    private final TestRepository TestRepository;

    @Transactional(propagation = NOT_SUPPORTED)
    public void test() {
        var e = TestRepository.findByIdNoTx(1L);
    }
}

In the scenario described above, after executing the repository in TestService2::test, the connection remains in the TransactionSynchronizationManager's ResourceMap without being properly removed.

Typically, users might believe that since there is no transaction, the connection will be immediately released. However, it seems that in this case, that assumption does not hold true.

Below is an example of a Spring Batch scenario:

    @Bean
    public Step step1() {
        DefaultTransactionAttribute transactionAttribute = new DefaultTransactionAttribute();
        transactionAttribute.setPropagationBehavior(Propagation.NEVER.value());

        return stepBuilderFactory.get("step1").tasklet((contribution, chunkContext) -> {
                var e = testRepository.findByIdNoTx(1L);
                return RepeatStatus.FINISHED;
            })
            .transactionAttribute(transactionAttribute)
            .build();
    }

Even in cases like the one described, it seems that after the execution of testRepository.findByIdNoTx(), the transaction manager retains the resource in the resource map, leading to the connection not being released as expected.

The problem described above can cause an exception due to db wiat_timeout when the user creates logic that takes a long time between two repository requests (expecting no connection because there is no transaction).

The cause of the problem seems to be the entityManager creation availability branch in EntityManagerFactoryUtils.

		else if (!TransactionSynchronizationManager.isSynchronizationActive()) {
			// Indicate that we can't obtain a transactional EntityManager.
			return null;
		}

Line 249 in EntityManagerFactoryUtils is currently using only isSynchronizationActive() for validation.

After creating a typical API application, if you execute the code like the one in the example above when there is no transaction, the TransactionSynchronizationManager.isSynchronizationActive() will be marked as false, returning null, and not maintaining the connection.
I believe this approach is correct.

I'm curious about your thoughts on adding the following validation logic here:

else if (!TransactionSynchronizationManager.isActualTransactionActive() || !TransactionSynchronizationManager.isSynchronizationActive()) {
			// Indicate that we can't obtain a transactional EntityManager.
			return null;
		}

Thank you!

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions