У меня есть «один» метод в веб-API, который делает два вызова базы данных.
- Убедитесь, что с помощью count() определенных объектов не существует
- Сохраните объект, если он еще не существует
Логика здесь явно не в сохранении объекта, если он уже существует. Довольно просто. Проблема возникает, когда в таблице БД начинают появляться дублированные объекты. Затем я понял, что не могу контролировать плохие вызовы клиентов (одновременно повторяющиеся вызовы).
Таким образом, проблема возникает, когда второй вызов этого метода происходит точно между блоками кода 1) и 2) с теми же параметрами (тот же объект):
- Вызов 1 => count() == 0
- Вызов 2 => count() == 0 (это 0, потому что первый вызов не закончил обработку и сохранение объекта в этот момент)
- Вызов 1 => сохраняет объект
- Вызов 2 => сохраняет объект, потому что count() == 0
Неверный результат => в моей таблице есть две строки с одинаковыми значениями
Поэтому мне нужно не обрабатывать «Вызов 2», пока не завершится кодовый блок «Вызов 1».
Я много занимался этим. Вариантом может быть блокировка таблицы, которую я запрашиваю, до тех пор, пока метод не завершит выполнение (но это может привести к новым проблемам в уравнении).
Другие люди говорят об использовании атомарных транзакций, но я не вижу, как это может помочь, если в то же время выполняется другой вызов.
Другим возможным решением было бы использование unique_together, поэтому невозможно создать две строки с одинаковыми значениями (я начинаю думать, что это будет лучший вариант)
Я потерялся в глуши, пытаясь понять, как это исправить. Учитывая, что эту проблему трудно проверить, я подумал сначала спросить здесь. Может быть, я просто упускаю что-то очевидно простое...
Просто чтобы уточнить, вот как я узнаю, существует ли уже объект:
# return error if the follow is already done
current_follow_query = request.user.follows.filter(influencer=influencer)
if current_follow_query.count() > 0:
return Response(status=status.HTTP_204_NO_CONTENT)
# save the actual follow object
obj = Follow(follower=request.user, influencer=influencer)
obj.save()
Любые идеи?