Понимание работы нескольких процессов 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. рабочий 1 выполнит value = MyModel.objects.get(id=1).integerValueField, который обратится к базе данных и извлечет объект с первичным ключом 1 и сохранит его в памяти. value будет установлено значение integerValueField, которое в нашем примере 100 будет равно
  2. рабочий 1 выполняет otherTask()
  3. рабочий 2 выполняет value = MyModel.objects.get(id=1).integerValueField и, как и рабочий 1, сохраняет текущее значение integerValueField в базе данных в value. value снова будет 100, так как значение еще не изменилось в базе данных
  4. .
  5. рабочий 2 выполняет otherTask()
  6. рабочий 1 теперь выполнит updatedValue = value + 1, который установит updatedValue в значение 101, а затем выполнит MyModel.objects.filter(id=1).update(integerValueField=updatedValue), чтобы сохранить его в базе данных
  7. .
  8. worker 2 теперь также выполнит updatedValue = value + 1 - но value будет также 101 вместо 102, поскольку используется локальная копия значения базы данных. Никакого дополнительного доступа к базе данных здесь не происходит. После этого будет выполнено MyModel.objects.filter(id=1).update(integerValueField=updatedValue), которое также обновит базу данных, но не изменит значение - оно по-прежнему будет 101
  9. .

Что делает select_for_update, так это блокирует строку базы данных, чтобы к ней не мог получить доступ другой рабочий в то же время (это концепция, которая называется взаимный исключительный доступ и часто реализуется через блокировку). Это решит вашу проблему потерянных обновлений. Однако здесь следует учитывать, что вы заблокируете весь доступ к этой строке, пока работает otherTask() (а это, очевидно, значительное время), и это может легко привести к длительным задержкам для ваших клиентов и даже хуже. Я бы очень подумал, нет ли лучшего способа решить эту проблему. Если нет, то я бы, по крайней мере, рассмотрел многопоточные Gunicorn workers - здесь есть хорошее обсуждение.

Вернуться на верх