Grails Can';无法找出StaleObjectStateException的原因

Grails Can';无法找出StaleObjectStateException的原因,grails,gorm,quartz-scheduler,optimistic-locking,staleobjectstate,Grails,Gorm,Quartz Scheduler,Optimistic Locking,Staleobjectstate,我很难找出我一直看到的原因: `HibernateOptimisticLockingFailureException: FlowExecution: optimistic locking failed; nested exception is org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorre

我很难找出我一直看到的原因:

`HibernateOptimisticLockingFailureException: FlowExecution: optimistic locking failed; nested exception is org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect)`
我有一个使用Quartz Scheduler启动作业的服务,在我的上下文中,这些作业称为
,每个流可能由多个
任务组成
,流和任务是
可执行文件
,有关其实际
执行
的信息存储为
流执行
任务执行
。该服务使用
FlowService
启动流

UPD:有一个石英作业,“ExecutorJob”负责触发我的流/任务。当它被触发时,它使用FlowService启动它应该启动的任何东西。所以我想知道石英线程是否有可能在每次使用服务时都没有创建新的hibernate会话,这就是问题的原因。我没有改变FlowService的作用域,所以它是一个单例,GORM如何管理它使用的会话

UPD2:尝试在ExecutorJob上使用persistenceContextInterceptor,以确保每次使用该服务都使用一个新会话,但没有解决问题。添加了执行器作业的简化代码

我无法在本地重现这个问题,但它在生产中经常发生,更具体地说,当有很多流需要启动时。 我尝试过同步任务和流的
execute
方法,但没有成功,我现在尝试使用悲观锁,但我猜它不会解决问题,因为检查应用程序日志似乎没有两个线程更新同一行。下面,我试图展示一个简化版本的代码,模拟项目的结构

// ------------------
// DOMAIN CLASSES
// ------------------
abstract class Executable {
    static hasMany = [flowTasks: FlowTask]
    static transients = ['executions']

    List<Execution> getExecutions() {
        this.id ? Execution.findAllByExecutable(this) : []
    }

    void addToExecutions(Execution execution) {
        execution.executable = this
        execution.save()
    }

    abstract List<Execution> execute(Map params)
}

class Flow extends Executable {
    SortedSet<FlowTask> tasks
    static hasMany = [tasks: FlowTask]

    private static final Object lockExecute = new Object()
    private static final Object lockExecuteTask = new Object()

    List<FlowExecution> execute(Map params) {
        synchronized (lockExecute) {
            List<Map> multiParams = multiplyParams(params)
            multiParams.collect { Map param ->
                FlowExecution flowExecution = new FlowExecution()
                addToExecutions(flowExecution)
                flowExecution.save()
                this.attach()
                save()
                executeTasks(firstTasks(param), flowExecution, param)
            }
        }
    }

    List<Map> multiplyParams(Map params) {
        // creates a list of params for the executions that must be started
        [params]
    }

    Set<FlowTask> firstTasks(Map params) {
        // finds the first tasks to be executed for the flow
        tasks.findAdll { true }
    }

    private FlowExecution executeTasks(Set<FlowTask> tasks, FlowExecution flowExecution, Map params) {
        synchronized (lockExecuteTask) {
            tasks.each { FlowTask flowTask ->
                try {
                    List<Execution> executions = flowTask.execute(params)
                    executions.each { Execution execution ->
                        flowExecution.addToExecutions(execution)
                    }
                    flowExecution.attach()
                } catch {
                    // log error executing task
                    throw e
                }            
            }

            this.attach()
            try {
                save(flush: true)
            } catch (HibernateOptimisticLockingFailureException e) {
                // log error saving flow
                throw e
            }

            flowExecution
        }
    }

}

class Task extends Executable {
    private static final Object lockExecute = new Object()
    private static final Object lockGetExecution = new Object()

    TaskExecution execute(TaskExecution execution) {
        taskService.start(execution)
        execution
    }

    List<TaskExecution> execute(Map params) {
        synchronized (lockExecute) {
            List<Map> multiExecParams = multiplyParams(params)
            multiExecParams.collect { Map param ->
                TaskExecution execution = getExecution(param)
                execute(execution)
            }
        }
    }

    TaskExecution getExecution(Map params) {
        synchronized (lockGetExecution) {
            TaskExecution execution = new TaskExecution(executable: this)
            execution.setParameters(params)
            addToExecutions(execution)

            execution.attach()
            execution.flowExecution?.attach()
            this.attach()
            try {
                save(flush: true)
            } catch (HibernateOptimisticLockingFailureException e) {
                // log error saving task
                throw e
            }

            execution
        }
    }

    List<Map> multiplyParams(Map params) {
        // creates a list of params for the tasks that must be started
        [params]
    }

}

class FlowTask {
    static belongsTo = [flow: Flow, executable: Executable]

    List<Execution> execute(Map params) {
        executable.execute(params)
    }
}

abstract class Execution {
    Map parameterData = [:]
    static belongsTo = [executable: Executable, flowExecution: FlowExecution]
    static transients = ['parameters', 'taskExecutions']   
    void setParameters(Map params) {
        params.each { key, value ->
            parameterData[key] = JsonParser.toJson(value)
        }
    }
}

class TaskExecution extends Execution {
}

class FlowExecution extends Execution {
    List<Execution> executions
    static transients = ['executions']

    FlowExecution() {
        executions = []
    }

