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
Fixed #29984 -- Support prefetch_related() with Queryset.iterator() #10707
Conversation
There seems to be some cursor exhaustion issues for PostgreSQL. I haven't tested it but I wonder if Did you investigate it a bit @RaphaelKimmig? |
08d1e02
to
e6d1aef
Compare
I've just looked into those failures and I think the issue was simply the tests using a batch size large enough to not keep a cursor open. Given that this implementation now takes I've updated the failing tests to use an appropriate From what I understand having multiple cursors open at the same time should not be an issue, in fact there is a test for that ( |
e6d1aef
to
18d9428
Compare
@charettes What's the steps to getting this merged? This feature would be extremely useful. |
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.
@taylor-cedar AFAIK this is still a PoC because there isn't a clear consensus on how we should turn on the feature given backward compatibility concerns.
django/db/models/query.py
Outdated
@@ -338,7 +338,22 @@ def __or__(self, other): | |||
#################################### | |||
|
|||
def _iterator(self, use_chunked_fetch, chunk_size): | |||
yield from self._iterable_class(self, chunked_fetch=use_chunked_fetch, chunk_size=chunk_size) | |||
clone = self._chain() |
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.
It'd be great if we could avoid this and all of the following if not self._prefetch_related_lookups
e.g.
# Avoid materialization of chunks of objects if no prefetching must take
# place.
if not self._prefetch_related_lookups:
yield from self._iterable_class(self, chunked_fetch=use_chunked_fetch, chunk_size=chunk_size)
return
That'll make sure the exact same behavior is preserved when no prefetching is involved and allow you to revert the test_server_side_cursors.py
changes.
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.
Yep, makes sense. I've reverted the test changes.
django/db/models/query.py
Outdated
clone._result_cache = None | ||
return | ||
|
||
if clone._prefetch_related_lookups: |
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 conditional clause can be dropped once we perform an initial not self._prefetch_related_lookups
as described above.
django/db/models/query.py
Outdated
clone._prefetch_done = False | ||
|
||
if len(clone._result_cache) == 0: | ||
clone._result_cache = None |
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.
Why is this is required? I don't think there's a need to unref an empty list given clone
is never returned?
django/db/models/query.py
Outdated
clone._result_cache = list(islice(iterator, chunk_size)) | ||
clone._prefetch_done = False | ||
|
||
if len(clone._result_cache) == 0: |
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.
Could use boolean not clone._result_cache
check as well.
django/db/models/query.py
Outdated
clone._prefetch_related_objects() | ||
|
||
for item in clone._result_cache: | ||
yield item |
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.
Could be reduced to yield from iter(clone._result_cache)
.
18d9428
to
25e8d3e
Compare
Closing due to inactivity. |
https://code.djangoproject.com/ticket/29984