Spring JPA Concurrency Transactional or Lock

29 Views Asked by At

As per ACID properties, @Transactional should handle Isolation by default. For Isolation.REPEATABLE_READ, it should prevent dirty read, non repeatable read problems. I'm using MySQL database, Spring boot 3.2.3, Java 17

I'm trying to handle concurrency since a 3 days. Nothing is working.

I have a Product Entity, I'm trying to do addQuantity(id){q+10} and mulQuantity(id){q*2} which updates quantity field.

To demonstrate concurrency I'm trying to run two threads one adds other multiplies

My Expectation is that: Either it should add first and multiply next or vice versa, but in any case the updates should not be overriden

But with my approaches it is getting overriden

Controller

@PostMapping("/run")
    ResponseEntity<?> run() {

        ExecutorService executorService = Executors.newFixedThreadPool(4);
        executorService.submit(() -> productService.addQuantity(1L));
        executorService.submit(() -> productService.mulQuantity(1L));
        executorService.submit(() -> productService.addQuantity(2L));
        executorService.submit(() -> productService.mulQuantity(2L));

        executorService.shutdown();

        return ResponseEntity.ok("ok");
    }

Service

@Transactional
    public void addQuantity(Long id) {
        Product p = productRepository.findById(id).get();
        p.setQuantity(p.getQuantity() + 10);
        log.info("ADD id {}, q {}", id, p.getQuantity());
        productRepository.save(p);
    }

    @Transactional
    public void mulQuantity(Long id) {
        Product p = productRepository.findById(id).get();
        p.setQuantity(p.getQuantity() * 2);
        log.info("MUL id {}, q {}", id, p.getQuantity());
        productRepository.save(p);
    }

Entity

public class Product {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private int price;
    private int quantity;
}

My Approaches:

  1. I think this is Optimistic locking, @Vesrion, Got this ObjectOptimisticLockingFailureException which is ok, but not concurrent..
@Version
private int version;
  1. Adding Isolation level at add, multiply method, which is by default the same, overrides it
@Transactional(isolation = Isolation.REPEATABLE_READ)
  1. Only working way was, pessimistic lock on repository
@Lock(LockModeType.PESSIMISTIC_WRITE)
Optional<Product> findById(Long id);
  1. Did not try Isolation.SERIALIZATION, but I think it will work but not efficient

My questions:

1)I want to know, is adding @Transactional not sufficient to handle concurrency? (when I run two different transactions in mysql command line, it either waits or give dead lock, it works very well) But why there is an issue in Spring boot.

2)Do we really need to handle locking separately, no way out?

3) Ways to handle concurrency with transaction, with lock

4) Except for rollback what is use of @Transactional?

Open to provide more details, please help me out.

0

There are 0 best solutions below