Рубрики
Без рубрики

Опытные опасности get_or_create: гоночные условия

get_or_create, это удивительная утилита помощника, пока не будет. Есть кивна для того, что мы собираемся … Теги с Python, Django, Orm.

get_or_create , это удивительный помощник помощника, пока не будет. Есть кивку на то, о чем мы собираемся говорить в Документы :

Этот метод является атомным, предполагающим правильное использование, правильная конфигурация базы данных и правильное поведение базовой базы данных. Однако, если уникальность не используется на уровне базы данных для _ kwargs используется в get_or_create Позвоните (см. Уникальный или_ Unique_together ) Этот метод склонен к гонке-состоянию, которое может привести к нескольким рядам с теми же параметрами, которые одновременно вставляются одновременно.

Давайте поговорим об этом более подробно. Вот интересные биты исходного кода для get_or_create :

lookup, params = self._extract_model_params(defaults, **kwargs)
try:
    return self.get(**lookup), False
except self.model.DoesNotExist:
    return self._create_object_from_params(lookup, params)

Довольно объяснительный, и делает именно то, что подразумевает название. И если вы пойдете дальше в _create_object_from_params , вы заметите, что это намного больше, чем просто позвонить в .create () Отказ Вот что там происходит:

try:
    with transaction.atomic(using=self.db):
        obj = self.create(**params)
    return obj, True
except IntegrityError:
    exc_info = sys.exc_info()
    try:
        return self.get(**lookup), False
    except self.model.DoesNotExist:
        pass
    six.reraise(*exc_info)

Это круто – это фактически учитывает условия расы. Он пытается создать объект, но если эта операция бросает IntegrityError. Он делает поиск снова и пытается вернуть то, что он находит.

Проблема в том, что в том, что если вы попадаете в эту часть кода в одном потоке (означающий поиск уже имел место и ничего не вернул) На объекте, который не имеет ограничения уникальности на атрибутах, вы выполняете поиск на основе , если один создан в другой поток, создание в этом потоке будет не бросить IntergeityError , и вы в конечном итоге с двумя! Это может быть хорошо – на данный момент. Ведь ваш звонок в get_or_create Вернул экземпляр, соответствующую вашим параметрам, и так вызов в другом потоке, и оба будут продолжать свой веселый путь.

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

Мораль истории? Не используйте get_or_create На объектах, которые не имеют ограничения уникальности на атрибутах, вы выполняете поиск на основе, На уровне базы данных Отказ

Оригинал: “https://dev.to/adriennedomingus/the-perils-of-getorcreate-race-conditions-1gh5”