Hibernate не снимает блокировку таблицы после завершения транзакцииJAVA

Программисты JAVA общаются здесь
Ответить
Anonymous
 Hibernate не снимает блокировку таблицы после завершения транзакции

Сообщение Anonymous »

Есть сервис, который управляет очередью: выбирает клиента оператору и записывает обоих в базу данных.

Код: Выделить всё

public class ClientService {

@Transactional
public ClientDTO method1(Long userId) {
//some code
return method2(userId);
}

@Transactional
public ClientDTO method2(Long userId) {
//some code
ClientDTO client = anotherClass.getClient();
return method3(userId, ClientDTO.getId());
}

@Transactional
public ClientDTO method3(Long userId, Long clientId) {
User user = userRepository.findByIdForUpdate(userId).get();
Client client = clientRepository.findByIdForUpdate(clientId).get();
client.setUser(user);
client.setStatus(Constants.CLIENT_STATUS_CALLED));
user.setStatus(Constants.OPERATOR_STATUS_WAITING_FOR_THE_CALLED_CLIENT);
result = mapper.toDTO(client);
//some code
return result;
}
}
Проблема в том, что этот алгоритм слишком сложен и требует слишком много времени для выполнения. И сервис работает в кластере: есть несколько экземпляров сервиса, которые параллельно выполняют этот код. Начались гонки: один клиент был закреплен за разными операторами или за одним оператором были закреплены разные клиенты.
Было решено ввести блокировки на уровне БД. Также было решено вынести в отдельный метод транзакции фрагмент кода, задающий статусы клиенту и оператору: чтобы эта операция выполнялась максимально атомарно.

Код: Выделить всё

public interface UserRepository extends GenericRepository {
@Lock(LockModeType.PESSIMISTIC_WRITE)
Optional findByIdForUpdate(Long id);
}

public interface ClientRepository extends GenericRepository {
@Lock(LockModeType.PESSIMISTIC_READ)
Optional findByIdForUpdate(Long id);
}

public class ClientService {

//@Transactional
public ClientDTO method1(Long userId) {
//some code
return method2(userId);
}

//@Transactional
public ClientDTO method2(Long userId) {
//some code
final short tryCount = 3;
for (int i = 0; i < tryCount; i++) {
ClientDTO client = anotherClass.getClient();
try {
result = method3(userId, ClientDTO.getId());
} catch (ClientIsBusyException e) {
log.info("try again");
continue;
}
return result;
}
}

//@Transactional
public ClientDTO method3(Long userId, Long clientId) throws ClientIsBusyException {
result = clientTransactionalService.clientSetStateCalled(userId, clientId);
//some code
return result;
}
}

public class ClientTransactionalService {
@Transactional(propagation = Propagation.REQUIRES_NEW)
public ClientDTO clientSetStateCalled(Long userId, Long clientId) throws ClientIsBusyException {
User user = userRepository.findByIdForUpdate(userId).get();
if(Constants.OPERATOR_STATUS_WAITING_FOR_THE_CALLED_CLIENT.equals(user.getStatus().getId())
throw new IllegalStateException();

Client client = clientRepository.findByIdForUpdate(clientId).get();
if (client.getUser() != null && !Objects.equals(client.getUser().getId(), userId))
throw new ClientIsBusyException();

client.setUser(user);
client.setStatus(Constants.CLIENT_STATUS_CALLED));
user.setStatus(Constants.OPERATOR_STATUS_WAITING_FOR_THE_CALLED_CLIENT);
return mapper.toDTO(client);
}
}
Но это привело к другим проблемам. Теперь есть куча кода, который не включен в транзакцию REQUIRES_NEW. Я не знаю, в какой транзакции он выполняется.
Теперь в некоторых сценариях, когда метод1 вызывается дважды подряд, в первый раз он блокирует запись в таблице Client, но не снимает блокировку в конце метода clientSetStateCalled. При втором вызове clientRepository.findByIdForUpdate зависает, ожидая истечения срока блокировки.
У меня два вопроса:

[*]Как Hibernate управляет транзакциями, если @Transactional не установлен?
[*]Как я могу гарантировать, что блокировка таблиц будет снята на 100% с помощью метода clientSetStateCalled?


Подробнее здесь: https://stackoverflow.com/questions/793 ... ction-ends
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «JAVA»