Проблема заключается в этой части:
if vote.count(1) == 0:
obj = VoteRecord()
obj.user = user
obj.option = option
obj.put()
Без транзакции ваш код может выполняться в следующем порядке в двух экземплярах интерпретатора:
if vote.count(1) == 0:
obj = VoteRecord()
obj.user = user
if vote.count(1) == 0:
obj = VoteRecord()
obj.user = user
obj.option = option
obj.put()
obj.option = option
obj.put()
Или любая странная комбинация. Проблема состоит в том, что тест подсчета снова запускается до того, как произошел пут, поэтому второй поток проходит первую часть условного выражения вместо второй.
Вы можете это исправить, поместив код в функцию и затем используя
db.run_in_transaction()
для запуска функции.
Проблема в том, что вы, похоже, полагаетесь на количество объектов, возвращаемых запросом, для логики принятия решения, которую необходимо ввести в транзакцию. Если вы прочитаете доклады о вводе / выводе Google или посмотрите на группу, вы увидите, что это не рекомендуется. Это потому, что вы не можете выполнить транзакцию запроса. Вместо этого вы должны где-то сохранить счет как значение сущности, запросить его вне функции транзакции, а затем передать ключ для этой сущности в вашу функцию транзакции.
Вот пример функции транзакции, которая проверяет свойство объекта. Ключ передается в качестве параметра:
def checkAndLockPage(pageKey):
page = db.get(pageKey)
if page.locked:
return False
else:
page.locked = True
page.put()
return True
Только один пользователь одновременно может заблокировать эту сущность, и никогда не будет дублирующихся блокировок.