An inner REQUIRES_NEW scope can need another physical connection while the outer transaction still owns one.
Spring REQUIRES_NEW: budget the second database connection
Count simultaneous borrowers
Suppose 24 request workers each start an outer transaction and then enter an audit method marked REQUIRES_NEW. The outer work can keep 24 connections occupied while the inner scopes wait for 24 more. With a pool of 24, no worker can acquire its inner connection. Even a pool of 25 merely permits one inner scope to make progress at a time. This is a capacity problem, not a rollback annotation problem.
Separate transaction intent from durability
The inner audit can commit even if the outer reservation rolls back. That may be right for a failed-attempt record, but wrong for an event claiming a reservation succeeded. For success events, the outbox path keeps the record and state change in one physical transaction. Propagation choices should follow the failure contract.
Exercise a saturation case
Drive concurrent calls up to the request pool limit, measure connection acquisition waits, and verify the business result after a timeout. Include other pool consumers such as health checks and scheduled work. The arithmetic here is a planning scenario; actual pool size depends on measured concurrent transaction scopes and database capacity.
Implementation sketch
@Transactional
public void reserve(ReservationCommand command) {
inventory.reserve(command);
attemptRecorder.record(command.requestId()); // separate bean
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void record(String requestId) {
attempts.insert(requestId);
}Cost and verification
A REQUIRES_NEW call may consume a second pooled connection per active outer transaction. Extra connections increase database sessions and contention; undersizing turns the inner call into a pool wait or timeout.
Common Mistakes
- Do not assume REQUIRES_NEW reuses the suspended outer connection.
- Do not call the inner method through self-invocation and expect proxy advice.
- Do not commit a success event independently of a business transaction that can still fail.
Read next
Spring transaction propagation: joined rollback and independent commit, Spring AOP proxies: self-invocation bypasses proxy advice, Spring outbox transaction boundaries: where atomicity ends, Spring imperative transaction: a worker thread does not inherit it.
