Реально ли вообще хоть что то гарантировать в GAE datastore (Java, JDO)?

Изучаю/работаю с Google App Engine для Java.


Выбрал Java и JDO как среду разработки из-за удобного описания структуры данных прямо в коде (для проектов средней сложности более чем удобно), но, похоже именно это создает больше проблем чем удобств.


Помимо самих особенностей и 'неудобств' по работе с datastore (это все таки key-value база данных) столкнулся с тем что похоже нет никаких гарантий, что записанное несколькими секундами ранее будет доступно сейчас, мало того, без каких либо ошибок запись может вообще не произойти.

* Простейший пример: объект Test {id, amount}, необходимо создать 1 экземпляр, если он еще не был создан, и при каждом вызове сервлета увеличивать amount на 1.

Версия sdk 1.5.2, windows 7. Проект создается по умолчанию в eclipse с установленным google appengine plugin + создаем файл Test.java и изменяем TestServlet.java, код тут paste.ly/B44n


Вот какой вывод получается:
# curl localhost:8888/test

Test is empty

OK


# curl localhost:8888/test

Test length:1

Test[1]: 1

OK


# curl localhost:8888/test

Test length:1

Test[1]: 1

OK


# curl localhost:8888/test

Test length:1

Test[1]: 2

OK


# curl localhost:8888/test

Test length:1

Test[1]: 3

OK


# curl localhost:8888/test

Test length:1

Test[1]: 4

OK


# curl localhost:8888/test

Test length:1

Test[1]: 4

OK
А в логах сервера:
22.08.2011 14:29:13 com.google.appengine.tools.development.DevAppServerImpl start

INFO: The server is running at localhost:8888/

22.08.2011 14:29:17 test.TestServlet doGet

WARNING: new amount: 1

22.08.2011 14:29:18 test.TestServlet doGet

WARNING: add amount: 2

22.08.2011 14:29:20 test.TestServlet doGet

WARNING: add amount: 2

22.08.2011 14:29:22 test.TestServlet doGet

WARNING: add amount: 3

22.08.2011 14:29:23 test.TestServlet doGet

WARNING: add amount: 4

22.08.2011 14:29:24 test.TestServlet doGet

WARNING: add amount: 5

22.08.2011 14:29:30 test.TestServlet doGet

WARNING: add amount: 5


Обратите внимание, между вызовами было прилично времени, секунды… компьютер совсем не нагруженный, достаточно мощный (4-ядерный феном с 8гб озу), но по каким то причинам, далеко не при каждом вызове информация об увеличении Test.amount сохранялась.


К тому же во время тестов у меня встречались ситуации, когда созданная ранее запись просто не отображалась в результате запроса select, соответственно в данном случае создавались несколько записей. Понятно в правильной реализации конкретно этой задачи можно было бы заранее (в коде) самостоятельно определить уникальный ключ объекта, но само собой это не всегда возможно!


p.s. почти наверняка я в коде допустил какую то совершенно нубскую ошибку, но не могу понять какую.


p.p.s. использование метода класса Test — public void addAmount() {this.amount++;} не решило проблемы, явно причина где то в сохранении данных в базе, но я не пойму как принудительно сохранить изменения, по документации это должно происходить при pm.close() или pm.makePersistent(...)
  • Вопрос задан
  • 2746 просмотров
Пригласить эксперта
Ответы на вопрос 7
@vtysh
Конкретно с JPA не работал, но работал с Hibernate. Попробуйте вызвать flush после makePersistance
db.apache.org/jdo/api20/apidocs/javax/jdo/PersistenceManager.html#flush()
Ответ написан
Комментировать
@rPman Автор вопроса
pm.flush() в finally ничего не изменил.
Ответ написан
@rPman Автор вопроса
для очистки совести проверил, конечно ничего не изменилось, там d=pm.getObjectById(Test.class, new_id); исключительно для тестов и вывода в лог данных далее по коду.

Гарантированно сериями по 2 запроса данные тупо никуда не записываются, при этом в пределах метода сервлета данные успешно записаны, доступны по повторным запросам через Query, но по завершению метода и выдачи результата клиенту изменения куда то исчезают

p.s. в основном проекте все еще хуже… в один объект данные сохраняются, в другом, записываемым в этом же методе — нет, группы у объектов разные, в транзакцию завернуть еще не могу, я пока еще не придумал красиво объединить в группу слабо связанные сущности, по документации, чтобы это сделать — нужна связь типа 1к1 или 1кМ, либо что то нетривиальное с ключами
Ответ написан
Комментировать
@rPman Автор вопроса
Стало еще страшнее!!! я ошибся выше в коментарии, не перепроверил, если запросить после изменений данных данные объекта через Query execute() и .get(0) то данные ВООБЩЕ никогда не записываются!
Добавив код после обновления данных:
q=pm.newQuery(Test.class);
qdr=(List)q.execute();
q.closeAll();
resp.getWriter().println(«Test repeat read: „+d.getAmount()+“, queryed:»+qdr.get(0).getAmount());

т.е. вывод всегда такой:
# curl 127.0.0.1:8888/test
Test length:1
Test[1]: 44
Test repeat read: 45, queryed:44

# curl 127.0.0.1:8888/test
Test length:1
Test[1]: 44
Test repeat read: 45, queryed:44

# curl 127.0.0.1:8888/test
Test length:1
Test[1]: 44
Test repeat read: 45, queryed:44
Ответ написан
@rPman Автор вопроса
Залил приложение на appspot.com (настройки базы High Replication), Test.amount увеличивается успешно, но проскакивает такое:
# curl blablabla.appspot.com/test
Test length:1
Test[1]: 9
Test repeat read: 10, queryed:9

# curl blablabla.appspot.com/test
Test length:1
Test[1]: 10
Test repeat read: 11, queryed:10

# curl blablabla.appspot.com/test
Test length:1
Test[1]: 11
Test repeat read: 12, queryed:12

# curl blablabla.appspot.com/test
Test length:1
Test[1]: 12
Test repeat read: 13, queryed:12

# curl blablabla.appspot.com/test
Test length:1
Test[1]: 13
Test repeat read: 14, queryed:13

Т.е. нет никакой гарантии что Query вернет верные данные, но каким то образом данные все равно корректно изменяются.

p.s. уже размышляю по исключению из проекта любых Query и работа с данными исключительно по id (а особые списки реализовывать самостоятельно) все еще надеюсь что я где то ошибся, ибо это нереально глупое поведение БД
Ответ написан
Mecid
@Mecid
Что-то я не встречал таких проблем как у вас, пользуюсь App Engine почти 2 года.
Ответ написан
@vtysh
Пример мне не давал покоя долго. Не знаю, на сколько еще актуально, но похоже в последних версиях GAE таки что-то починили. Теперь не могу воспроизвести проблему даже без использования транзакций.
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы