I have this method in my interface which extends CrudRepository:
@Lock(LockModeType.PESSIMISTIC_WRITE)
Voucher findTop1ByUsedFalseAndProductOrderByIdAsc(Product product);
And this component:
@Component
@Transactional
public class VoucherFetchComponent {
@Autowired
private VoucherRepository voucherRepository;
public Voucher fetch(Product product) {
Voucher voucher=voucherRepository.findTop1ByUsedFalseAndProductOrderByIdAsc(product);
if(voucher==null)
return null;
voucher.setUsed(true);
voucherRepository.save(voucher);
return voucher;
}
}
The problem is concurrently executing fetch() method. (assume that I only have 1 voucher left in my DB)
What I expect:
1- Services A and B call the fetch() method of VoucherFetchComponent
2- Service A (or B) locks the row, updates one of it's fields and returns
3- The other service can now access the row which was locked. But now the query doesn't match the given criteria (used=false), so it returns null.
What I get
1 and 2 just like above
3- The other service returns the old object which has the parameter (used=false) but it had been updated before!
If you look carefully at the javadoc of @Lock it states that:
Annotation used to specify the LockModeType to be used when executing the query.
So the lock is active only during the query execution.. after the query is done.. the lock is released. It is not active for the duration of the entire transaction as you expect it to be.
I would suggest using standard locking mechanism:
VoucherRepository
public void lock(Voucher voucher, LockModeType lockType){
entityManager.lock(voucher, lockType);
VoucherFetchComponent
public Voucher fetch(Product product) {
Voucher voucher=voucherRepository.findTop1ByUsedFalseAndProductOrderByIdAsc(product);
if(voucher==null)
return null;
voucherRepository.lock(voucher, LockModeType.PESSIMISTIC_WRITE);
voucher.setUsed(true);
voucherRepository.save(voucher);
return voucher;
}
Once the transaction finishes, the lock will be released.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With