Как справиться со слишком большим количеством открытых файловых дескрипторов в Python/Django?
Мое приложение Django имеет несколько моделей, которые имеют некоторые поля файлов:
class MyModelA(models.Model):
field1 = models.FileField(...)
field2 = models.FileField(...)
field3 = models.FileField(...)
class MyModelB(models.Model):
field1 = models.FileField(...)
field2 = models.FileField(...)
field3 = models.FileField(...)
Чтобы сгенерировать некоторые тестовые данные для моего приложения, которые включают поддельные файлы, я делаю следующее:
my_app/management/commands/generatetestdata.py:
class Command(BaseCommand):
def handle(self, *args, **options):
self.generate_model_a_test_data()
self.generate_model_b_test_data()
...
def generate_model_a_test_data(self):
field1_data = Path("/path/to/field1/sample/file").read_bytes()
field2_data = Path("/path/to/field2/sample/file").read_bytes()
for _ in range(1000):
instances = []
instances.append(MyModelA(
field1=ContentFile(field1_data, name="Some File Name"),
field2=ContentFile(field2_data, name="Some File Name"),
))
MyModelA.objects.bulk_create(instances)
Это не совсем тот код, который я использую, но идея такова.
Есть только несколько реальных файлов, которые я считываю из файловой системы. Я считываю их в память и создаю тонну экземпляров ContentFile, которые при сохранении фактически копируют эти файлы в мой каталог мультимедиа. Да, это не так эффективно, как использование чего-то вроде симлинков, но это нормально.
Проблема заключается в том, что иногда запуск python manage.py generatetestdata приводит к успеху, а в других случаях через несколько минут происходит сбой: django.db.utils.OperationalError: unable to open database file
Когда он не работает, это происходит после создания тысяч файлов (НЕ сразу после выполнения команды), так что это точно не проблема с правами.
Я подозреваю, что причина неудачи в том, что у Python слишком много открытых дескрипторов файлов, хотя я не совсем уверен, как это подтвердить.
Мой вопрос заключается в следующем: Как я могу сделать так, чтобы мой код закрывал эти файловые дескрипторы по мере выполнения, а не ждал, пока это сделает сборщик мусора? Похоже, что ожидание, пока GC сделает это, не работает.
Я пробовал что-то вроде этого:
def generate_model_a_test_data(self):
field1_data = Path("/path/to/field1/sample/file").read_bytes()
field2_data = Path("/path/to/field2/sample/file").read_bytes()
for _ in range(1000):
instances = []
with ContentFile(field1_data, name="Some File Name") as cf1:
with ContentFile(field2_data, name="Some File Name") as cf2:
instances.append(MyModelA(
field1=cf1,
field2=cf2,
))
MyModelA.objects.bulk_create(instances)
но это не помогло. Этот код с менеджерами контекста не падает, и правильно присоединяет данные файла к экземплярам модели, но все равно падает, если я создаю слишком много экземпляров модели.
Есть ли способ подтвердить, что проблема заключается в слишком большом количестве открытых дескрипторов файлов, а затем каким-то образом заставить код закрыть их?