【问题标题】:Why does default_storate.exists() with django-storages with S3Boto backend cause a memory error with a large S3 bucket?为什么带有 S3Boto 后端的 django-storages 的 default_storate.exists() 会导致大型 S3 存储桶出现内存错误?
【发布时间】:2014-02-02 22:36:55
【问题描述】:

在运行default_storage.exists() 时,我正在使用 S3Boto 后端遇到django-storages 的内存泄漏问题

我在这里关注文档: 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() 函数,这会导致缩略图生成非常缓慢。

【问题讨论】:

    标签: python django boto sorl-thumbnail django-storage


    【解决方案1】:

    更新(2017 年 1 月 23 日)

    为避免这种情况,您可以在创建Storage 时简单地传递preload_metadata=False,或在设置中设置AWS_PRELOAD_METADATA = False

    感谢 @r3mot 在 cmets 中的建议。

    原答案

    其实是因为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)
    

    您只需将此子类放在您应用的一个模块中,并通过设置模块中的虚线路径引用它。这个子类的唯一缺点是每次调用 3 个被覆盖的方法中的任何一个都会产生一个 Web 请求,这可能没什么大不了的。

    【讨论】:

    • @AndrewWidgery 没有问题 - 顺便说一句,我刚刚更新了 size 方法以从 return 行上的大小属性访问中删除 ()(它是文件的属性,而不是方法)。那将是一个例外。
    • 你也可以在创建存储的时候直接传递preload_metadata=False,或者在你的django设置文件中设置AWS_PRELOAD_METADATA = False
    • @r3m0t 哇,太好了,感谢您的提示-添加到答案中!
    猜你喜欢
    • 2016-07-01
    • 2013-06-05
    • 1970-01-01
    • 2014-02-05
    • 1970-01-01
    • 2012-02-28
    • 2016-10-05
    • 1970-01-01
    相关资源
    最近更新 更多