为什么带有S3Boto后端的django-storage的default_storate.exists()会导致大型S3存储桶出现内存错误?

 呆保保_369 发布于 2023-02-02 20:14

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 个回答
  • 更新(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)
    

    您必须将此子类放在应用程序的某个模块中,并通过设置模块中的虚线路径引用它.这个子类的唯一缺点是每次调用任何3个重写方法都会产生一个web请求,这可能不是什么大问题.

    2023-02-02 20:18 回答
撰写答案
今天,你开发时遇到什么问题呢?
立即提问
热门标签
PHP1.CN | 中国最专业的PHP中文社区 | PNG素材下载 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有