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

Smart eager loading #172

Open
wants to merge 9 commits into
base: master
Choose a base branch
from

Conversation

moufmouf
Copy link
Member

@moufmouf moufmouf commented Sep 5, 2019

This PR adds a new feature: Smart Eager Loading.

The idea behind smart eager loading is to track the origin of each bean. If a bean originates from a result set, when a related bean is accessed (through a foreign key), instead of quering only the data related to the bean, we query the data related to all related beans.

This optimisation allows us to jump from N+1 queries to only 2 queries \o/

@coveralls
Copy link

coveralls commented Sep 5, 2019

Coverage Status

Coverage decreased (-0.3%) to 97.447% when pulling a816e5b on moufmouf:smart_eager_load into 45a8c05 on thecodingmachine:master.

This will allow us in the future to build a oneToMany handler for smart eager loading.
@pascalwacker
Copy link

First of all, hats of to you. This is a pretty smart solution, if it works for every day code.

Since there's no comment function on the article on thecodingmachine.io, I'll ask it here, since it wasn't covered in the article.

What happens, if we have an object with multiple relations?
Ex. a User has some PhoneNumbers (like mobile, landline, office, etc.) as well as multiple PaymentMehodes (Mastercard, Visa, etc.). Assuming we're listing a few users, ex. all that have bought something in the last month, how would that scenario be handled. Ex. twig code:

{% for user in users %}
    ...
    <h2>Phone numbers</h2>
    {% for phonenumber in user.phonenumbers %}
         ...
    {% endfor %}
    ...
    <h2>Payment Methods</h2>
    {% for paymentmethod in user.paymentmethodes %}
        ...
    {% endfor %}
    ...
{% endfor %}

Will it issue one query for User and PhoneNumber and a second one with User and PaymentMethod? If so, one possibility would be to wait with the loading query until the second element of an iterator is accessed, and only then collect all needed elements (which probably would need a lot more book keeping).

@moufmouf
Copy link
Member Author

Hey Pascal!

In your example, there would be 3 requests I think:

  • one for the User table
  • one for the PhoneNumber table (select * from PhoneNumbers where id IN (select ID from Users where ...)
  • one for the PaymentMethod table (select * from PaymentMethod where id IN (select ID from Users where ...)

We could try to run only one query instead of 3, but that would generate a lot of book keeping indeed. Actually, trying to perform 1 query instead of 3 could lead to performance issues. This is a documented bottleneck in Doctrine ORM for instance (see https://tideways.com/profiler/blog/5-doctrine-orm-performance-traps-you-should-avoid)

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.

3 participants