Foundation · Build
Propagation & Isolation in Practice
Propagation decides whether a method joins the caller’s transaction or starts its own; isolation decides which concurrent anomalies remain visible.
Primary source: Spring Framework 6.2: Transaction Propagation and Isolation
Lesson 14 of 17
1d streakThe Architectural Question
Default propagation joins whatever transaction is active: one failed payment silently discards every audit row, and raising isolation globally serializes workloads that never needed it.
What this guide proves
- Choose REQUIRED, REQUIRES_NEW, and SUPPORTED deliberately
- Explain read phenomena from READ_UNCOMMITTED to SERIALIZABLE
- Keep audit writes alive when the business transaction rolls back
Transaction propagation: REQUIRED vs REQUIRES_NEW
Propagation defines how transactions behave when one transactional service calls another. Choosing the wrong propagation creates unexpected rollbacks or locks.
| Propagation | If Transaction Exists | If No Transaction Exists | Common Use Case |
|---|---|---|---|
| REQUIRED (Default) | Joins current transaction | Creates a new transaction | Standard business services |
| REQUIRES_NEW | Suspends current transaction; creates independent new one | Creates a new transaction | Audit logging, billing attempts that must persist even if parent rolls back |
| MANDATORY | Joins current transaction | Throws IllegalTransactionStateException | Methods that must never run without an active transaction |
| SUPPORTS | Joins current transaction | Runs non-transactionally | Read queries that can adapt to caller context |
Database isolation levels and concurrency phenomena
| Isolation Level | Dirty Reads Allowed? | Non-Repeatable Reads Allowed? | Phantom Reads Allowed? |
|---|---|---|---|
| READ_UNCOMMITTED | YES | YES | YES |
| READ_COMMITTED (PostgreSQL/Oracle Default) | NO | YES | YES |
| REPEATABLE_READ (MySQL InnoDB Default) | NO | NO | YES (MVCC prevents in most modern DBs) |
| SERIALIZABLE | NO | NO | NO (highest isolation, highest lock contention) |
Active Recall Checkpoint
What happens if a REQUIRES_NEW method throws an exception that is caught and swallowed by the outer REQUIRED caller?
Hint: Are the two transactions independent?
Show answer & explanation
The inner REQUIRES_NEW transaction rolls back. Because the outer method caught the exception, the outer transaction can still commit its own changes successfully.
Propagation rules of thumb
Use it when
Default to Propagation.REQUIRED for almost all business workflows. Use REQUIRES_NEW strictly for independent audit logs or dead-letter error records.
Reconsider when
Avoid changing transaction isolation levels on individual methods unless resolving a documented concurrency race condition.
Runtime Architecture & Flow
High-value visual model
Propagation and Isolation Decision Flow
Propagation decides whether code joins, suspends, or creates a transaction. Isolation decides what concurrent data changes can be observed.
1. REQUIRED joins the use case
Most service methods should share the existing transaction boundary.
2. REQUIRES_NEW suspends the caller
Use only when nested work must commit independently, such as audit logging.
3. NESTED uses a savepoint when supported
It can roll back part of the work without abandoning the outer transaction.
4. Isolation controls concurrent visibility
Test dirty reads, non-repeatable reads, phantoms, and write skew against the target database.
Working Implementation
Code Walkthrough
Make an audit service write with REQUIRES_NEW so a declined payment still records who attempted it.
@Service
class OrderService {
private final AuditService audit;
@Transactional
void placeOrder(PlaceOrder command) {
orders.save(Order.from(command));
audit.record(command.requestId());
throw new PaymentDeclined();
}
}
@Service
class AuditService {
@Transactional(propagation = Propagation.REQUIRES_NEW)
void record(UUID requestId) {
auditEvents.save(new AuditEvent(requestId, Instant.now()));
}
}Synthesis & Production Verification
Deliberate Failure Condition
- Break: Call audit.record() inside placeOrder() where both use REQUIRED and the order save throws
- Observe: The audit trail vanishes exactly when you need it most
- Repair: REQUIRES_NEW suspends the outer transaction and commits independently—self-invocation bypasses this entirely because no proxy boundary is crossed.
Production Acceptance Criteria
- Prove REQUIRED joins and REQUIRES_NEW suspends via SQL logs
- Reproduce one read phenomenon at READ_COMMITTED
- Trigger self-invocation and explain why propagation did not apply
Key Takeaway
Propagation.REQUIRED joins active transactions; REQUIRES_NEW creates independent transaction boundaries for audit trails and dead letters.
Architectural & Technical Review
Core Interview Questions
Q01What is the difference between Propagation.REQUIRED and Propagation.REQUIRES_NEW?Show answer
REQUIRED joins the existing transaction if present; REQUIRES_NEW suspends the current transaction and starts an independent new one.
Q02What concurrency phenomena does READ_COMMITTED prevent?Show answer
It prevents dirty reads, but non-repeatable reads and phantom reads remain possible depending on database concurrency control.
Q03What occurs if an inner REQUIRES_NEW method fails and the exception is caught by the outer caller?Show answer
The inner transaction rolls back independently, while the outer transaction can still commit if the exception was handled.
Production Pitfalls
Critical Anti-Patterns
- Pitfall: Using REQUIRES_NEW without connection pool headroom — Each suspended transaction retains its physical connection while the new transaction borrows another, risking pool deadlocks.
- Pitfall: Overriding isolation levels arbitrarily — Setting non-standard isolation levels increases database locking contention without measurable benefit for standard queries.
Primary Documentation & Specifications
Lesson Primary References
Supplementary Module Resources