Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Caches Cloudfront Signers #1417

Merged
merged 5 commits into from
Jul 6, 2024
Merged

Conversation

scuml
Copy link
Contributor

@scuml scuml commented Jul 5, 2024

This adds a class-level cache layer in front of cloudfront signers.

Was debugging slowness on a django repo with 50 s3 storage fields, and the expensive load_pem_private_key was called for every field implementing the S3Storage class, even though the id/key was the same for each field. This added a full 3 seconds to server start/restart time.

Caching seems desirable for all users of the library, will provide an instant speed benefit for current users, and will keep django apps snappy and responsive no matter how many filefields they add to their django project.

@jschneier
Copy link
Owner

Thanks this makes sense. Should this be removed in __getstate__ and __setstate__?

@scuml
Copy link
Contributor Author

scuml commented Jul 5, 2024

Good idea to check that:

Since it is declared and always treated as a class var and not an instance var, it never shows up in dict, so it would not need to be handled in __getstate__ and __setstate__

>>> class A():
...     class_var = None
...     instance_var = None
...     def __init__(self):
...             self.__class__.class_var = 1
...             self.instance_var = 2
...             print(self.__dict__)
...
>>> A()
{'instance_var': 2}

@jschneier
Copy link
Owner

Doesn’t line 379 reference as an instance variable?

@scuml
Copy link
Contributor Author

scuml commented Jul 5, 2024

Correct - remedied.

@jschneier
Copy link
Owner

Just the lint failure and then good to merge!

@scuml
Copy link
Contributor Author

scuml commented Jul 5, 2024

Ah - yes, line length is why I removed class on that line during development.
Linted and done.

@jschneier
Copy link
Owner

Huh well that's a lint error I haven't seen before.

@scuml
Copy link
Contributor Author

scuml commented Jul 5, 2024

Python typing was a mistake.... i think this will fix it.

@scuml
Copy link
Contributor Author

scuml commented Jul 5, 2024

Looks like that one throws a number of false positives.
astral-sh/ruff#5243

@jschneier
Copy link
Owner

Python typing was a mistake....

Amen.

@jschneier
Copy link
Owner

This is what it wants (verified locally)

diff --git a/storages/backends/s3.py b/storages/backends/s3.py
index 98b6e94..76dec89 100644
--- a/storages/backends/s3.py
+++ b/storages/backends/s3.py
@@ -4,6 +4,7 @@ import os
 import posixpath
 import tempfile
 import threading
+import typing
 import warnings
 from datetime import datetime
 from datetime import timedelta
@@ -30,8 +31,6 @@ from storages.utils import safe_join
 from storages.utils import setting
 from storages.utils import to_bytes
 
-from typing import Dict
-
 try:
     import boto3.session
     import botocore
@@ -313,7 +312,7 @@ class S3Storage(CompressStorageMixin, BaseStorage):
     # settings/args are ignored.
     config = None
 
-    _signers: Dict[str, str] = {}
+    _signers: typing.ClassVar = {}
 
     def __init__(self, **settings):
         omitted = object()

@jschneier
Copy link
Owner

Alternatively we could update it to ignore the error via a comment. Either is fine for me as I don't find this annotation very helpful.

@jschneier jschneier merged commit e268efa into jschneier:master Jul 6, 2024
19 checks passed
@scuml
Copy link
Contributor Author

scuml commented Jul 6, 2024

Thanks for merging. My leaning would be to ignore as the classvar type a) conveys nothing of value to the user and in a way obfuscates the fact it's a simple dict, and b) would avoid confusion later with future contributors wondering why this single type exists, and if they are now obligated to litter the rest of a very readable codebase with types.

And now I'm checking the commits and seeing that's exactly what you did.
Well played.

