У меня есть модель под названием устройство. Каждое устройство имеет метод 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'])
Делает это звучит правильно?