Skip to content

Commit 03327bb

Browse files
committed
Fix pickling and add docs
1 parent b834c75 commit 03327bb

File tree

3 files changed

+27
-11
lines changed

3 files changed

+27
-11
lines changed

docs/backends/amazon-S3.rst

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,13 +259,25 @@ Settings
259259
Setting this overrides the settings for ``addressing_style``, ``signature_version`` and
260260
``proxies``. Include them as arguments to your ``botocore.config.Config`` class if you need them.
261261

262+
``client_ttl`` or ``AWS_S3_CLIENT_TTL``
263+
264+
Default: ``3600``
265+
266+
The amount of seconds to cache a boto3 client resource.
267+
268+
.. note::
269+
270+
Long-lived boto3 clients have a known `memory leak`_, which is why the client needs to be
271+
periodically recreated to avoid excessive memory consumption.
272+
262273
.. _AWS Signature Version 4: https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-query-string-auth.html
263274
.. _S3 region list: https://docs.aws.amazon.com/general/latest/gr/s3.html#s3_region
264275
.. _list of canned ACLs: https://docs.aws.amazon.com/AmazonS3/latest/dev/acl-overview.html#canned-acl
265276
.. _Boto3 docs for uploading files: https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/s3.html#S3.Client.put_object
266277
.. _Boto3 docs for TransferConfig: https://boto3.amazonaws.com/v1/documentation/api/latest/reference/customizations/s3.html#boto3.s3.transfer.TransferConfig
267278
.. _ManifestStaticFilesStorage: https://docs.djangoproject.com/en/3.1/ref/contrib/staticfiles/#manifeststaticfilesstorage
268279
.. _Botocore docs: https://botocore.amazonaws.com/v1/documentation/api/latest/reference/config.html#botocore.config.Config
280+
.. _memory leak: https://github.com/boto/boto3/issues/1670
269281

270282
.. _cloudfront-signed-url-header:
271283

storages/backends/s3.py

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import warnings
88
from datetime import datetime
99
from datetime import timedelta
10+
from functools import cached_property
1011
from urllib.parse import urlencode
1112

1213
from django.contrib.staticfiles.storage import ManifestFilesMixin
@@ -379,10 +380,6 @@ def __init__(self, **settings):
379380
else:
380381
self.cloudfront_signer = None
381382

382-
self._ttl_cache = cachetools.cached(
383-
cache=cachetools.TTLCache(maxsize=2, ttl=3600), lock=threading.Lock()
384-
)
385-
386383
def get_cloudfront_signer(self, key_id, key):
387384
cache_key = f"{key_id}:{key}"
388385
if cache_key not in self.__class__._signers:
@@ -451,6 +448,7 @@ def get_default_settings(self):
451448
"use_threads": setting("AWS_S3_USE_THREADS", True),
452449
"transfer_config": setting("AWS_S3_TRANSFER_CONFIG", None),
453450
"client_config": setting("AWS_S3_CLIENT_CONFIG", None),
451+
"client_ttl": setting("AWS_S3_CLIENT_TTL", 3600),
454452
}
455453

456454
def __getstate__(self):
@@ -465,23 +463,27 @@ def __setstate__(self, state):
465463
def connection(self):
466464
"""
467465
Get the (cached) thread-safe boto3 s3 resource.
468-
469-
This function has a 1 hour time to live cache for the boto3 resource.
470-
We want to avoid storing a resource for too long to avoid their memory leak
471-
ref https://github.com/boto/boto3/issues/1670.
472466
"""
473467
return self._ttl_cache(self._create_connection)()
474468

475469
@property
476470
def unsigned_connection(self):
477471
"""
478472
Get the (cached) thread-safe boto3 s3 resource (unsigned).
473+
"""
474+
return self._ttl_cache(self._create_connection)(unsigned=True)
479475

480-
This function has a 1 hour time to live cache for the boto3 resource.
476+
@cached_property
477+
def _ttl_cache(self):
478+
"""
479+
This time to live cache is used to periodically recreate boto3 clients.
481480
We want to avoid storing a resource for too long to avoid their memory leak
482481
ref https://github.com/boto/boto3/issues/1670.
483482
"""
484-
return self._ttl_cache(self._create_connection)(unsigned=True)
483+
return cachetools.cached(
484+
cache=cachetools.TTLCache(maxsize=2, ttl=self.client_ttl),
485+
lock=threading.Lock(),
486+
)
485487

486488
def _create_connection(self, *, unsigned=False):
487489
"""

tests/test_s3.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,9 @@ def test_pickle(self):
7575
_ = storage.connection
7676
_ = storage.bucket
7777
p = pickle.dumps(storage)
78-
pickle.loads(p)
78+
new_storage = pickle.loads(p)
79+
_ = new_storage.connection
80+
_ = storage.bucket
7981

8082
def test_storage_url_slashes(self):
8183
"""

0 commit comments

Comments
 (0)