-
Notifications
You must be signed in to change notification settings - Fork 204
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
Async Django app #2663
Closed
Closed
Async Django app #2663
Changes from all commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
84c81d7
Add ASGI Django app
sarayourfriend 36d16fc
Use uvicorn locally for full asgi testing
sarayourfriend cd6e699
Use reusable aiohttp session and prevent async redis event loop closures
sarayourfriend b1bbc1d
Fix local static files when using uvicorn
sarayourfriend 8d5dd60
Update unit tests for async
sarayourfriend dd68ca2
Remove unnecessary closure
sarayourfriend 0ed63f5
Fix tests
sarayourfriend 980ef96
Try various things to fix the event loop closure, make a mess
sarayourfriend File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
""" | ||
Safely share aiohttp ClientSession for the entire application. | ||
|
||
CC BY-SA 4.0 by StackOverflow user https://stackoverflow.com/users/8601760/aaron | ||
https://stackoverflow.com/a/72634224 | ||
""" | ||
|
||
import asyncio | ||
|
||
import aiohttp | ||
|
||
from conf.asgi import application | ||
|
||
|
||
# aiohttp recommends reusing the same session for the whole application | ||
# https://docs.aiohttp.org/en/stable/client_quickstart.html#make-a-request | ||
_SESSION: aiohttp.ClientSession = None | ||
|
||
_lock = asyncio.Lock() | ||
|
||
|
||
async def get_aiohttp_session(): | ||
global _SESSION | ||
|
||
async with _lock: | ||
if not _SESSION or _SESSION.closed: | ||
_SESSION = aiohttp.ClientSession() | ||
application.on_shutdown.append(_SESSION.close) | ||
|
||
return _SESSION |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,6 +10,7 @@ | |
from decouple import config | ||
from elasticsearch_dsl.response import Hit | ||
|
||
from api.utils.aiohttp import get_aiohttp_session | ||
from api.utils.check_dead_links.provider_status_mappings import provider_status_mappings | ||
from api.utils.dead_link_mask import get_query_mask, save_query_mask | ||
|
||
|
@@ -32,28 +33,28 @@ def _get_expiry(status, default): | |
return config(f"LINK_VALIDATION_CACHE_EXPIRY__{status}", default=default, cast=int) | ||
|
||
|
||
async def _head(url: str, session: aiohttp.ClientSession) -> tuple[str, int]: | ||
async def _head(url: str, **kwargs) -> tuple[str, int]: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Changes in this module are just to support the reusable aiohttp session. We should have been doing that already anyway. |
||
session = await get_aiohttp_session() | ||
try: | ||
async with session.head(url, allow_redirects=False) as response: | ||
async with session.head( | ||
url, allow_redirects=False, headers=HEADERS, **kwargs | ||
) as response: | ||
return url, response.status | ||
except (aiohttp.ClientError, asyncio.TimeoutError) as exception: | ||
_log_validation_failure(exception) | ||
return url, -1 | ||
|
||
|
||
# https://stackoverflow.com/q/55259755 | ||
@async_to_sync | ||
async def _make_head_requests(urls: list[str]) -> list[tuple[str, int]]: | ||
tasks = [] | ||
timeout = aiohttp.ClientTimeout(total=2) | ||
async with aiohttp.ClientSession(headers=HEADERS, timeout=timeout) as session: | ||
tasks = [asyncio.ensure_future(_head(url, session)) for url in urls] | ||
responses = asyncio.gather(*tasks) | ||
await responses | ||
tasks = [asyncio.ensure_future(_head(url, timeout=2)) for url in urls] | ||
responses = asyncio.gather(*tasks) | ||
await responses | ||
return responses.result() | ||
|
||
|
||
def check_dead_links( | ||
async def check_dead_links( | ||
query_hash: str, start_slice: int, results: list[Hit], image_urls: list[str] | ||
) -> None: | ||
""" | ||
|
@@ -85,7 +86,7 @@ def check_dead_links( | |
to_verify[url] = idx | ||
logger.debug(f"len(to_verify)={len(to_verify)}") | ||
|
||
verified = _make_head_requests(to_verify.keys()) | ||
verified = await _make_head_requests(to_verify.keys()) | ||
|
||
# Cache newly verified image statuses. | ||
to_cache = {CACHE_PREFIX + url: status for url, status in verified} | ||
|
@@ -161,6 +162,9 @@ def check_dead_links( | |
) | ||
|
||
|
||
sync_check_dead_links = async_to_sync(check_dead_links) | ||
|
||
|
||
def _log_validation_failure(exception): | ||
logger = parent_logger.getChild("_log_validation_failure") | ||
logger.warning(f"Failed to validate image! Reason: {exception}") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is now correctly configured in settings for local and production deployments (which are intercepted by Nginx anyway) so there's no need to repeat the configuration here.