Является ли get_or_create () потокобезопасным - PullRequest
28 голосов
/ 20 июня 2011

У меня есть модель Django, доступ к которой можно получить только с помощью get_or_create(session=session), где сессия является внешним ключом для другой модели Django.

Поскольку я получаю доступ только через get_or_create(), я бы предположил, что у меня будет только один экземпляр с ключом для сеанса. Тем не менее, я нашел несколько экземпляров с ключами для одного сеанса. Что происходит? Это состояние гонки или get_or_create() работает атомарно?

Ответы [ 5 ]

39 голосов
/ 22 февраля 2012

НЕТ, get_or_create не атомарный .

Сначала запрашивается DB, существует ли удовлетворительная строка;база данных возвращается, Python проверяет результаты;если он не существует, он создает его.Между get и create может произойти все что угодно - и строка, соответствующая критериям get, будет создана каким-то другим кодом.

Например, в отношении вашей конкретной проблемы, если две страницы открытыпользователем (или выполняется несколько запросов ajax) одновременно, это может привести к сбою всех get, и для всех них create новая строка - с одним и тем же сеансом.

Itпоэтому важно использовать get_or_create только тогда, когда проблема дублирования будет обнаружена базой данных через некоторые unique / unique_together, так что даже если несколько потоков могут добраться доточка save (), только одна будет успешной, а другие вызовут ошибку IntegrityError, с которой вы можете поймать и справиться.

Если вы используете get_or_create с (набором) полей, которые не являются уникальнымив базе данных вы создадите дубликаты в своей базе данных, что редко является тем, что вам нужно.

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

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

Ссылки и цитаты:

12 голосов
/ 21 июня 2011

На самом деле это не потокобезопасно, вы можете посмотреть код метода get_or_create объекта QuerySet, в основном он выполняет следующее:

try:
    return self.get(**lookup), False
except self.model.DoesNotExist:
    params = dict([(k, v) for k, v in kwargs.items() if '__' not in k])
    params.update(defaults)
    obj = self.model(**params)
    sid = transaction.savepoint(using=self.db)
    obj.save(force_insert=True, using=self.db)
    transaction.savepoint_commit(sid, using=self.db)
    return obj, True

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

7 голосов
/ 21 июня 2011
2 голосов
/ 09 сентября 2012

У меня возникла эта проблема с представлением, которое вызывает 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
0 голосов
/ 21 июня 2011

Я думаю, что это не условие гонки. Условие гонки возникает, когда 2 или более потоков или процессов пытаются получить доступ к одному и тому же ресурсу, чтобы изменить его одновременно. Вы описываете ситуацию, когда у вас get_or_create много объектов, использующих один и тот же сеанс, что не является проблемой, поскольку вы не пытаетесь одновременно получить доступ к сеансу для изменения какого-либо атрибута.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...