Потерянное обновление — Java, Spring и JPAJAVA

Программисты JAVA общаются здесь
Ответить Пред. темаСлед. тема
Anonymous
 Потерянное обновление — Java, Spring и JPA

Сообщение Anonymous »

У меня возникла проблема на работе, которую я пытался решить несколько месяцев, и это сводит меня с ума.
Эту вещь трудно объяснить, она связана с некоторыми особенностями предметной области. это мне не разрешено обсуждать, и я не могу копировать и вставлять точный код. Я постараюсь изложить свои мысли как можно более понятно на некоторых репрезентативных примерах.
Коротко говоря, система состоит из корневого объекта, назовем его объектом MainDocument. Вокруг этого объекта вращаются несколько объектов. Сущность MainDocument имеет состояние. Назовем это состояние «MainDocumentState».
public class MainDocument {
@OneToOne
@JoinColumn(name = "document_state_id")
MainDocumentState state;
@Version
long version = 0L;
}

Доступно около 10 состояний, но в этом примере мы сосредоточимся на двух из них. Давайте назовем их ReadyForAuthorization и Authorized.
Это все, что вам нужно знать для примера.
О технологии, которые мы используем:
  • Spring
  • GWT Webapp
  • Java 1.6
  • Гибернация
  • JPA
  • БД Oracle.
О самой проблеме:
Есть раздел системы, который является критическим и обрабатывает большую часть входящего трафика. Назовем этот раздел «разделом авторизации». В этом разделе мы отправляем информацию через SOAP WS, предоставленную Таможенной и пограничной службой нашей страны, для авторизации основного документа на таможне.
Код выглядит следующим образом:
Код выглядит следующим образом: п>
@Transactional
public void authorize(Integer mainDocId) {

MainDocument mainDocument = mainDocumentService.findById(mainDocId);
// if document is not found, an exception is thrown.
Assert.isTrue(mainDocument.notAutorized(), "The document is already authorized");
// more business logic validations happen here. This validations are not important for the topic discussed here. They make sure that the document meets some basic preconditions.

try {

Transaction aTransaction = transactionService.newTransaction(); // creates a transaction, an entity stored in the database that keeps track of all the authorization service calls
try {
Response response = wsAuthroizationService.sendAuthorization(mainDocument.getId(), mainDocument.getAuthorizationId()); // take into account that sometimes this call can take between 2-4 minutes.
catch (Exception e) {
aTransaction.failed();
transactionService.saveOrUpdate(aTransaction);
throw e;
}
// the behaviour is the same for every error code.
if (response.getCode() != 0) {
aTransaction.setErrorCode(resposne.getCode());
transactionService.saveOrUpdate(aTransaction);
throw AuthroizationError("Error on auth");
}
aTransaction.completed();
mainDocument.setAuthorizationCode(0);
mainDocument.authorize(); // will change state to "Authorized"
} catch (Exception e) {
mainDocument.authorize(); // will not change state because authorizationCode != 0 or its null.
} finally {
saveOrUpdate(mainDocument);
}
}

Когда происходит потеря обновления и как это влияет на систему:
  • Идентификатор основного документа: 1@Thread-1 пытается авторизоваться
  • Документ не авторизован, выполнение продолжается
  • Проходит через веб-сервис и авторизуется ОК
    < li>Транзакция закрывается и происходит фиксация.
  • Пока 1 выполняет фиксацию, приходит MainDocument 1@Thread-2 и пытается
    аутентифицироваться.
  • 1 еще не сохраняется, Thread-2 пытается авторизоваться.
    Поток-2 отклонен WS с ответом «документ 1 уже авторизован».
  • Поток-2 пытается зафиксировать.
  • Поток-1 сначала фиксирует документ 1, Thread-2 фиксируется на втором месте.
MainDocument с id:1 сохраняется с состоянием ReadyForAuthorization, а правильное состояние должно быть Authorized.
Сложность возникает потому, что воспроизвести практически невозможно. Это происходит только в рабочей среде, и даже если я попытаюсь загрузить сервер сотнями вызовов, я не смогу добиться такого же поведения.
Реализованные решения:
  • Потоковый барьер: если два потока с одинаковым идентификатором MainDocument пытаются авторизоваться, последний вошедший будет отклонен. Он реализован с аспектом с порядком 100, поэтому выполняется после фиксации @Transactional. Протестировано и проверено на трассировке стека, которую транзакция фиксирует до того, как аспект перехватывает и удаляет поток из барьера.
  • @Version, которая работает в других разделах системы, вызывая исключение OptimisticLockException при попытке одной фиксации. чтобы переопределить другую фиксацию из более старой транзакции. В этом случае исключение OptimisticLockException не возникает.
  • Транзакция сохраняется с помощью @Transactional(propagation = REQUIRES_NEW), поэтому она не зависит от основной транзакции и фиксируется правильно. С помощью этих транзакций становится ясно, что потерянное обновление является проблемой, поскольку мы видим завершенную транзакцию с сообщением об успехе, а MainDocument сохраняется с другим состоянием, без ошибок в файле server.log.
  • Используя Imperva SecureSphere, мы можем проверять все обновления в определенной таблице. Мы можем ясно видеть, как первая транзакция фиксируется с правильным состоянием, а вторая транзакция перезаписывает первую.
Я был бы признателен, если бы кто-нибудь с параллелизмом и управлением транзакциями Опыт может дать мне несколько полезных советов о том, как отладить или воспроизвести проблему, или, по крайней мере, реализовать некоторые решения для уменьшения ущерба.
Для ясности: в час поступает более 1000 запросов. и 99,99% этих запросов заканчиваются правильно. Общее количество случаев возникновения данной проблемы составляет около 20 в месяц.
Добавлено 13.09.17:
The saveOrUpdate метод, который мы используем, если необходимо:
* "http://blog.xebia.com/2009/03/23/jpa-im ... -entities/" >JPA
* implementation patterns: Saving (detached) entities
*
* @param entity
*/
protected E saveOrUpdate(E entity) {
if (entity.getId() == null) {
getJpaTemplate().persist(entity);
return entity;
}
if (!getJpaTemplate().getEntityManager().contains(entity)) {
return merge(entity);
}
return entity;
}


Подробнее здесь: https://stackoverflow.com/questions/461 ... ng-and-jpa
Реклама
Ответить Пред. темаСлед. тема

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

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

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

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

  • Похожие темы
    Ответы
    Просмотры
    Последнее сообщение

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