Почему default_storate.exists() с django-хранилищами с бэкэндом S3Boto вызывает ошибку памяти с большим объемом памяти S3?

Я испытываю то, что выглядит как утечка памяти с django-storages используя бэкэнд S3Boto при запуске default_storage.exists()

Я слежу за документами здесь: http://django-storages.readthedocs.org/en/latest/backends/amazon-S3.html

Вот соответствующая часть моего файла настроек:

DEFAULT_FILE_STORAGE = 'storages.backends.s3boto.S3BotoStorage'

Вот что я делаю, чтобы повторить проблему:

./manage.py shell

from django.core.files.storage import default_storage

# Check default storage is right
default_storage.connection
>>> S3Connection:s3.amazonaws.com

# Check I can write to a file
file = default_storage.open('storage_test_2014', 'w')
file.write("does this work?")
file.close()
file2 = default_storage.open('storage_test_2014', 'r')
file2.read()
>>> 'does this work?'

# Run the exists command
default_storage.exists("asdfjkl") # This file doesn't exist - but the same thing happens no matter what I put here - even if I put 'storage_test_2014'

# Memory usage of the python process creeps up over the next 45 seconds, until it nears 100%
# iPython shell then crashes
>>> Killed

Единственная потенциальная проблема, о которой я подумал, состоит в том, что в моем S3-контейнере содержится 93 000 элементов - мне интересно, если.exists просто загружает весь список файлов, чтобы проверить? Если это так, то наверняка должен быть другой путь? К сожалению, sorl-thumbnail использует эту функцию.exists() при создании нового эскиза, что приводит к очень медленной генерации миниатюр.

1 ответ

Решение

Обновление (23 января 2017 г.)

Чтобы избежать этого, вы можете просто пройти preload_metadata=False при создании Storageили установить AWS_PRELOAD_METADATA = False в настройках.

Спасибо @ r3mot за это предложение в комментариях.

Оригинальный ответ

На самом деле это потому, что S3BotoStorage.exists звонит S3BotoStorage.entries, который заключается в следующем:

    @property
    def entries(self):
        """
        Get the locally cached files for the bucket.
        """
        if self.preload_metadata and not self._entries:
            self._entries = dict((self._decode_name(entry.key), entry)
                                for entry in self.bucket.list(prefix=self.location))

Лучший способ справиться с этой ситуацией - создать подкласс S3BotoStorage следующее:

from storages.backends.s3boto import S3BotoStorage, parse_ts_extended


class MyS3BotoStorage(S3BotoStorage):
    def exists(self, name):
        name = self._normalize_name(self._clean_name(name))
        k = self.bucket.new_key(self._encode_name(name))
        return k.exists()

    def size(self, name):
        name = self._normalize_name(self._clean_name(name))
        return self.bucket.get_key(self._encode_name(name)).size

    def modified_time(self, name):
        name = self._normalize_name(self._clean_name(name))
        k = self.bucket.get_key(self._encode_name(name))
        return parse_ts_extended(k.last_modified)

Вам нужно будет просто поместить этот подкласс в один из модулей вашего приложения и ссылаться на него через пунктирный путь в вашем модуле настроек. Единственный недостаток этого подкласса заключается в том, что каждый вызов любого из трех переопределенных методов приведет к веб-запросу, что может не иметь большого значения.

Другие вопросы по тегам