micronaut-projects / micronaut-data

Ahead of Time Data Repositories
Apache License 2.0
464 stars 195 forks source link

docs: How optimistic locking interacts with @Transactional should be documented #2955

Closed mikehearn closed 4 months ago

mikehearn commented 4 months ago

Issue description

When using op-locks it's typical that the unhappy path (OptimisticLockException) will automatically cause the transactional code to be re-executed so it can re-read the new version of the entity and have another go. I would assume this happens in Micronaut Data but the docs are silent on this point and no example is given of how you'd use this in practice (with jdbc).

dstepanov commented 4 months ago

What other frameworks do that?

mikehearn commented 4 months ago

Hmm, you got me, this is a common pattern in Permazen but that isn't a Micronaut/Spring-like framework.

Let me put it another way: if the framework doesn't re-execute for you, then how are you meant to handle OptimisticLockException? You can't catch it and retry inside a @Transactional method because you'd just read the same data again (with a high consistency level). Is it implied that you should only combine it with programmatic transactions and implement your own retry loop? How does it interact with @Retry, if at all?

dstepanov commented 4 months ago

I don't think retrying makes sense, or at least the dump one. Essentially, you made some operation based on the data you had before, now you might need to reconsider the change or ask the user to redo it, etc

graemerocher commented 4 months ago

it isn't safe for a framework to handle OptimisticLockException automatically without causing data loss. The framework cannot know what a safe merge of the data looks like since that is domain specific knowledge of the application. The developer has to catch the exception and then write logic to perform the merge manually, or like Denis said send the request back to the user and ask them to try again and correct the merge manually. Think of how git works for VCS.

mikehearn commented 4 months ago

In the past what I've found is that the merge happens automatically if you retry, because the logic in the transaction is of the form "read some stuff, make some changes respecting what's there, write it back". If the merge really can't be done by code then yes you have no choice but to throw it back to the user and try to avoid losing their work with some sort of custom merge UI, but that's definitely the worst case scenario. In other cases it's sufficient to retry because the merge logic effectively already exists.

Still, it's not a big deal either way. With @Transactional I guess you can use that on an inner method and wrap the call in try/catch, or just use the programmatic API. I'll close this.