2

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 個のアイテムがあることです。もしそうなら、きっと別の方法があるに違いない?残念ながら、sorl-thumbnail は新しいサムネイルを生成するときにこの .exists() 関数を使用するため、サムネイルの生成が非常に遅くなります。

4

1 に答える 1

6

更新 (2017 年 1 月 23 日)

これを回避するには、 を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)

このサブクラスをアプリのモジュールの 1 つに配置し、設定モジュールのドット パスを介して参照するだけです。このサブクラスの唯一の欠点は、オーバーライドされた 3 つのメソッドのいずれかを呼び出すたびに Web リクエストが発生することですが、これは大したことではない可能性があります。

于 2014-01-14T19:10:05.737 に答える