【问题标题】:Quartz job stops executing on StaleObjectStateException exceptionQuartz 作业因 StaleObjectStateException 异常而停止执行
【发布时间】:2013-04-29 22:53:57
【问题描述】:

我在 Grails 项目 (grails 2.2.1) 上遇到了石英问题(插件 :quartz2:2.1.6.2,但我甚至使用插件 :quartz:1.0-RC7 进行了测试,但问题没有改变)。

我有这样的工作

class MyJob {

def concurrent = false

def execute(context){

        try {

            //....
            // works with domains .....
            myDomain.save(flush: true)
            // works with domains .....
            //....

            sessionFactory.currentSession.flush()

        } catch (org.springframework.dao.OptimisticLockingFailureException olfe) {
            println "Job failed by database exception "
        } catch ( org.springframework.orm.hibernate3.HibernateOptimisticLockingFailureException ole){
            println "Job failed by database exception "
        } catch ( org.hibernate.HibernateException hibe ){
            println "Job failed by database exception "
        }
    }

}

}

有时在执行方法中会出现 StaleObjectStateException。这符合我的逻辑,我正在使用 grails 乐观锁定,并且这个异常每周只发生一次。

问题是当这个异常发生时 Job 停止再次触发。

我已尝试将方法代码共同包装在 try catch 中并在内部刷新休眠会话以捕获异常但没有运气。我的任何捕获都没有捕获异常。

在网上我发现这是一个old grails quartz bug,但它已修复,无论如何使用 try{}catch 必须绕过该错误。

附: 该作业是通过这种类型的调用从引导程序调度的

MyJob.schedule( 10000L )

停止调度的异常是

[194949896] core.ErrorLogger Unable to notify JobListener(s) of Job that was executed: (error will be ignored). trigger= DEFAULT.MT_3tbn6lewgiqa3 job= DEFAULT.MyJob
org.quartz.SchedulerException: JobListener 'persistenceContextJobListener' threw exception: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [MyDomain#42] [See nested exception: org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [MyDomain#42]]
    at org.quartz.core.QuartzScheduler.notifyJobListenersWasExecuted(QuartzScheduler.java:1939)
    at org.quartz.core.JobRunShell.notifyJobListenersComplete(JobRunShell.java:361)
    at org.quartz.core.JobRunShell.run(JobRunShell.java:235)
    at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:557)
Caused by: org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [MyDomain#42]
    at grails.plugin.quartz2.PersistenceContextJobListener.jobWasExecuted(PersistenceContextJobListener.groovy:46)
    at org.quartz.core.QuartzScheduler.notifyJobListenersWasExecuted(QuartzScheduler.java:1937)
    ... 3 more

.....

events.PatchedDefaultFlushEventListener Could not synchronize database state with session
org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [MyJob#42]
    at MyJob.execute(MyJob.groovy:354)
    at grails.plugin.quartz2.GrailsArtefactJob.execute(GrailsArtefactJob.java:57)
    at org.quartz.core.JobRunShell.run(JobRunShell.java:213)
    at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:557)

【问题讨论】:

  • 在你的代码示例中你没有捕捉到'org.hibernate.StaleObjectStateException',所以我想这是正常的工作失败......
  • @moskiteau org.hibernate.StaleObjectStateException 是一个 org.hibernate.HibernateException

标签: hibernate grails exception-handling quartz-scheduler grails-2.0


【解决方案1】:

不知道您的示例有多准确,但您需要知道 groovy 正在包装异常。这意味着即使代码抛出 StaleObjectStateException,您也可以将其包装在上面未捕获的简单 RuntimeException 中。 myDomain.save(flush: true) 方法有多深(直接在工作中执行或从其他服务执行)?

【讨论】:

    【解决方案2】:

    我遇到了类似的问题,quartz 作业在一个没有绑定休眠会话的线程上运行,我能够通过抓取一个新会话然后强制刷新()和清除来绕过它()。如果您不刷新和清除作业,最终将重新使用以前的作业线程之一并尝试写出相同的对象(不记得它是否必须是相同的对象或同一类的任何对象) ,但在绑定到该线程的会话中会有一个未提交的副本,这反过来会导致 StaleObjectException:

    我的代码如下所示:

    def sessionFactory
    
    def execute() {
        def session = SessionFactoryUtils.getSession(sessionFactory,true)
    
        myDomain.save(flush: true)
    
        session.flush()
        session.clear()
    }
    

    您可能只需要在示例代码中执行 flush() 和 clear() 即可获得相同的结果。

    【讨论】:

    • 比你小。尝试冲洗和清除。问题不是我的项目假定的 StaleObjectException 。问题是当 StaleObjectException 发生时我无法捕捉到它并且石英停止执行其他调用
    【解决方案3】:

    我很抱歉恢复旧帖子,但我们最近在旧版 Grails 应用程序 (Grails 2.2.3) 中遇到了这个问题,并且在执行方法中刷新会话并不能总是解决问题,所以我将概述我们做了什么解决问题。

    在我们的例子中,有时异常会在执行方法的上下文之外发生,即使我们在执行方法中显式刷新会话也是如此。更具体地说,异常是在 Quartz2 插件的 PersistenceContextJobListener 代码中引发的,该代码在执行方法完成执行后刷新会话。因此,在查看 Quartz2 插件代码后,我们意识到我们需要覆盖默认的 PersistenceContextJobListener,它包装了作业执行方法并刷新会话。

    首先,请注意 PersistenceContextJobListener 的 jobWasExecuted 回调方法中没有异常处理。

    https://github.com/9ci/grails-quartz2/blob/master/src/groovy/grails/plugin/quartz2/PersistenceContextJobListener.groovy#L44

    您真正需要做的就是实现自己的作业侦听器并将 jobWasExecuted 代码包装在 try/catch 中。有关如何执行此操作的示例,请参见以下代码 sn-ps。

    https://gist.github.com/jmiranda/45084eb32f07f6e3d1934547cd4fbb9f https://gist.github.com/jmiranda/5148f0a67afc8950bad950793e9c2303

    我们以原始 Quartz 插件的 SessionBinderJobListener 为例(错误,我们或多或少地复制了它)。

    https://github.com/grails-plugins/grails-quartz/blob/master/src/main/groovy/grails/plugins/quartz/listeners/SessionBinderJobListener.java

    无论如何,这应该可以让您防止触发的作业由于未捕获的 StaleObjectStateException 而完全停止。

    【讨论】:

    • 贾斯汀做得很好。我不再从事那个项目,我无法测试解决方案,但对我来说似乎很好。
    猜你喜欢
    • 1970-01-01
    • 2012-03-19
    • 2019-12-07
    • 2017-04-20
    • 1970-01-01
    • 2018-03-18
    • 1970-01-01
    • 1970-01-01
    • 2014-02-06
    相关资源
    最近更新 更多