Fix hydration of proxy objects with lazy public properties - #1784
Conversation
|
@pascal-hofmann the fix works on your test, but I'd appreciate it if you could confirm the fix works in your actual setup as well. Unfortunately, there is no more support for ODM 1.1, which means you'll have to upgrade to 1.2 in order to get a bugfix. ODM 1.2 contains no BC breaks, so in theory the upgrade should be relatively straightforward, given your system fulfills the requirements with respect to PHP and MongoDB driver versions. |
|
@alcaeus Thanks for looking into this so quickly. I will check if this solves the issue in our application too. |
|
@alcaeus I just gave it a try. It looks good: This also fixes the issue in our application! 🎉 |
|
Great, merging then :) |
|
@alcaeus will you cherry pick this into |
|
Sorry @malarzm, forgot this one. Created a PR to apply to 2.0. |
This pull request fixes #1775 by ensuring the proxy initializer is unset before hydration for a proxy document begins. As shown in the provided test case, this can lead to data loss if
a) a document contains public properties and is still an uninitialized proxy
b) the document contains a
manyrelationship (reference or embed) which isn't changed during the process.The data loss occurs because the hydrator sets the value of a lazy public property, triggering proxy initialization. For one, this causes an additional database read which can cause performance problems.
Setting aside any issues due to data being changed in the database in the meantime, the document in the end will be exactly what to expect, without any issues. However, after proxy initialization is complete, the original hydration cycle for the document sets the
originalValuefor the document in UnitOfWork. This sets a differentPersistentCollection(containing the same data internally) asoriginalValuefor anymanyrelationship.The next time a change set is computed for the (initialized) proxy document, UnitOfWork compares
originalValueandactualValuefor the relationship. Since the instance changed, it schedules a collection deletion for theoriginalValue. However, sinceactualValuealready is aPersistentCollectionand isn't marked asdirty(because we didn't change anything in the collection), it won't be persisted, leaving only the collection deletion to cause some data loss.The root issue is fixed by the changes in
HydratorFactory, which ensures that a proxy object with public properties is not hydrated twice, removing the cause for the issue. I checked to see whether we need to apply some changes to the logic which deals with collection replacements, but I figured out that the only way we could end up with a non-dirty persistent collection inactualValueis ifa) a non-empty collection was replaced with an empty one; in this case the deletion without insertion is the correct course of action
b) a non-dirty persistent collection containing data was injected into a document; in this case, we won't prevent a user from shooting themselves in the foot.