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

Fixed #29984 -- Support prefetch_related() with Queryset.iterator() #10707

Closed
wants to merge 1 commit into from

Conversation

RaphaelKimmig
Copy link
Contributor

@charettes
Copy link
Member

charettes commented Nov 29, 2018

There seems to be some cursor exhaustion issues for PostgreSQL. I haven't tested it but I wonder if psycopg2 allows us to have to have multiple cursors opened on the same connection at the same time. In this case we need way to keep a server side cursor opened while we iteratively open multiple client side cursors to perform the prefetches.

Did you investigate it a bit @RaphaelKimmig?

@RaphaelKimmig
Copy link
Contributor Author

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 chunk_size results at a time, we'd only expect a cursor to remain open if there are more results than can be fetched in a single chunk.

I've updated the failing tests to use an appropriate chunk_size.

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 (ServerSideCursorsPostgres.test_server_side_cursor_many_cursors).

@taylor-cedar
Copy link

@charettes What's the steps to getting this merged? This feature would be extremely useful.

Copy link
Member

@charettes charettes left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@@ -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()
Copy link
Member

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.

Copy link
Contributor Author

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.

clone._result_cache = None
return

if clone._prefetch_related_lookups:
Copy link
Member

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.

clone._prefetch_done = False

if len(clone._result_cache) == 0:
clone._result_cache = None
Copy link
Member

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?

clone._result_cache = list(islice(iterator, chunk_size))
clone._prefetch_done = False

if len(clone._result_cache) == 0:
Copy link
Member

@charettes charettes Jan 12, 2019

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.

clone._prefetch_related_objects()

for item in clone._result_cache:
yield item
Copy link
Member

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).

@felixxm
Copy link
Member

felixxm commented Jun 12, 2020

Closing due to inactivity.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
4 participants