We added one annotation. Our API latency doubled. 💀
No slow SQL. No missing indexes. No traffic spike. Just one innocent-looking @Transactional.

@Transactional
public void updateOrder() {
Order order = repository.findById(id);
paymentService.verifyPayment(); // ~800ms
repository.save(order);
}
At first glance, nothing looked wrong. But the transaction starts before the external API call.
That means:
- A database connection is acquired.
- The transaction stays open while waiting about 800 ms for another service.
- That connection can’t return to the pool.
At low traffic, nobody notices. At production scale, hundreds of requests hold database connections far longer than necessary.
The result:
- Connection-pool exhaustion
- Requests waiting for available connections
- API latency spiking across the system
The database wasn’t slow. The queries weren’t slow. We were holding an expensive resource for too long.
The fix was surprisingly simple:
External API call
↓
@Transactional
fetch → update → save
Keep transactions for database work only. @Transactional isn’t just about whether you use it. It’s also about where you use it.
A few extra milliseconds inside a transaction might seem harmless until they’re multiplied across thousands of concurrent requests.