-
-
Notifications
You must be signed in to change notification settings - Fork 504
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
Saving documents with embedded collections and default storage strategy leads to inconsistent state between in memory objects and database #2621
Comments
After investigating this for a while, I was able to figure out what exactly is happening for the first failing test (where pages in one book are not removed when a different book is removed entirely): The default strategy for collections (so Back to the pushAll strategy, the issue arises when running multiple updates in a nested collection. Based on the example above, we have the following document: {
"_id": "661cd2c1e676fe8fe10e0fb3",
"books": [
{ "pages": [ { "pageNumber": 1, "font": "default" } ], "name": "red" },
{ "pages": [ { "pageNumber": 1, "font": "fancy" } ], "name": "green" },
{
"pages": [
{ "pageNumber": 1, "font": "beauty" },
{ "pageNumber": 2, "font": "beauty" }
],
"name": "blue"
}
]
} We now want to remove the red book and page one from the blue book. If we were to write the queries ourselves, it would look something like this. Note that these are NOT the queries ODM generates. First, we use {
"$unset": {
"books.0": true,
"books.2.pages.0": true
}
} Then, we use {
"$pull": {
"books": true,
"books.2.pages": true
}
} It is at this stage that MongoDB complains:
In short, we can't update any collection nested in books at the same time as updating books in a single query but would instead have to make multiple queries. There is logic in place to prevent the error and it works most of the time, but not in this particular case. The reason why MongoDB forbids these kinds of updates is exactly the reason becomes apparent when we look at what ODM actually does. Here's the update that uses {
"$unset": {
"books.0": true
}
} Followed by the {
"$pull": {
"books": true
}
} This is the result of an optimisation made in #1880, which consolidated multiple collection updates into a single query and introduced logic to avoid the conflict error highlighted above. When looking at property paths that have changed ( This issue is tricky to fix without completely reverting #1880. While that may fix the bug introduced, it also re-introduces a host of other problem due to each document requiring multiple queries to update each contained collection individually. A quick attempt at fixing this not only failed because of the conflict when using If the setArray strategy works for you, I would suggest sticking with it for the time being. I have also yet to look into the other issue, where I suspect a similar issue with |
Bug Report
Summary
I created a document structure that has two levels of embeddable document collections. Changing those collections by removing objects or setting them again leads to unexpected states being saved to the database. The state inside the database doesn't match the state of the object in memory anymore.
Changing the storage strategy to 'setArray' instead of using the default one resolves the issue
Current behavior
I setup the following example with the objects
Bookshelf
,Book
andPages
.A bookshelf has a collection of embedded books and a book has a collection of embedded pages.
I noticed unexpected behavior the following cases:
Removing one book from the collection of books and removing a page of another book.
In this case, the book gets removed as expected but the page that should have been removed stays.
Removing one book from the collection of books and resetting the pages of one of the remaining books.
In this case, the book gets removed as expected but the pages of an unrelated book get removed and the pages which were reset are duplicated.
How to reproduce
https://github.com/xammmue/doctrine_odm_save_storage_strategy_issue_poc
I created this repository which includes the classes Bookshelf, Book and Page. I also added some tests with assertions that would match the behavior I expected. The two failing tests
test_removeBookAndPage
andtest_reinitializePagesAndDeleteOtherBook
reproduce the (in my opinion) faulty behavior.Changing the storage strategy for the books and pages to e.g. 'setArray' resolves the problems and all cases I observed work as expected.
Expected behavior
I expected that the state saved in the database matches the state of the objects in memory.
The text was updated successfully, but these errors were encountered: