Параллелизм с Objectify в GAE

Я создал тестовое веб-приложение для проверки сохранения-чтения-удаления Entities, я создал простой цикл для сохранения Entity, извлечения и изменения, а затем удаления его 100 раз.

На каком-то интервале цикла проблем нет, однако есть интервалы, когда возникает ошибка, которая Entity уже существует и, следовательно, не может быть сохранена (я добавил пользовательскую обработку исключений).

Также через какой-то интервал цикла Entity нельзя изменить, потому что он не существует, и, наконец, через какой-то интервал Entity нельзя удалить, потому что он не существует.

Я понимаю, что цикл может быть настолько быстрым, что операция с хранилищем данных Appengine еще не завершена. Таким образом, возникают такие ошибки, как Entity does not exist, при попытке доступа к нему или операция удаления еще не завершена, поэтому создание объекта с тем же ID еще не может быть создано и так далее.

Однако я хочу понять, как справиться с такой ситуацией, когда параллельная операция выполняется с Entity.


person quarks    schedule 05.07.2012    source источник
comment
Это требует гораздо большего объяснения... эти ошибки не имеют никакого смысла без контекста того, что вы делаете. Однако, если бы мне пришлось угадывать, похоже, что вы выполняете асинхронные операции и не завершаете их.   -  person stickfigure    schedule 05.07.2012
comment
@stickfigure Рассмотрим пример двух потоков, пытающихся обновить Entity, если эти два потока получили доступ к Entity примерно в одно и то же время, а затем обновили Entity, скажем, увеличили значение, тогда теоретически после операции сохранения будет отсутствовать приращение любого из двух потоков   -  person quarks    schedule 06.07.2012
comment
Именно для этого и были изобретены транзакции :-)   -  person stickfigure    schedule 06.07.2012
comment
@stickfigure Да, я только что прочитал это с сайта кода Objectify, я действительно реализовал класс DAOT, который работает сейчас, я считаю, что шаблоны DAOT тоже хорошо протестированы.   -  person quarks    schedule 06.07.2012
comment
@stickfigure Однако после прочтения вики и кода кажется, что он основан на ConcurrentModificationException, чтобы гарантировать, что он не сохраняется в то же время, однако, как насчет чтения значений счетчика, есть ли исключение, когда приложение пытается прочитать ключ одновременно?   -  person quarks    schedule 06.07.2012
comment
@stickfigure Я имею в виду, как и в традиционном примере, когда два потока читают один и тот же ключ одновременно и увеличивают его? Я имею в виду, что значение, которое оба потока имеют и будут фиксировать, будет неправильным. Я не вижу в коде, который решает это в коде DAOT?   -  person quarks    schedule 06.07.2012
comment
Транзакция вызовет исключение ConcurrentModificationException при фиксации, если какой-либо другой txn первым зафиксировал изменение объекта. Так что более медленный выйдет из строя. Шаблон DAOT (и метод transact() в Ofy4) будет автоматически повторять ошибки параллелизма до тех пор, пока txn не завершится успешно.   -  person stickfigure    schedule 06.07.2012


Ответы (1)


Насколько я понимаю, вы делаете что-то вроде следующего:

for i in range(0,100):
    ent = My_Entity()    # create and save entity
    db.put(ent)
    ent = db.get(ent.key())   # get, modify and save the entity
    ent.property = 'foo'
    db.put(ent)
    ent.get(ent.key())  # get and delete the entity
    db.delete(my_ent)

с некоторой проверкой ошибок, чтобы убедиться, что у вас есть объекты для удаления, изменения, и вы сталкиваетесь с кучей ошибок при поиске объекта для удаления или изменения. Как вы говорите, это потому, что вызовы не гарантированно выполняются по порядку.

Однако я хочу понять, как справиться с такой ситуацией, когда параллельная операция выполняется с сущностью.

Лучше всего для этого пакетировать любые модификации, которые вы делаете для сохраняющейся сущности. Например, если вы собираетесь создавать/сохранять/модифицировать/сохранять или изменять/сохранять/удалять, по возможности попробуйте объединить эти шаги (т.е. создать/изменить/сохранить или изменить/удалить). Это не только позволит избежать ошибок, которые вы видите, но также сократит ваши RPC. Следуя этой стратегии, приведенный выше цикл будет сокращен до...

prop = None
for i in range(0,100):
    prop = 'foo'

Другими словами, для всего, что требует быстрой установки/удаления, просто используйте локальную переменную. Это ответ GAE для вас. После того, как вы разберетесь со всеми быстрыми вещами, вы не сможете сохранить эту информацию в сущности.

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

РЕДАКТИРОВАТЬ: Вы также можете посмотреть на API конвейеров.

person Bovard    schedule 05.07.2012
comment
Правильно, пакетная транзакция подходит для создания/изменения/сохранения, есть еще одна проблема, которую я хочу решить, то есть когда два потока пытаются обновить сущность (скажем, значение в баллах) одновременно, это означает, что когда оба потока сохранить, значение не является ожидаемым значением, скажем, для функции точек приращения - person quarks; 06.07.2012
comment
В качестве обходного пути вы можете использовать метод, который Бретт Слаткин предлагает в своем выступлении Google IO Создание конвейеров данных с высокой пропускной способностью с помощью Google App Engine 33:00 - person Bovard; 10.07.2012