Понимание работы нескольких процессов gunicorn
У меня нет знаний о том, что я пытаюсь понять, серфинг в интернете привел меня сюда.
Я использую django-rest-framework, gunicorn и Nginx.
Предположим, у меня есть 3 рабочих процесса установки gunicorn.
и у меня есть очень простое представление, которое считывает значение из базы данных, выполняет другую задачу, которая занимает около 1 секунды, увеличивает значение на 1 и сохраняет его обратно в базу данных.
class View():
value = MyModel.objects.get(id=1).integerValueField
otherTask() #takes around 1 second (assume)
updatedValue = value + 1
MyModel.objects.filter(id=1).update(integerValueField=updatedValue)
return
Всегда ли это будет работать?
что если другой рабочий процесс gunicorn обрабатывает запрос одновременных пользователей? Если база данных обновляется другим процессом в промежутке между чтением значения и обновлением значения каким-то другим рабочим процессом? Это блокируется каким-то образом для поддержания целостности?
если я могу получить действительные ссылки, чтобы прочитать больше о теме, будет работать хорошо для меня.
Для расширения комментария The Pjot - нет, код, который вы предоставили, не будет надежно работать, если вы выполните его с несколькими рабочими Gunicorn. То, что происходит здесь, называется условием гонки и на самом деле не является чем-то специфичным для Django - здесь обсуждается именно это в более общей настройке базы данных.
Теперь, что произойдет в вашем конкретном случае, если несколько рабочих Gunicorn обратятся к одному и тому же объекту ( или один рабочий с несколькими потоками), выглядит примерно так, если мы предположим, что MyModel.objects.get(id=1).integerValueField находится 100 в начале:
- рабочий 1 выполнит
value = MyModel.objects.get(id=1).integerValueField, который обратится к базе данных и извлечет объект с первичным ключом1и сохранит его в памяти.valueбудет установлено значениеintegerValueField, которое в нашем примере100будет равно - рабочий 1 выполняет
otherTask() - рабочий 2 выполняет
value = MyModel.objects.get(id=1).integerValueFieldи, как и рабочий 1, сохраняет текущее значениеintegerValueFieldв базе данных вvalue.valueснова будет100, так как значение еще не изменилось в базе данных .
- рабочий 2 выполняет
otherTask() - рабочий 1 теперь выполнит
updatedValue = value + 1, который установитupdatedValueв значение101, а затем выполнитMyModel.objects.filter(id=1).update(integerValueField=updatedValue), чтобы сохранить его в базе данных .
- worker 2 теперь также выполнит
updatedValue = value + 1- ноvalueбудет также101вместо102, поскольку используется локальная копия значения базы данных. Никакого дополнительного доступа к базе данных здесь не происходит. После этого будет выполненоMyModel.objects.filter(id=1).update(integerValueField=updatedValue), которое также обновит базу данных, но не изменит значение - оно по-прежнему будет101.
Что делает select_for_update, так это блокирует строку базы данных, чтобы к ней не мог получить доступ другой рабочий в то же время (это концепция, которая называется взаимный исключительный доступ и часто реализуется через блокировку). Это решит вашу проблему потерянных обновлений. Однако здесь следует учитывать, что вы заблокируете весь доступ к этой строке, пока работает otherTask() (а это, очевидно, значительное время), и это может легко привести к длительным задержкам для ваших клиентов и даже хуже.
Я бы очень подумал, нет ли лучшего способа решить эту проблему. Если нет, то я бы, по крайней мере, рассмотрел многопоточные Gunicorn workers - здесь есть хорошее обсуждение.