PESSIMISTIC_WRITE не работает в многопоточности
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);
Когда первый поток останавливается на данной строке, я жду полсекунды, отпускаю его, после чего на строке останавливается второй поток, и я отпускаю и его. После этого операция выполняется два раза подряд, т.е. второй поток считывает значение из БД до того, как транзакция первого потока завершится.
Почему так происходит? Является ли это сайд-эффектом от использования дебаггера?