【问题标题】:concurrent transaction in grails resulting in database stale state exceptiongrails中的并发事务导致数据库陈旧状态异常
【发布时间】:2013-11-10 13:58:49
【问题描述】:

以下是我在 api 调用上运行的代码。下面的代码在 grails 服务中,默认情况下是事务性的。但即使在锁定行之后,我也收到此错误:Message: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect)。只有一个计划在数据库中,因此 for 循环只运行一次。但是对 api 的并发调用会导致错误。请帮我解决这个问题

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)
                }
        }
    }

【问题讨论】:

    标签: hibernate grails grails-orm


    【解决方案1】:

    只有在阅读计划后才能锁定计划。所以多个线程可以同时读取计划。然后一个线程锁定它并更新它,另一个线程锁定它并更新它。但是它们都是同时读取的,所以它们都读取相同的数据,内部具有相同的版本:

    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
    

    阅读时需要加锁(也就是悲观加锁),as documented

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

    那么第二个线程必须等待第一个线程保存并提交其事务,然后才能读取相同的计划。

    这具有悲观锁定的所有缺点:吞吐量可能会降低,因为一次只有一个事务可以使用该计划,而这正是乐观锁定试图避免的。代价是您可能会遇到异常并且必须处理它们(通过重试、显示错误消息或根据情况提供任何最佳答案)

    【讨论】:

      猜你喜欢
      • 2015-11-29
      • 2015-08-30
      • 2013-09-10
      • 1970-01-01
      • 1970-01-01
      • 2022-12-20
      • 2011-09-23
      • 1970-01-01
      • 2014-10-25
      相关资源
      最近更新 更多