Как можно пропустить удаленные модели во время выполнения oop дорогостоящих операций в Django? - PullRequest
0 голосов
/ 15 февраля 2020

У меня есть модель под названием устройство. Каждое устройство имеет метод data_downloaded() и data_uploaded(), который просматривает базу данных для всех записей, связанных с этим устройством, и суммирует данные, которые они использовали. Каждое устройство может потенциально иметь тысячи записей, которые необходимо объединить для расчета его использования. Мои модели выглядят так:

class Device(models.Model):
    ...
    mac_address = models.CharField(...)
    data_downloaded_cached = models.BigIntegerField(...)
    data_uploaded_cached = models.BigIntegerField(...)

    def data_downloaded(self):
        return DataRecord.objects.filter(mac_address=self.mac_address, direction="download").aggregate(data=Sum('data')).get("data", 0.00) or 0.00

    def data_uploaded(self):
        return DataRecord.objects.filter(mac_address=self.mac_address, direction="upload").aggregate(data=Sum('data')).get("data", 0.00) or 0.00

class DataRecord(models.Model):
    timestamp = models.DateTimeField(...)
    mac_address = models.CharField(...)
    direction = models.CharField(...)
    data = models.BigIntegerField(...)

Поскольку device.data_downloaded() и device.data_uploaded() являются дорогостоящими операциями, для вычисления которых может потребоваться минута (нужно объединить много строк базы данных), я не могу вызвать эти методы непосредственно при загрузке шаблона, поэтому я кеширую значения. Фоновый процесс запускает следующий код для обновления этих кэшированных значений каждые несколько минут:

for device in Device.objects.all():
    device.data_downloaded_cached = device.data_downloaded()
    device.data_uploaded_cached = device.data_uploaded()
    device.save()

Когда шаблоны отображают список устройств, а также значения загрузки и выгрузки для каждого устройства, они могут просто использовать кэшированные значения вместо того, чтобы пытаться запускать эти дорогостоящие операции «на лету», в результате чего шаблоны будут загружаться в браузере пользователя вечно.

Проблемы, с которыми я сталкиваюсь:

  • Если устройство удаляется во время выполнения кода, приведенного выше, оно все равно будет обрабатываться функцией for l oop
  • Когда l oop доберется до удаленного устройства, вычисляет значения и вызывает device.save(), операция сохранения приведет к удалению удаленного устройства (воссоздание его в базе данных)

Мне явно не важно обновлять кэшированные значения для устройств, которые были недавно удалены. Я также не хочу блокировать базу данных на несколько минут, пока выполняются эти дорогостоящие операции. Как я могу убедиться, что удаленные устройства пропускаются?

Примечание. Устройство можно удалить до или после начала дорогостоящих операций. Главное, чтобы меня беспокоило, чтобы убедиться, что device.save() не воссоздает устройство. Если я смогу убедиться, что дорогостоящие операции не выполняются на устройствах, которые были удалены до начала этих операций, то отлично. Если нет, я просто должен убедиться, что удаленные устройства не воссоздаются с помощью device.save()

Редактировать: По предложению Виллема Ван Онсема я могу сделать что-то вроде этого:

totals = DataRecord.objects.values('mac_address').order_by('mac_address').annotate(downloaded=Sum('data', filter=Q(direction="download"))).annotate(uploaded=Sum('data', filter=Q(direction="upload")))

updates = []
for device in totals:
    dev = Device.objects.filter(mac_address=device['mac_address']).first()
    if dev:
        dev.data_downloaded_cached = device["downloaded"]
        dev.data_uploaded_cached = device["uploaded"]
        updates.append(dev)

Device.objects.bulk_update(updates, ['data_downloaded_cached', 'data_uploaded_cached'])

Делает это звучит правильно?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...