Redisson Lock + @Transactional Still Corrupted Data Under Load
This failure mode is easy to miss in code review because the logic looks correct in isolation. Any Java service that mixes declarative transactions with distributed locks can silently corrupt data under load if the lock is released before the transaction commits.
A seemingly correct combination of Redisson distributed lock and Spring's @Transactional caused inventory to go negative under high concurrency. The lock was released in a finally block inside the transactional method, but Spring's AOP proxy had not yet called connection.commit(). Another thread could acquire the lock, read the uncommitted old state, and perform its own deduction, resulting in a dirty write.
The root cause is a scope mismatch: the lock's lifecycle ended before the transaction's lifecycle. Two fixes restore correctness. The recommended approach moves the lock acquisition and release to an outer service layer, so the transactional method returns and commits before the lock is released. An alternative uses TransactionTemplate to manually commit the transaction inside the locked block before releasing the lock.
A secondary Redisson pitfall is also noted: passing an explicit leaseTime to tryLock disables the WatchDog automatic renewal mechanism, risking premature lock expiry during long-running operations.
The bug is a classic AOP ordering problem: developers reason about source-code order, but the proxy interleaves transaction boundaries around the method, not inside it.
Many teams treat distributed locks and database transactions as interchangeable safety nets, but their lifecycles are independent and must be deliberately sequenced.
The WatchDog detail is a sharp edge in Redisson's API design — an optional parameter silently disables a safety feature, and the default behavior is safer than the explicit one.