Refs #33646 -- Reduced sync_to_async hops on async QuerySet iteration.

Until we add support for truly asynchronous database backends it's
actually detrimental to have complete set retrieval require multiple
hops between sync and async emulated contexts via asgiref.

By defaulting to sending the whole sync _fetch_all() to the current
context thread pool we reduce unncessary work when dealing with large
result sets since the queryset cannot be iterated anyway before all
results are retrieved and cached.
This commit is contained in:
Simon Charette 2022-05-04 01:31:59 -05:00 committed by GitHub
parent 37470bbd90
commit 1109e66990
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 2 additions and 14 deletions

View File

@ -398,7 +398,7 @@ class QuerySet:
# Remember, __aiter__ itself is synchronous, it's the thing it returns
# that is async!
async def generator():
await self._async_fetch_all()
await sync_to_async(self._fetch_all)()
for item in self._result_cache:
yield item
@ -1842,12 +1842,6 @@ class QuerySet:
if self._prefetch_related_lookups and not self._prefetch_done:
self._prefetch_related_objects()
async def _async_fetch_all(self):
if self._result_cache is None:
self._result_cache = [result async for result in self._iterable_class(self)]
if self._prefetch_related_lookups and not self._prefetch_done:
sync_to_async(self._prefetch_related_objects)()
def _next_is_sticky(self):
"""
Indicate that the next filter call and the one following that should
@ -2025,12 +2019,6 @@ class RawQuerySet:
if self._prefetch_related_lookups and not self._prefetch_done:
self._prefetch_related_objects()
async def _async_fetch_all(self):
if self._result_cache is None:
self._result_cache = [result async for result in RawModelIterable(self)]
if self._prefetch_related_lookups and not self._prefetch_done:
sync_to_async(self._prefetch_related_objects)()
def __len__(self):
self._fetch_all()
return len(self._result_cache)
@ -2047,7 +2035,7 @@ class RawQuerySet:
# Remember, __aiter__ itself is synchronous, it's the thing it returns
# that is async!
async def generator():
await self._async_fetch_all()
await sync_to_async(self._fetch_all)()
for item in self._result_cache:
yield item