birdy
birdy

Reputation: 9646

How to fix & avoid: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect)

I am doing performance test on my grails application using jMeter. In a scenario when concurrent users hit the site, sometime I see the following error in the logs:

Excepton: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) [com.mypack.app.MyClass#1113]

How can I fix and avoid errors like these in the future?

Below is the full stack:

->>  245 | doCall    in com.mypack.app.MyController$_save_closure3_closure8
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
|    225 | save      in com.mypack.app.MyClontroller
|    195 | doFilter  in grails.plugin.cache.web.filter.PageFragmentCachingFilter
|     63 | doFilter  in grails.plugin.cache.web.filter.AbstractFilter
|   1145 | runWorker in java.util.concurrent.ThreadPoolExecutor
|    615 | run       in java.util.concurrent.ThreadPoolExecutor$Worker
^    745 | run . . . in java.lang.Thread

Upvotes: 0

Views: 1517

Answers (1)

Burt Beckwith
Burt Beckwith

Reputation: 75681

Grails defaults to optimistic locking, where it's assumed that there will be few or no concurrent updates, or that when they occur you have a strategy in place to resolve issues. This is similar to many source control systems, where two users can edit one file but if the edits overlap there is a conflict that must be manually resolved.

In general it's impractical to resolve concurrent edit issues. If you change one property in a MyClass instance and I change a different one, we could theoretically merge those two changes into one new record, but what if your change would have affected the change that I made if I had seen it first?

To keep things simple, if you allow concurrent edits and don't have a 'merge' strategy, you should use explicit locking. This does affect performance, but if you only lock for specific domain classes or for specific workflows, the impact should be small, especially if the time that a record is kept locked is short.

But locking in web apps is tricky. You need to lock when you start the 'edit' workflow and load the domain class instance to populate that GSP, and keep the record locked while the user makes changes in the browser, and release the lock when the 'update' action finishes (either successfully or not). This isn't supported in Grails because each request gets its own Hibernate Session and it's closed (along with its Connection, which would release your lock) at the end of each request. And if you were to implement this you'd have to make sure that if the update action isn't called (or is called after a max allowable time) that the lock is freed to allow others to edit.

So you're back to a manual merge strategy (or Webflow or something similar). You can remove automatic version checking in the domain class by adding version false in the mapping block. Then in your update action, convert the block that checks for stale versions to custom code that looks at versions but also at changed fields, and uses whatever logic makes sense for your domain class(es) to resolve/merge concurrent edits.

Upvotes: 3

Related Questions