concurrent transaction in grails resulting in database stale state exception

孤者浪人 提交于 2019-12-12 09:56:52

问题


following is my code that is run on an api call.The below code is in a grails service which are transactional by default.But even after locking the row,I am getting this error : Message: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect).There is just one plan in database so for loop is run just once.but there are concurrent calls to api which are causing error.Please help me fix this

def updateAllPlans(def user,def percentComplete,def chapterId){
        def plans = Plan.findAllWhere(user:user)
        def chapter = Chapter.findById(chapterId)
        for(def plan:plans){
                def foundChapters = plan.psubject.ptopics.psubtopics.pchapters.chapter.flatten().contains(chapter)
                if(foundChapters){
                    plan.lock()
                    if(percentComplete=='0'){
                        plan.durationViewComplete = plan.durationViewComplete.plusMillis(chapter.duration.getMillisOfSecond())
                        plan.durationViewComplete = plan.durationViewComplete.plusSeconds(chapter.duration.getSecondOfMinute())
                        plan.durationViewComplete = plan.durationViewComplete.plusMinutes(chapter.duration.getMinuteOfHour())
                        plan.durationViewComplete = plan.durationViewComplete.plusHours(chapter.duration.getHourOfDay())
                    }else{
                        plan.durationComplete = plan.durationComplete.plusMillis(chapter.duration.getMillisOfSecond())
                        plan.durationComplete = plan.durationComplete.plusSeconds(chapter.duration.getSecondOfMinute())
                        plan.durationComplete = plan.durationComplete.plusMinutes(chapter.duration.getMinuteOfHour())
                        plan.durationComplete = plan.durationComplete.plusHours(chapter.duration.getHourOfDay())
                    }

                    plan.save(flush:true, failOnError:true)
                }
        }
    }

回答1:


You only lock the plan after it has been read. So multiple threads can read the plan concurrently. Then one thread locks it and updates it, and the other thread locks it and updates it as well. But they both read it concurrently, so both read the same data, with the same version inside:

thread 1: read plan, with version = 3
thread 2: read plan, with version = 3
thread 1: lock plan
thread 1 : update and save plan. version becomes 4
thread 2 : lock plan
thread 2 : update and save plan. version in memory is 3, but version in database is 4, so an exception is thrown

You need to lock when reading (which is pessimistic locking), as documented:

def airport = Airport.findByName("Heathrow", [lock: true])

Then the second thread will have to wait for the first one to have saved and committed its transaction before reading the same plan.

This has all the disadvantages of pessimistic locking though: the throughput might be reduced because only one transaction at a time can use the plan, which is precisely what optimistic locking tries to avoid. The cost is that you can get exceptions and have to deal with them (by retrying, displaying an error message, or whatever is the best answer depending on the situation)



来源:https://stackoverflow.com/questions/19890556/concurrent-transaction-in-grails-resulting-in-database-stale-state-exception

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!