Conditional save for atomic-style operations #511
Closed
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Software often requires an atomic-style database read-and-modify, i.e.:
in a way that ensures no other changes to the database record have occurred between steps 1 and 3. A simple example of this pattern is document versioning. E.g.:
If two users (e.g., user A and user B) attempt to perform this type of operation at the same time, it it uncertain which new version of the document will be stored in the database, but the other can be lost irrevocably. Furthermore, depending on the nature of the updates (e.g., if the updates are expressed as "deltas" between documents), this situation can result in invalid documents being stored in the database.
Mongo's
collection.updatemethod includes aquerydocument and anupdatedocument that allows a user to accomplish the aforementioned atomic-style read-and-update. Continuing with the versioning example, a client can include the expected version number in thequerydocument. E.g.:If the document version stored in the database does not match (e.g., if another client has updated the database document(, the database document is not altered.
MongoEngine allows a developer to directly construct an update query (see MongoEngine User Guide 2.5.9: Atomic Updates). E.g., one could write:
However, explicitly constructing the update query using this 'out of band' style, while it is less cumbersome than generating the full PyMongo object, is awkward, can get unwieldily if multiple document fields are to be updated, and removes much of the benefit of an Object-Document Mapper like MongoEngine. What is desired is a way to achieve "atomic-style read-and-modify" operations using MongoEngine's ODM methods
This PR includes a new optional
save_conditionparameter to the MongoEngine'sDocument.save()method. This parameter can be a dictionary of conditions that will be added to thecollection.update()call embedded in theDocument.save()method. For example, continuing the versioning example, one could write:With the above code, the modified document would only be saved in the version of the document stored in the Mongo database was still equal to
n; i.e., if someone else had modified the document in the interim, then the save would fail.Of course, this is a trivial example of the pattern, but the general problem of "only save these updates if the underlying document hasn't been changed" is common and I think MongoEngine could benefit from a less cumbersome method to solve it.
Please let me know if you have any question or comments.