Возможно ли, что транзакция .atomi c не работает должным образом? - PullRequest
0 голосов
/ 27 апреля 2020

enter image description here

Это представление API DRF для записи, как. Когда кому-то нравится запись, я вставляю аналогичную запись в таблицу entry_like, и плюс на 1 к полю likes_num в другой таблице entry. Но что-то пошло не так, что некоторые из entry_like записей, соответствующих одной записи, меньше, чем поле likes_num в таблице entry. Я не знаю, почему он не работает, как ожидалось, даже метод post с включенным декоратором transaction.atomic. Есть ли случаи, когда декоратор transaction.atomic работает не так, как ожидалось?

Ответы [ 2 ]

2 голосов
/ 27 апреля 2020

Да, я думаю, что это тот случай, когда transaction.atomic() работает не так, как вы ожидаете.

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

Вы ожидаете, что она будет работать так, как если бы уровень изоляции был SERIALIZABLE. Фактически уровень изоляции по умолчанию в Django равен READ COMMITTED. И на этом уровне изоляции, если у вас есть две из этих транзакций, работающих одновременно, они обе будут перезаписывать likes_num одним и тем же номером.

Одним из решений является использование F-объекта вместо установки likes_num до заданного значения c. В этом случае новое значение будет основываться на любом значении в поле на момент записи, а не на том, какое значение было в поле в более ранней точке при чтении строки.

entry.likes_num = F('likes_num') + 1

Другим решением является использование select_for_update(), которое заблокирует строку entry. Лучше избегать блокировок, если вы можете, поэтому я бы выбрал версию F-объекта.

1 голос
/ 27 апреля 2020

Я думаю, вам нужно использовать F-объекты

from django.db.models import F

...
entry.likes_num = F('likes_num') + 1
entry.save()

Поскольку у вас нет ошибок при выполнении кода и допустимы две транзакции.

...