PESSIMISTIC_WRITE не работает в многопоточности

Рейтинг: 1Ответов: 0Опубликовано: 25.01.2023

Java 1.8, MariaDB 10.6, IntelliJ IDEA 2022.3.1 (Ultimate Edition).

У меня был следующий код:

@Path("/operation")
@RequestScoped
public class OperationController {
    
    @EJB
    private OperationLocalBean operationBean;

    @POST
    @Path("/{id}")
    public void doOperation(@PathParam("id") Integer id) {
        operationBean.doOperation(id);
    }
}
@Stateless
@LocalBean
public class OperationLocalBean implements OperationBean {

    @PersistenceContext(unitName = "operation")
    private EntityManager em;
    
    @Override
    public void doOperation(int id) {
        Operation operation = em.find(Operation.class, id);
        em.detach(operation);
        
        if (!operation.isApplied()) {
            operation.do();
            operation.setApplied(true);
            em.merge(operation);
        }
    }
    
}

Существовала проблема, что когда два потока одновременно вызывали эндпоинт /operation/{id}, они одновременно считывали одно и то же значение из базы данных, вследствие чего одна операция выполнялась дважды.

Вместо EnityManager#find, который ранее использовался для чтения сущности из БД, я написал следующий метод:

  public Operation getAndLock(int id) {
    final long lockTimeoutInMilliseconds = 1000L;

    Map<String, Object> properties = new HashMap<>();
    properties.put("javax.persistence.lock.timeout", lockTimeoutInMilliseconds);

    return em.find(Operation.class, id, LockModeType.PESSIMISTIC_WRITE, properties);
  }

Сейчас код метода OperationLocalBean#doOperation выглядит так:

@Override
public void doOperation(int id) {
    Operation operation = getAndLock(id);
    em.detach(operation);
        
    if (!operation.isApplied()) {
        operation.do();
        operation.setApplied(true);
        em.merge(operation);
    }
}

Теперь проблема ушла, второй поток читает значение из БД только после того, как первый поток закроет свою транзакцию.

Но я снова сталкиваюсь с той же проблемой, если ставлю точку остановы на последнюю строку метода:

return em.find(Operation.class, id, LockModeType.PESSIMISTIC_WRITE, properties);

Когда первый поток останавливается на данной строке, я жду полсекунды, отпускаю его, после чего на строке останавливается второй поток, и я отпускаю и его. После этого операция выполняется два раза подряд, т.е. второй поток считывает значение из БД до того, как транзакция первого потока завершится.

Почему так происходит? Является ли это сайд-эффектом от использования дебаггера?

Ответы

Ответов пока нет.