Я использую JDO 2.3 в приложении. Я использовал хранилище данных Master/Slave для локального тестирования и недавно переключился на использование хранилища данных HRD для локального тестирования, а части моего приложения ломаются (что и следовало ожидать). Одна часть приложения, в которой происходит нарушение, заключается в том, что он быстро отправляет много писем - это из-за 1-секундной лимитированной вещи, которая не срабатывает при исключении параллельной модификации.
Хорошо, так что и следовало ожидать, поэтому я снова попробую повторить попытку перезагрузки после того, как они потерпят неудачу (может быть, это не лучший хак, но я просто пытаюсь заставить его работать быстро).
Но странно, что происходит. Некоторые из записей, которые должны быть успешными (те, которые НЕ получают исключение параллельной модификации) также терпят неудачу, даже если этап фиксации завершен, и запрос возвращает мой код успеха. Я вижу из журнала, что повторные запросы работают нормально, но эти другие запросы, которые, кажется, были совершены с первой попытки, я думаю, никогда не применялись ". Но из того, что я прочитал о фазе Apply, снова писать тот же объект должен заставить применить... но это не так.
Далее следует код. Некоторые примечания:
- Я пытаюсь использовать автоматическое кеширование JDO. Так вот где JDO использует memcache под обложками. Это фактически не работает, если вы не обернете все транзакции.
- все запросы выполняются, это чтение строки из объекта, изменение части строки и сохранение этой строки для объекта. Если эти запросы не были в транзакциях, у вас, конечно, проблема с "грязным чтением". Но с транзакциями изоляция должна находиться на уровне "сериализуемого", поэтому я не вижу, что происходит здесь.
- изменяемый объект является корневым объектом (не в группе)
- У меня есть транзакции между группами.
Соответствующий код (это упрощенная версия):
PersistenceManager pm = PMF.getManager();
Transaction tx = pm.currentTransaction();
String responsetext = "";
try {
tx.begin();
// I have extra calls to "makePersistent" because I found that relying
// on pm.close didn't always write the objects to cache, maybe that
// was only a DataNucleus 1.x issue though
Key userkey = obtainUserKeyFromCookie();
User u = pm.getObjectById(User.class, userkey);
pm.makePersistent(u); // to make sure it gets cached for next time
Key mapkey = obtainMapKeyFromQueryString();
// this is NOT a java.util.Map, just FYI
Map currentmap = pm.getObjectById(Map.class, mapkey);
Text mapData = currentmap.getMapData(); // mapData is JSON stored in the entity
Text newMapData = parseModifyAndReturn(mapData); // transform the map
currentmap.setMapData(newMapData); // mutate the Map object
pm.makePersistent(currentmap); // make sure to persist so there is a cache hit
tx.commit();
responsetext = "OK";
} catch (JDOCanRetryException jdoe) {
// log jdoe
responsetext = "RETRY";
} catch (Exception e) {
// log e
responsetext = "ERROR";
} finally {
if (tx.isActive()) {
tx.rollback();
}
pm.close();
}
resp.getWriter().println(responsetext);
ОБНОВЛЕНИЕ. Я уверен, что знаю, почему это происходит, но я все равно награжу награду всем, кто сможет ее подтвердить.
В принципе, я думаю, проблема заключается в том, что транзакции на самом деле не реализованы в локальной версии хранилища данных. Ссылки:
https://groups.google.com/forum/?fromgroups=#!topic/google-appengine-java/gVMS1dFSpcU https://groups.google.com/forum/?fromgroups=#!topic/google-appengine-java/deGasFdIO-M https://groups.google.com/forum/?hl=en&fromgroups=#!msg/google-appengine-java/4YuNb6TVD6I/gSttMmHYwo0J
Поскольку транзакции не реализованы, откат по существу не работает. Поэтому я получаю грязное чтение, когда две транзакции пытаются изменить запись в одно и то же время. Другими словами, A считывает данные, а B считывает данные одновременно. Попытка изменить данные, а B пытается изменить другую часть данных. А пишет в хранилище данных, затем пишет B, стирая изменения A. Затем B "откатывается" с помощью движка приложения, но поскольку откаты являются "нет-op" при работе в локальном хранилище данных, изменения B остаются, а A нет. Между тем, поскольку B - это поток, который выбрал исключение, клиент повторяет B, но не повторяет A (так как A предположительно является транзакцией, которая преуспела).