ikarius referenced this pull request in gip-inclusion/dora-back Jul 29, 2024
Bumps
[django-storages[boto3]](https://github.com/jschneier/django-storages)
from 1.14.3 to 1.14.4.
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/jschneier/django-storages/blob/master/CHANGELOG.rst">django-storages[boto3]'s
changelog</a>.</em></p>
<blockquote>
<p>1.14.4 (2024-07-09)</p>
<hr />
<h2>S3</h2>
<ul>
<li>Pull <code>AWS_SESSION_TOKEN</code> from the environment
(<code>[#1399](https://github.com/jschneier/django-storages/issues/1399)</code>_)</li>
<li>Fix newline handling for text mode files
(<code>[#1381](https://github.com/jschneier/django-storages/issues/1381)</code>_)</li>
<li>Do not sign URLs when <code>querystring_auth=False</code> e.g public
buckets or static files
(<code>[#1402](https://github.com/jschneier/django-storages/issues/1402)</code>_)</li>
<li>Cache CloudFront Signers
(<code>[#1417](https://github.com/jschneier/django-storages/issues/1417)</code>_)</li>
</ul>
<h2>Azure</h2>
<ul>
<li>Fix <code>collectstatic --clear</code>
(<code>[#1403](https://github.com/jschneier/django-storages/issues/1403)</code>_)</li>
<li>Add <code>mode</code> kwarg to <code>.url()</code> to support
creation of signed URLs for upload
(<code>[#1414](https://github.com/jschneier/django-storages/issues/1414)</code>_)</li>
<li>Fix fetching user delegation key when custom domain is enabled
(<code>[#1418](https://github.com/jschneier/django-storages/issues/1418)</code>_)</li>
</ul>
<h2>SFTP</h2>
<ul>
<li>Add implementations of <code>get_(modified|accessed)_time</code>
(<code>[#1347](https://github.com/jschneier/django-storages/issues/1347)</code>_)</li>
</ul>
<h2>Dropbox</h2>
<ul>
<li>Add support for Python 3.12
(<code>[#1421](https://github.com/jschneier/django-storages/issues/1421)</code>_)</li>
</ul>
<h2>FTP</h2>
<ul>
<li>Conform to <code>BaseStorage</code> interface
(<code>[#1423](https://github.com/jschneier/django-storages/issues/1423)</code>_)</li>
<li>Add <code>FTP_ALLOW_OVERWRITE</code> setting
(<code>[#1424](https://github.com/jschneier/django-storages/issues/1424)</code>_)</li>
</ul>
<p>.. _<a
href="https://redirect.github.com/jschneier/django-storages/issues/1399">#1399</a>:
<a
href="https://redirect.github.com/jschneier/django-storages/pull/1399">jschneier/django-storages#1399</a>
.. _<a
href="https://redirect.github.com/jschneier/django-storages/issues/1381">#1381</a>:
<a
href="https://redirect.github.com/jschneier/django-storages/pull/1381">jschneier/django-storages#1381</a>
.. _<a
href="https://redirect.github.com/jschneier/django-storages/issues/1402">#1402</a>:
<a
href="https://redirect.github.com/jschneier/django-storages/pull/1402">jschneier/django-storages#1402</a>
.. _<a
href="https://redirect.github.com/jschneier/django-storages/issues/1403">#1403</a>:
<a
href="https://redirect.github.com/jschneier/django-storages/pull/1403">jschneier/django-storages#1403</a>
.. _<a
href="https://redirect.github.com/jschneier/django-storages/issues/1414">#1414</a>:
<a
href="https://redirect.github.com/jschneier/django-storages/pull/1414">jschneier/django-storages#1414</a>
.. _<a
href="https://redirect.github.com/jschneier/django-storages/issues/1417">#1417</a>:
<a
href="https://redirect.github.com/jschneier/django-storages/pull/1417">jschneier/django-storages#1417</a>
.. _<a
href="https://redirect.github.com/jschneier/django-storages/issues/1418">#1418</a>:
<a
href="https://redirect.github.com/jschneier/django-storages/pull/1418">jschneier/django-storages#1418</a>
.. _<a
href="https://redirect.github.com/jschneier/django-storages/issues/1347">#1347</a>:
<a
href="https://redirect.github.com/jschneier/django-storages/pull/1347">jschneier/django-storages#1347</a>
.. _<a
href="https://redirect.github.com/jschneier/django-storages/issues/1421">#1421</a>:
<a
href="https://redirect.github.com/jschneier/django-storages/pull/1421">jschneier/django-storages#1421</a>
.. _<a
href="https://redirect.github.com/jschneier/django-storages/issues/1423">#1423</a>:
<a
href="https://redirect.github.com/jschneier/django-storages/pull/1423">jschneier/django-storages#1423</a>
.. _<a
href="https://redirect.github.com/jschneier/django-storages/issues/1424">#1424</a>:
<a
href="https://redirect.github.com/jschneier/django-storages/pull/1424">jschneier/django-storages#1424</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/jschneier/django-storages/commit/b37d53e1209f6333de71502a9cb34d8cffdc7e9f"><code>b37d53e</code></a>
Release version 1.14.4 (<a
href="https://redirect.github.com/jschneier/django-storages/issues/1428">#1428</a>)</li>
<li><a
href="https://github.com/jschneier/django-storages/commit/a79a304e10917c1b8e64cb3fcb3c3ea52c049718"><code>a79a304</code></a>
[sftp] Cleanup duplicated properties (<a
href="https://redirect.github.com/jschneier/django-storages/issues/1426">#1426</a>)</li>
<li><a
href="https://github.com/jschneier/django-storages/commit/cd42c1720247dd9c363ac99a5fa97ef4cf2e4acb"><code>cd42c17</code></a>
[dropbox] Remove dead prefix removal code (<a
href="https://redirect.github.com/jschneier/django-storages/issues/1425">#1425</a>)</li>
<li><a
href="https://github.com/jschneier/django-storages/commit/855e58dd8a6a0d6cbc2d5db70f6566ea89ba2bd4"><code>855e58d</code></a>
[ftp] Add FTP_ALLOW_OVERWRITE setting (<a
href="https://redirect.github.com/jschneier/django-storages/issues/1424">#1424</a>)</li>
<li><a
href="https://github.com/jschneier/django-storages/commit/fe759bffedf6462b478ab2bafd973038e821a3b1"><code>fe759bf</code></a>
[ftp] Conform to BaseStorage interface (<a
href="https://redirect.github.com/jschneier/django-storages/issues/1423">#1423</a>)</li>
<li><a
href="https://github.com/jschneier/django-storages/commit/a5319477473e5bab60b0d698e2a8127a8f750787"><code>a531947</code></a>
[general] Write overwriting files in terms of .exists() (<a
href="https://redirect.github.com/jschneier/django-storages/issues/1422">#1422</a>)</li>
<li><a
href="https://github.com/jschneier/django-storages/commit/0041eb205dc7476210cc7c263574e807322d33d6"><code>0041eb2</code></a>
[dropbox] Add support for Python3.12 (<a
href="https://redirect.github.com/jschneier/django-storages/issues/1421">#1421</a>)</li>
<li><a
href="https://github.com/jschneier/django-storages/commit/4cd12e088bf1be39e49c20f7fa1d1e7f7ed7c61b"><code>4cd12e0</code></a>
[sftp] Format times to UTC (<a
href="https://redirect.github.com/jschneier/django-storages/issues/1420">#1420</a>)</li>
<li><a
href="https://github.com/jschneier/django-storages/commit/ea2522602492fae494f68511f864dc8dd0266098"><code>ea25226</code></a>
[sftp] Add implementations of get_(modified|accessed)_time (<a
href="https://redirect.github.com/jschneier/django-storages/issues/1347">#1347</a>)</li>
<li><a
href="https://github.com/jschneier/django-storages/commit/6c494a071a907b75727a15898167a27cd0d2cb89"><code>6c494a0</code></a>
[docs/scaleway] Create Scaleway docs (<a
href="https://redirect.github.com/jschneier/django-storages/issues/1419">#1419</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/jschneier/django-storages/compare/1.14.3...1.14.4">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=django-storages[boto3]&package-manager=pip&previous-version=1.14.3&new-version=1.14.4)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: ikarius <fred@ikarius.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants