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

fix: flush only once per calls to Factory::create() in userland #836

Draft
wants to merge 3 commits into
base: 2.3.x
Choose a base branch
from

Conversation

nikophil
Copy link
Member

@nikophil nikophil commented Mar 2, 2025

Ok, I think it's time for this now 😅

It's been a while that I wanted to achieve this: we're calling too many flush(), because we're flushing every time an entity is created, although, the "Doctrine way" would be to only flush once per "root factory".

This is actually possible thanks to all the work that have been done around "schedule for insert" stuff.

A problem that could have happened because of this, is with "after persist" callbacks : not calling save() for every entity would mean not calling their "after persist callbacks", but I was pleasantly surprised that this is an already solved problem, thanks to this PR 🎉

What triggered me to do this now, was this issue raised by @mmarton

given this code:

ProductFactory::createOne([
    'stock' => StockFactory::new(), // this is an "inverse OneToOne"
    'category' => CategoryFactory::new(), // this is a regular "ManyToOne"
])

there is a problem with the "placeholder solution" for inverse OneToOne : at some point, because we flush right after the category is created, and because stock is already persisted (the doctrine way), it tries to save the "placeholder" 🤷

I've tried a lot of different things, but nothing works with every combination possible of "cascade persist" stuff. But the problem is naturally fixed when we deffer the flush() call: we can safely remove the placeholder from the UoW, before any flush attempt is made.

Another solution would be to use PHP 8.4 proxies, because the placeholder could be the real object, but that's more work, and it will only fix the problem for the last PHP version. Moreover both of these solutions are not mutually exclusive, and I'm planning to work on this at some point,(maybe in the same time when I'll use PHP 8.4 proxies for data provider as well).

It comes also with a performance boost:
locally, without Mongo and with DAMA enabled, the command phpunit --exclude-group maker runs in 10s now and in 15s before 🏃 ⚡
FYI, before the fix, the whole testsuite was performing 3040 calls to flush(), and now, only 1300

Surprisingly, there is no performance boost in the testsuite without dama.

Finally, I think this should be released in 2.3.x, because the 2.4 release will be full of new features, which are not 100% ready.

⚠️ EDIT:

I've discovered yet another problem with the "placeholder" stuff for inversed one-to-one, when not using auto-generated ids: in case of cascade: persist, the placeholder was passed to $em->persist(), and it was emitting a EntityMissingAssignedId because the id is generated in the constructor of the entity, but the "placeholder" is created with newInstanceWithoutConstructor().

Then I decided to even deffer $em->persist() calls. This has the main advantage that we can do all the weird stuff we want, and only after that, doctrine joins the party. With the solution, I think we will finally be done with all that "inverse one to one" madness 🎉 😅

@nikophil nikophil marked this pull request as draft March 2, 2025 11:07
@nikophil nikophil requested a review from kbond March 2, 2025 11:45
@nikophil nikophil marked this pull request as ready for review March 2, 2025 11:45
@nikophil nikophil changed the title fix: flush only once per Factory::create() calls in userland fix: flush only once per calls to Factory::create() in userland Mar 2, 2025
@nikophil nikophil marked this pull request as draft March 6, 2025 07:48
@nikophil nikophil force-pushed the fix/flush-once branch 2 times, most recently from 95947f7 to 4292296 Compare March 6, 2025 08:16
@nikophil nikophil force-pushed the fix/flush-once branch 3 times, most recently from 98003a3 to b892369 Compare March 7, 2025 18:15
@nikophil nikophil marked this pull request as ready for review March 7, 2025 20:26
@nikophil nikophil force-pushed the fix/flush-once branch 3 times, most recently from 5316d45 to 0ee9fb8 Compare March 9, 2025 16:27
Copy link
Member

@kbond kbond left a comment

Choose a reason for hiding this comment

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

This is great!

@nikophil nikophil force-pushed the fix/flush-once branch 2 times, most recently from 639a303 to 88620c3 Compare March 14, 2025 13:40
@nikophil nikophil marked this pull request as draft March 17, 2025 11:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

2 participants