-
Notifications
You must be signed in to change notification settings - Fork 356
DATAJDBC-219 Implement optimistic record locking #166
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
Conversation
I just ran into an issue with an immutable version attribute. The initial approach is setting the version in the DefaultDataAccess strategy and this may be too late. I can't find a good way to return the new entity (with its version set) from the data access strategy. |
While this current PR does implement the optimistic record locking pattern, The problem is that the version is being set in the data access strategy (as you would expect). However, there is no way to pass back the updated version and set that information in the DBAction. So, in the interpreter, I am inferring the version number using the same logic as the data access strategy....ugh. This breaks encapsulation (as I am really computing the version in two places) There is also a problem in that if the version is immutable, then setting this by reference in the DBAction's entity is not going to be reflected when the updated aggregate root is returned from the template. So, I wrote logic similar to that of the generated Id: grab the version from the entity in the DBEntity (although, I guess I could have just added the version directly to the actions) and then set the version using the root property accessor and the path from the DBAction. I would love some feedback, I suspect this PR will need some pretty substantial changes. |
Thank @tkvangorder for picking this up. I don't think we should support version attributes on nested entities within an aggregate. I don't see how version attributes make sense in nested entities. Just as we have auditing only on the aggregate root level. For setting the version I wonder if it makes more sense to pull he logic out of the The responsibility of the @mp911de I'd appreciate it if you could also take a look. |
@schauder Makes sense. If the interpreter sets the version, the |
It gets the entity plus a version. Since the entity contains a version we have both values available and now need to (re)compute it. I'd say the entity should contain the original version so it only gets changed after the update happened successfully. |
@schauder Getting closer.
I went back and forth on the need for an I still think there is more work to be done. After passing the next version on the update, it requires the data access strategy to correctly set that value when performing the update. This ends up being clunky and you have to do some work in each data access strategy to set the new version without modifying the entity by reference. See the I am thinking maybe it would make more sense to set the new version on the entity in the interpreter and passing the old version as an additional argument into the update and then revert that change in the interpreter when an error occurs. |
@schauder What do you think? I Added a commit where I reworked things such that the previous version is passed into the update and the delete. It feels cleaner, in that the interpreter is doing all the heavy lifting. |
Any news here? #askingforafriend |
Not yet, it is still waiting for me to find some time. This will probably happen in October. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I Added a commit where I reworked things such that the previous version is passed into the update and the delete. It feels cleaner, in that the interpreter is doing all the heavy lifting.
I don't like manually undoing stuff and therefore would prefer passing in the new version and setting it in the entity only after the change went through in the interpreter.
Inside the interpreter the following would happen:
- create new version
- call access strategy with unchanged entity and new version
- set new version
Or am I missing something?
@@ -15,11 +15,14 @@ | |||
*/ | |||
package org.springframework.data.jdbc.core; | |||
|
|||
import static org.springframework.data.relational.core.conversion.RelationalEntityVersionUtils.getVersionNumberFromEntity; | |||
import static org.springframework.data.relational.core.conversion.RelationalEntityVersionUtils.setVersionNumberOnEntity; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We use static imports only in tests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, I will make that change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks pretty good.
Unfortunately AggregateChange
changed significantly lately /cough/ this morning /cough/ so we need a rebase.
Actually scratch that. Undoing won't work anyway since we then should also undo Id setting that might have happened in other entities and also do it when an exception is thrown during saving of other entities contained in the aggregate. |
You are right, I guess trying to prevent a mutated version "on error" is a futile exercise given that any error within the larger transaction is likely to leave any mutable entities in an indeterminate state. I will give this another crack when I free up some time (hopefully within the week) |
Tyler shakes a fist at you, Haha, no worries, wouldn't be the first time this has happened to me. |
@schauder I finally had some time over the holiday to give this tracker some attention. I have:
|
Optimistic locking is based on a numeric attribute annotated with `@Version` on the aggregate root. That attribute is increased before any save operation and checked during updates to ensure that the database state hasn't changed since loading the aggregate. Original pull request: #166.
DeleteWithVersion doesn't require an entity anymore. Added the `@author` and `@since` tags where they were missing. Formatting. Added documentation. Original pull request: #166.
Thanks, this is polished and merged |
…Dialect for MySQL. Add a MySql specific dialect converter for converting from byte to boolean. Original pull request: #168.
Consider Dialect-specific converters also in DatabaseClient.create(…) factory. Reduce constant visibility. Add tests. Reformat code. Original pull request: #168.
@schauder This is my initial work on the optimistic record locking that incorporates the work Tom did originally. I wanted to get some eyes on this to make sure I am on the right track. I still need to add some more tests and this is very much a work in progress.