Создание сигнала после сохранения вручную замедляет работу приложения, 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. Так есть ли способ улучшить создание сигнала?
Подробнее
def post_save_fn(obj):
post_save.send(obj.__class__, instance=obj, created=True)
class CustomManager(models.Manager):
def bulk_create(self, objs, **kwargs):
#Your code here
data_obj = super(CustomManager, self).bulk_create(objs,**kwargs)
for i in data_obj:
# t1 = threading.Thread(target=post_save_fn, args=(i,))
# t1.start()
post_save.send(i.__class__, instance=i, created=True)
return data_obj
class Test(Base):
test_name = models.CharField(max_length=100)
test_code = models.CharField(max_length=50)
objects = CustomManager()
class Meta:
db_table = "test_db"
2 ответа
В чем проблема?
Как уже упоминалось в комментариях, проблема заключается в том, что функции, которые вызываются через метод, занимают много времени. (Помните, что сигналы не асинхронны!! — это распространенное заблуждение).
Я не знаком с ними, но бегло взглянув на функции, которые будут вызываться после сохранения (см. здесь и здесь), мы увидим, что они включают дополнительные обращения к базе данных.
Несмотря на то, что вы сохраняете множество отдельных обращений к базе данных с помощью использования, вы все равно снова вызываете базу данных несколько раз для каждого сигнала post_save.
Что можно с этим сделать?
Короче говоря. Немного!! Для подавляющего большинства запросов django медленная часть будет вызывать базу данных. Вот почему мы пытаемся свести к минимуму количество обращений к базе данных (используя такие вещи, как
bulk_create
).
Прочитав первые несколько абзацев всей идеи, вы перенесете то, что обычно находится в коде, в базу данных. Большим преимуществом здесь является то, что вам не нужно так часто переписывать код и повторно развертывать. Но недостатком является то, что вам неизбежно придется больше обращаться к базе данных, что замедлит работу. Это будет хорошо для некоторых случаев использования, но не для всех.
Я могу придумать две вещи, которые могут помочь:
- Происходит ли все это в настоящее время как часть цикла запрос/ответ. А если есть, то нужно ли? Если ответы на эти два вопроса «да» и «нет» соответственно, вы можете переместить эту работу в отдельную очередь задач. Это по-прежнему будет медленным, но, по крайней мере, не замедлит работу вашего сайта.
- В зависимости от того, каковы ваши рабочие процессы и характер данных, которые вы создаете, может случиться так, что вы сможете делать все, что
post_save
сигналы выполняют свою собственную функцию и делают это более эффективно. Но это определенно будет зависеть от ваших данных и вашего приложения и отойдет от философииdjango-river
.
Используйте отдельного работника, если логика «сигнала» позволяет вам выполняться после массового сохранения.
Вы можете создать дополнительную таблицу очереди и поместить метаданные о том, что делать для вашего будущего работника.
Создайте отдельный воркер (модуль Django) с необходимой логикой и данными из таблицы очереди. Вы можете сделать это как команду управления, это позволит вам запускать воркер в основном потоке (вы можете запускать команды управления из обычного кода Django) или вы можете запускать его с помощью crontab по расписанию.
Как запустить такого воркера?
Если вам нужно что-то сделать так же близко, как вы создали записи - запустите это в отдельном потоке с помощью
threading
модуль. Таким образом, ваш жизненный цикл запроса-ответа будет выполнен сразу после того, как вы запустите новый поток.
В противном случае, если вы можете сделать это позже - составьте расписание и запустите его с помощью crontab, используя структуру команд управления.