Ручное создание Post Save Signal делает приложение медленным, Django
У нас есть Django-приложение, которое использует Django-river для управления рабочими процессами. Для повышения производительности нам пришлось использовать bulk_create. Нам нужно вставить данные в несколько таблиц и по несколько строк в каждой. Первоначально мы использовали обычный метод .save(), и рабочий процесс работал так, как ожидалось (поскольку сигналы после сохранения создавались правильно). Но как только мы перешли к bulk_create, производительность повысилась с минут до секунд. Но Django_river перестал работать, так как в нем не было сигналов по умолчанию для постсохранения. Нам пришлось реализовать сигналы на основе имеющейся документации.
class CustomManager(models.Manager):
def bulk_create(items,....):
super().bulk_create(...)
for i in items:
[......] # code to send signal
И
class Task(models.Model):
objects = CustomManager()
....
В результате рабочий процесс снова заработал, но генерация сигналов занимает время, и это уничтожает все улучшение производительности, полученное с помощью bulk_create. Есть ли способ улучшить создание сигналов?
В чем проблема?
Как уже отмечалось в комментариях, проблема заключается в том, что функции, вызываемые через post_save
, занимают много времени. (Помните, что сигналы не являются асинхронными! - это распространенное заблуждение).
Я не знаком с django-river
, но если быстро взглянуть на функции, которые будут вызваны после сохранения (см. здесь и здесь), то можно увидеть, что они включают дополнительные обращения к базе данных.
Хотя вы экономите много отдельных обращений к базе данных, используя bulk_create
, вы все равно обращаетесь к базе данных снова несколько раз для каждого сигнала post_save.
Что можно с этим сделать?
Короче говоря. Не много!!! Для подавляющего большинства запросов django, медленной частью будет обращение к базе данных. Вот почему мы стараемся минимизировать количество обращений к базе данных (используя такие вещи, как bulk_create
).
Прочитав первые несколько абзацев django-river
, можно понять, что вся идея заключается в том, чтобы перенести вещи, которые обычно находятся в коде, в базу данных. Большим преимуществом здесь является то, что вам не нужно часто переписывать код и развертывать его заново. Но недостатком является то, что вам неизбежно придется чаще обращаться к базе данных, что замедлит работу. Это подходит для некоторых случаев, но не для всех.
Есть две вещи, о которых я могу подумать, которые могут помочь:
- Происходит ли все это в настоящее время как часть цикла запроса/ответа. И если да, то нужно ли это делать? Если ответы на эти два вопроса "да" и "нет" соответственно, то вы можете переместить эту работу в отдельную очередь задач. Это все равно будет медленно, но, по крайней мере, не будет тормозить ваш сайт.
- В зависимости от того, каковы ваши рабочие процессы и характер данных, которые вы создаете, может быть случай, что вы можете делать все, что делают сигналы
post_save
в вашей собственной функции, и делать это более эффективно. Но это определенно зависит от ваших данных и вашего приложения и отходит от философииdjango-river
.
Используйте отдельный рабочий, если логика "сигнала" позволяет быть выполненным после массового сохранения.
Вы можете создать дополнительную таблицу очереди и поместить в нее метаданные о том, что делать для вашего будущего работника.
Создайте отдельный рабочий (модуль Django) с необходимой логикой и данными из таблицы очередей. Вы можете сделать это как управляющую команду, что позволит вам запускать рабочий в основном потоке (вы можете запускать управляющие команды из обычного кода Django) или вы можете запускать его через crontab на основе расписания.
Как запустить такого работника?
Если вам нужно, чтобы что-то было сделано сразу после создания записей - запустите это в отдельном потоке с помощью модуля threading
. Таким образом, ваш жизненный цикл запрос-ответ будет выполнен сразу после того, как вы запустите новый поток.
Если вы можете сделать это позже - составьте расписание и запустите его через crontab, используя каркас команд управления.