У меня есть модель Django, доступ к которой можно получить только с помощью get_or_create (session = session)
, где session - это внешний ключ для другого Django. модель.
Поскольку я получаю доступ только через get_or_create ()
, я могу представить, что у меня будет только один экземпляр с ключом к сеансу. Однако я нашел несколько экземпляров с ключами к одному и тому же сеансу. Что случилось? Это состояние гонки, или get_or_create ()
работает атомарно?
У меня была эта проблема с целью, которая называет get_or_create
.
Я использовал Gunicorn с несколькими рабочими, поэтому, чтобы проверить его, я изменил количество рабочих на 1, и это заставило проблему исчезнуть.
Самое простое решение, которое я нашел, было заблокировать стол для доступа. Я использовал этот декоратор для блокировки каждого представления (для PostgreSQL):
http://www.caktusgroup.com/blog/2009/05/26/explicit-table-locking-with- postgresql-and-django /
РЕДАКТИРОВАТЬ: я обернул оператор блокировки в этом декораторе в попытке / кроме, чтобы иметь дело с механизмами БД без его поддержки (SQLite во время модульного тестирования в моем случае):
try:
cursor.execute('LOCK TABLE %s IN %s MODE' % (model._meta.db_table, lock))
except DatabaseError:
pass
НЕТ, get_or_create не является не атомарным .
Сначала запрашивается DB, существует ли удовлетворительная строка; база данных возвращается, Python проверяет результаты; если он не существует, он создает его. Между get
и create
может произойти все что угодно - и строка, соответствующая критериям get
, будет создана каким-то другим кодом.
Например, к вашей конкретной проблеме, если пользователь открывает две страницы (или выполняется несколько запросов ajax) одновременно, это может привести к сбою всех get
, и для всех них create
a новый ряд - с той же сессией.
Таким образом, важно использовать только get_or_create
, когда проблема дублирования будет обнаружена базой данных через некоторые unique
/ unique_together
, так что, даже если несколько потоки могут добраться до точки save (), только один из них будет успешным, а другие вызовут ошибку IntegrityError, с которой вы можете поймать и разобраться.
Если вы используете get_or_create
с (набором) полей, которые не являются уникальными в базе данных, вы создадите дубликаты в вашей базе данных, что редко бывает тем, что вам нужно.
В общем, больше: не полагайтесь на свое приложение для обеспечения уникальности и избегайте дубликатов в вашей базе данных! Это работа с базой данных! (хорошо, если вы не заключите свои критические функции в некоторые блокировки, действительные для ОС, но я все равно рекомендую использовать базу данных).
С этими предупреждениями, используемыми правильно get_or_create
, это легко читаемая, легко записываемая конструкция, которая идеально дополняет проверки целостности базы данных.
Ссылки и цитаты: