spring-projects / spring-projects/spring-framework
Nested @TransactionalEventListener with BEFORE_COMMIT and REQUIRES_NEW may leave JDBC connections checked out
Nobody has claimed this yet.
- Dominant language
- Java
- Stars
- 60.2k
- Forks
- 38.8k
- Avg merge
- 5d 2h
- Merged PRs (30d)
- 27
Description
We observed an unexpected behavior when using nested @TransactionalEventListeners together with Propagation.REQUIRES_NEW.
Environment
- Spring Boot: 3.5.14
- Spring Framework: 6.2.19
- Hibernate ORM: 6.6.53.Final
- HikariCP
- JpaTransactionManager
- Java 21
Scenario
The application has the following event flow:
@Transactional
└── publish(Event1)
└── @TransactionalEventListener(AFTER_COMMIT)
├── @Transactional(REQUIRES_NEW)
│ └── publish(Event2)
└── transaction commits
└── @TransactionalEventListener(BEFORE_COMMIT)
└── @Transactional(REQUIRES_NEW)
The project attached below is a minimal reproducer.
Repository:
https://github.com/aytacdereli/spring-transactional-event-bug
Expected behavior
Every REQUIRES_NEW transaction should complete normally and release its JDBC connection back to HikariCP after commit.
Actual behavior
Occasionally one of the JDBC connections remains checked out after the transaction has completed.
After several executions, Hikari's active connection count increases and eventually the pool becomes exhausted.
No exception is thrown.
Additional observations
- Removing the intermediate
@TransactionalEventListenercompletely eliminates the issue. - Using only a single transactional event listener does not reproduce the problem.
REQUIRES_NEWis executed in a different Spring bean (proxy is applied correctly).- No
@Asyncis used for theBEFORE_COMMITlistener. - The behavior is reproducible with the attached minimal project.
At this point I am not sure whether this is an intended limitation of nested @TransactionalEventListeners or a transaction lifecycle issue in Spring Framework.
Any guidance would be appreciated.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Run the linked minimal reproducer with Spring Framework, JpaTransactionManager, nested @TransactionalEventListener callbacks, and REQUIRES_NEW transactions. Start by tracing transaction lifecycle and JDBC connection handling around the AFTER_COMMIT and BEFORE_COMMIT listeners. Done means identifying the lifecycle condition that leaves a connection checked out and confirming repeated executions return all connections to HikariCP without pool exhaustion.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java, spring
- Domain
- backend, database
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 42/100