How can I prevent Race Condition
How can I prevent Race Condition in this method:
@Lock
public boolean delete(int id) {
someEntity byId = someRepository.findById(id).orElseThrow(() -> new NotFoundException());
someRepository.delete(byId);
return true;
}
I have done @Lock
@Aspect
@Component
@EnableAspectJAutoProxy
public class LockAspect {
private final ReentrantLock reentrantLock;
public LockAspect() {
reentrantLock = new ReentrantLock();
}
@Around("@annotation(com.example.aspect.Lock)")
public void aroundDelete(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
reentrantLock.lock();
try {
proceedingJoinPoint.proceed();
} finally {
reentrantLock.unlock();
}
}
But this lock only blocks the app not really solves the problem the same with
Also I was thing about @Transactional(isolation=Isolation.REPEATABLE_READ)
or @Transactional(isolation= Isolation.SERIALIZABLE)
annotation above the delete
method but I'm not sure if that will solve the problem. It is spring app.
There are several ways of solving this, popular options are:
- Don't do a separate SELECT, just issue a DELETE. And if you get OptimisticLockException from Hibernate, then you know for sure the record didn't exist. It looks like you're using Spring Data, you can either create a
@Modifying
method for this or just userepo.getOne()
instead ofrepo.findXxx()
. - Use Repeatable Read isolation level - the database should return an error if another transaction updated the record concurrently. Eg. here are PostgreSQL docs (technically in PG this is a Snapshot isolation level though).
- Use pessimistic locking with
select .. for update
, this will not allow DML operations to proceed with the row until it's unlocked (transaction is finished).