4

У меня проблемы с конкуренцией в Google App Engine и пытаюсь понять, что происходит.Проблемы с контентом в Google App Engine

У меня есть обработчик запросов с аннотацией:

@ndb.transactional(xg=True, retries=5) 

.ми в этом коде я выборка некоторых вещей, обновить некоторые другие и т.д., но иногда ошибку, как это приходит в журнале во время запроса:

16:06:20.930 suspended generator _get_tasklet(context.py:329) raised TransactionFailedError(too much contention on these datastore entities. please try again. entity group key: app: "s~my-appname" 
path < 
    Element { 
    type: "PlayerGameStates" 
    name: "hannes2" 
    } 
> 
) 
16:06:20.930 suspended generator get(context.py:744) raised TransactionFailedError(too much contention on these datastore entities. please try again. entity group key: app: "s~my-appname" 
    path < 
    Element { 
     type: "PlayerGameStates" 
     name: "hannes2" 
    } 
    > 
) 
16:06:20.930 suspended generator get(context.py:744) raised TransactionFailedError(too much contention on these datastore entities. please try again. entity group key: app: "s~my-appname" 
    path < 
    Element { 
     type: "PlayerGameStates" 
     name: "hannes2" 
    } 
    > 
) 
16:06:20.936 suspended generator transaction(context.py:1004) raised TransactionFailedError(too much contention on these datastore entities. please try again. entity group key: app: "s~my-appname" 
    path < 
    Element { 
     type: "PlayerGameStates" 
     name: "hannes2" 
    } 
    > 
) 

..следующий по следам стека. При необходимости я могу обновить всю трассировку стека, но это довольно долго.

Я не понимаю, почему это происходит. Глядя на строку в моем коде, появляется исключение, я запускаю get_by_id на совершенно другом объекте (Round). «PlayerGameStates», имя «hannes2», упомянутое в сообщениях об ошибках, является родителем другого объекта GameState, который был get_async: из базы данных было несколько строк ранее; не

# GameState is read by get_async 
gamestate_future = GameState.get_by_id_async(id, ndb.Key('PlayerGameStates', player_key)) 
... 
gamestate = gamestate_future.get_result() 
... 

Weird (?) Дело в том, не существует запись в хранилище данных, происходящих в этой сущности. Мое понимание заключается в том, что ошибки конкуренции могут возникать, если один и тот же объект обновляется одновременно, в параллельном порядке. Или, может быть, если слишком много записей происходит за короткий промежуток времени.

Но может ли это случиться при чтении объектов также? («приостановил генератор получить ..» ??) И, это происходит после 5 ndb.transaction повторов ..? Я ничего не вижу в журнале, который указывает, что были сделаны какие-либо попытки.

Любая помощь очень ценится.

+2

Я бы посмотрел на вашу ключевую структуру. Конфликт не только на уровне сущности. Вам нужно также изучить родителей. Посмотрите на сферу своих групп сущностей, чтобы понять, почему у вас возникают разногласия. –

+0

Спасибо. Я пытался удержать сущностные группы небольшими и избегать разногласий - но я еще раз рассмотрю. В этом случае в группе сущностей имело место совпадение с родительским «ndb.Key (« PlayerGameStates »,« hannes2 »)», правильно? Я до сих пор не понимаю, почему ** чтение ** из него вызывает исключение/разглашение? Где я могу узнать об этом подробнее? – boffman

+3

@TimHoffman Хорошо, это было найдено в разделе [Документация транзакций между группами] (https://cloud.google.com/appengine/docs/python/datastore/#Python_Cross_group_transactions): «Примечание: первое чтение объекта группа в транзакции XG может вызывать исключение TransactionFailedError, если есть конфликт с другими транзакциями, обращающимися к той же группе сущностей. Это означает, что даже транзакция XG, которая выполняет только чтения, может выйти из строя с помощью исключения параллелизма ». – boffman

ответ

3

Да, дело может иметь место как для операций чтения, так и для записи.

После того, как транзакция начинается - в вашем случае, когда вызывается обработчик, аннотированный с помощью @ndb.transactional() - любая группа лиц, к которой обращаются (с помощью операций чтения или записи, не имеет значения), сразу же помечена как таковая. В этот момент неизвестно, будет ли к концу транзакции писать op или нет - это даже не имеет значения.

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

Примечания: этого утверждения является НЕ эмулируется на сервере разработки, то можно увидеть только при развертывании на GAE, с реальным датастором!

Что может добавить к путанице, это автоматическое повторение транзакций, которое может произойти после как фактических конфликтов записи, так и просто конфликтов доступа. Эти повторы могут появляться у конечного пользователя как подозрительное повторное выполнение некоторых путей кода - обработчика в вашем случае.

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

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

Обычно, когда вы сталкиваетесь с такими проблемами, вам необходимо повторно посетить структуру данных и/или способ доступа к ним (ваши транзакции). В дополнение к решениям, поддерживающим сильную согласованность (что может быть довольно дорого), вы можете повторно проверить, действительно ли согласованность является обязательной. В некоторых случаях это добавляется как требование одеяла только потому, что, похоже, упрощает вещи. По моему опыту это не так:

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

Смежные вопросы