    Set<TaskExecution> getTaskExecutions() {
        executions?.collect { Execution execution ->
            return execution.taskExecution
        }?.flatten()?.toSet()
    }

    void addToExecutions(Execution execution){
        executions.add(execution)
        execution.flowExecution = this
        execution.save()
    }

    def onLoad() {
        try {
            executions = this.id ? Execution.findAllByFlowExecution(this) : []
        } catch (Exception e){
            log.error(e)
            []
        }
    }
}

// -----------------
// SERVICE CLASSES
// -----------------
class FlowService {

    Map start(long flowId, Map params) {
        Flow flow = Flow.lock(flowId)

        startFlow(flow, params)
    }

    private Map startFlow(Flow flow, Map params) {
        List<RunningFlow> runningFlows = flow.execute(params) 

        [data: [success: true], status: HTTP_OK]
    }
}

//--------------------------------------
// Quartz job
//--------------------------------------
class ExecutorJob implements InterruptableJob {

    def grailsApplication = Holders.getGrailsApplication()

    static triggers = {}

    private Thread thread

    void execute(JobExecutionContext context) throws JobExecutionException {
        thread = Thread.currentThread()
        synchronized (LockContainer.taskLock) {
            Map params = context.mergedJobDataMap
            def persistenceInterceptor = persistenceInterceptorInstance

            try {
                persistenceInterceptor.init()

                Long executableId = params.executableId as Long

                def service = (Executable.get(executableId) instanceof Flow) ? flowServiceInstance : taskServiceInstance
                service.start(executableId, params)
            } catch (Exception e) {
                // log error
            } finally {
                persistenceInterceptor.flush()
                persistenceInterceptor.destroy()
            }
        }
    }

    PersistenceContextInterceptor getPersistenceInterceptorInstance() {
        grailsApplication.mainContext.getBean('persistenceInterceptor')
    }

    FluxoService getFlowServiceInstance() {
        grailsApplication.mainContext.getBean('flowService')
    }

    TarefaService getTaskServiceInstance() {
        grailsApplication.mainContext.getBean('taskService')
    }

    @Override
    void interrupt() throws UnableToInterruptJobException {
        thread?.interrupt()
    }    
}
//------------------
//域类
// ------------------
抽象类可执行文件{
静态hasMany=[flowTasks:FlowTask]
静态瞬态=['executions']
列表getExecutions(){
this.id?Execution.findAllByExecutable(this):[]
}
void addToExecutions(执行){
execution.execute=此
execution.save()
}
抽象列表执行(映射参数)
}
类流扩展了可执行文件{
分类集任务
静态hasMany=[tasks:FlowTask]
私有静态最终对象lockExecute=新对象()
私有静态最终对象lockExecuteTask=新对象()
列表执行(映射参数){
同步(锁执行){
列表多参数=多参数(参数)
multiParams.collect{Map param->
FlowExecution FlowExecution=新的FlowExecution()
addToExecutions(流执行)
flowExecution.save()
这个。附加()
保存()
executeTasks(firstTasks(param)、flowExecution、param)
}
}
}
列出多个参数(映射参数){
//为必须启动的执行创建参数列表
[参数]
}
设置第一个任务(映射参数){
//查找要为流执行的第一个任务
tasks.findadell{true}
}
私有FlowExecutionExecuteTasks(设置任务、FlowExecutionFlowExecution、映射参数){
已同步(lockExecuteTask){
tasks.each{FlowTask FlowTask->
试一试{
列表执行=flowTask.execute(参数)
executions.each{Execution Execution->
flowExecution.addToExecutions(执行)
}
flowExecution.attach()
}抓住{
//执行任务时发生日志错误
掷e
}            
}
这个。附加()
试一试{
保存(刷新:真)
}捕获(HibernateOptimisticLockingFailureException e){
//日志错误保存流
掷e
}
流执行
}
}
}
类任务扩展可执行文件{
私有静态最终对象lockExecute=新对象()
私有静态最终对象lockGetExecution=新对象()
TaskExecution执行(TaskExecution执行){
taskService.start(执行)
处决
}
列表执行(映射参数){
同步(锁执行){
List multiExecParams=多参数(params)
multiExecParams.collect{Map param->
TaskExecution=getExecution(参数)
执行
}
}
}
TaskExecution getExecution(映射参数){
已同步(lockGetExecution){
TaskExecution=new TaskExecution(可执行文件:this)
execution.setParameters(参数)
addToExecutions(执行)
execution.attach()
execution.flowExecution?.attach()
这个。附加()
试一试{
保存(刷新:真)
}捕获(HibernateOptimisticLockingFailureException e){
//日志错误保存任务
掷e
}
处决
}
}
列出多个参数(映射参数){
//为必须启动的任务创建参数列表
[参数]
}
}
类流任务{
静态belongsTo=[flow:flow,executable:executable]
列表执行(映射参数){
executable.execute(params)
}
}
抽象类执行{
映射参数data=[:]
静态belongsTo=[可执行文件:可执行文件,流执行:流执行]
静态瞬变=[“参数”,“任务执行”]
void setParameters(映射参数){
params.each{键,值->
parameterData[key]=JsonParser.toJson(值)
}
}
}
类TaskExecution扩展了执行{
}
类FlowExecution扩展了执行{
列出执行情况
静态瞬态=['executions']
流程执行(){
执行情况=[]
}
集热
def b = Book.get(1)
…
b.refresh()