Transactions & Concurrency
Transaction Propagation — REQUIRED, REQUIRES_NEW, NESTED and When to Use Each
How nested method calls join or create transaction context states.
Introduction
When one transactional service calls another, Spring must determine whether the invoked method should join the current transaction, open a separate independent transaction, or run without a transaction context. This is controlled by **Transaction Propagation**.
Why It Matters
Incorrect propagation settings lead to database integrity failures. A common production bug is the silent failure of security audits or log tracking statements because they join the business transaction and roll back along with it if a business error occurs.
Real-World Analogy
Think of propagation configurations like running a business project with consultants:
• REQUIRED (Join Team): The consultant joins your active project team. If your project fails, the entire team is dismissed.
• REQUIRES_NEW (Independent Agent): The consultant works in a separate division. If your project fails, their work remains intact and billed separately.
• NESTED (Sub-task): The consultant works on a sub-task. If the sub-task fails, you cancel only that task, without discarding the entire project work.
Detailed Mechanics
| Propagation Level | If Active Transaction Exists | If No Transaction Exists |
|---|---|---|
| REQUIRED | Joins the current transaction (Default) | Creates a new transaction |
| REQUIRES_NEW | Suspends current transaction, opens a new independent transaction | Creates a new transaction |
| NESTED | Creates a database Savepoint. Can roll back to savepoint independently | Creates a new transaction |
| MANDATORY | Joins the current transaction | Throws TransactionRequiredException |
| NEVER | Throws IllegalTransactionStateException | Executes without transaction context |
1. REQUIRES_NEW Connection Overhead
When using REQUIRES_NEW, Spring suspends the outer transaction. This means the outer transaction's database connection remains open and idle while a second connection is acquired from the pool to execute the inner transaction. If the connection pool is undersized, this can lead to connection starvation or pool deadlocks.
Practical Example
Here is a system where an audit log must be written to the database even if the main purchase transaction fails and rolls back:
Quick Quiz
Q1: If a child transaction propagation is set to REQUIRED, and the child throws an exception that is caught in the parent service, what happens?
A) Only the child modifications roll back; parent commits successfully.
B) The entire transaction rolls back, and Spring throws an UnexpectedRollbackException when parent tries to commit.
C) The transaction is split automatically, committing the parent modifications.
D) The database throws a lock error.
Answer: B — Because REQUIRED joins the outer transaction, any error marks the shared transaction resource as "rollback-only". Catching the exception cannot prevent the overall commit failure.
Q2: Which propagation level ensures that a method will fail if it is not called within an active transaction?
A) REQUIRED
B) SUPPORTS
C) MANDATORY
D) REQUIRES_NEW
Answer: C — MANDATORY propagation throws an exception if no transaction is active, forcing the caller to manage transaction scope.
Scenario-Based Challenge
Production Scenario:
Your application runs a transaction method using REQUIRES_NEW inside a loop that updates 20 elements. The connection pool size is configured to 10. During peak load, the application deadlocks and halts. Why does this happen?
This is a classic connection pool deadlock caused by REQUIRES_NEW. If 10 request threads enter the parent service, they each acquire a database connection. When they attempt to call the REQUIRES_NEW method, the parent connection is suspended but kept open. The thread requests a second connection from the pool. However, the pool is empty (all 10 connections are held by suspended parent threads). The threads wait indefinitely for a connection, resulting in a deadlock.
To fix this:
- Increase the connection pool size:
maximum-pool-size = (threads * 2). - Avoid nesting
REQUIRES_NEWinside loops; execute independent actions asynchronously or batch them before entering the main transaction.
Interview Questions
1. Conceptual: What is the difference between REQUIRES_NEW and NESTED?
REQUIRES_NEW opens a separate physical transaction connection. It operates independently of the parent: a rollback in parent does not affect the child. NESTED uses a single physical connection with database savepoints. Rollbacks in parent *will* roll back the nested changes, but rollbacks in the nested savepoint can be caught in the parent to preserve parent progress.
2. Concept: When is NESTED propagation supported?
NESTED requires transaction managers that support SQL Savepoints (like JDBC's DataSourceTransactionManager). It is not supported by default JPA transaction managers unless specific parameters or custom JDBC layers are configured.
Production Considerations
Use REQUIRES_NEW selectively. It has significant performance overhead because it triggers additional database physical connection handshakes. Default to REQUIRED, and isolate write logs or event triggers to asynchronous processes using @Async.