Transactionssenior8+ years

`placeOrder()` is `@Transactional` (REQUIRED) and calls `audit.logAttempt()` on another bean, which is `@Transactional(propagation = REQUIRES_NEW)` so the audit row survives a failed order. Under load, with a pool of 10 connections, requests start hanging for 30 seconds and then failing with a connection-timeout error, while the database is nearly idle. Walk through what REQUIRES_NEW actually does and why this happens.

REQUIRES_NEW does not share the caller's connection. It suspends the outer transaction, which means parking its EntityManager and connection (still open, not returned to the pool, because returning it would mean ending a transaction nobody asked to end), and then takes a second connection from the pool for the inner transaction. Each request therefore needs two connections at the moment it reaches logAttempt(). With ten requests in flight, all ten can each hold their outer connection and each wait for a second one that can only come from another request's outer connection, which none of them will release until they get their second. Nobody progresses; each waits for the pool's connection timeout (30 seconds by default in HikariCP) and fails. The database is idle because the deadlock is in the pool, not in the database.

The lesson behind it →
More on Transactions