Hi,
I have two related classes, Job and Task. Although there's no relation from Job to Task, there's a many-to-one relation from Task to Job. (IE, there's a 1-to-many relationship here but only 1 side has been modelled.)
There are many tasks, and each task operates in it's own thread. The Job also operates in a thread of it's own.
The task records are updated only by the owning task-thread, however the Job record can be updated by any thread. (The Job thread can update a Task record in the database only on creation, when the task-thread doesn't exist, or when it concludes the task-thread has died.)
I'm expecting some degree of contention on the Job record, however I'm getting deadlocks and StaleObjectExceptions on the TASK record from the task-threads!!
Is it possible that when I update the job in thread A, and then try to update the task in thread B, that thread B then gets an error because it sees that the job has been updated? And that the exception then mentions the Task instead of the Job?
I'm really stumped on the cause of these exceptions.
I'm using Hibernate 2.1.3, java-runtime is JDK1.3.1 from IBM's WAS5.0.2.2.
From the mapping document:
Code:
<class name="BatchJob" table="BATCH_JOB">
<id
column="JOBIDX"
name="jobIndex"
type="int"
unsaved-value="0"
>
<generator class="native" />
</id>
<version
column="SFTLCK"
name="softLock"
type="int"
unsaved-value="undefined"
/>
<property
column="CTRENGREF"
length="20"
name="contractEngineRef"
not-null="true"
type="string"
/>
<property
column="CMPREF"
length="20"
name="companyRef"
not-null="true"
type="string"
/>
<property
column="COLDTE"
name="collectionDate"
not-null="true"
type="java.sql.Date"
/>
<property
column="BEGREF"
length="20"
name="beginRef"
not-null="false"
type="string"
/>
<property
column="ENDREF"
length="20"
name="endRef"
not-null="false"
type="string"
/>
<property
column="BEGTIM"
name="beginTime"
not-null="true"
type="timestamp"
/>
<property
column="ENDTIM"
name="endTime"
not-null="false"
type="timestamp"
/>
<property
column="STA"
length="20"
name="status"
not-null="true"
type="string"
/>
<property
column="TSKHBTTIMITV"
name="taskHeartBeatTimeInterval"
not-null="true"
type="int"
/>
<property
column="TSKMAXREF"
name="taskMaxRef"
not-null="true"
type="int"
/>
<property
column="TSKMAXDUR"
name="taskMaxDuration"
not-null="true"
type="int"
/>
<property
column="JOBCHKTSKTIMITV"
name="jobCheckTaskTimeInterval"
not-null="true"
type="int"
/>
<property
column="JOBMAXTSK"
name="jobMaxTasks"
not-null="true"
type="int"
/>
<property
column="TSKREG"
name="tasksRegistered"
not-null="false"
type="int"
/>
<property
column="TSKINI"
name="tasksInitiated"
not-null="false"
type="int"
/>
<property
column="TSKACV"
name="tasksActive"
not-null="false"
type="int"
/>
<property
column="JMSMSG_ID"
length="50"
name="jmsMessageID"
not-null="true"
type="string"
/>
<property
column="HBTTIM"
name="heartBeatTimestamp"
not-null="true"
type="timestamp"
/>
<property
column="RTR"
name="retries"
not-null="false"
type="int"
/>
<property
column="REVWATPER"
name="reversalWaitPeriod"
not-null="true"
type="int"
/>
</class>
<class name="BatchTask" table="BATCH_TASK">
<id
column="TSKIDX"
name="taskIndex"
type="int"
unsaved-value="0"
>
<generator class="native" />
</id>
<version
column="SFTLCK"
name="softLock"
type="int"
unsaved-value="undefined"
/>
<many-to-one
column="JOBIDX"
name="job"
class="BatchJob"
not-null="true"
cascade="none"
/>
<property
column="CTRENGREF"
length="20"
name="contractEngineRef"
not-null="true"
type="string"
/>
<property
column="CMPREF"
length="20"
name="companyRef"
not-null="true"
type="string"
/>
<property
column="BEGREF"
length="20"
name="beginRef"
not-null="true"
type="string"
/>
<property
column="ENDREF"
length="20"
name="endRef"
not-null="true"
type="string"
/>
<property
column="BEGTIM"
name="beginTime"
not-null="true"
type="timestamp"
/>
<property
column="ENDTIM"
name="endTime"
not-null="false"
type="timestamp"
/>
<property
column="POLDONCNT"
name="nrOfPoliciesProcessed"
not-null="true"
type="int"
/>
<property
column="TSKSELREF"
name="taskSelectedRef"
not-null="true"
type="int"
/>
<property
column="TSKMAXREF"
name="taskMaxRef"
not-null="true"
type="int"
/>
<property
column="STA"
length="20"
name="status"
not-null="true"
type="string"
/>
<property
column="JMSMSG_ID"
length="50"
name="jmsMessageID"
not-null="true"
type="string"
/>
<property
column="CURREF"
length="20"
name="currentRef"
not-null="false"
type="string"
/>
<property
column="HBTTIM"
name="heartBeatTime"
not-null="true"
type="timestamp"
/>
<property
column="RTR"
name="retries"
not-null="false"
type="int"
/>
</class>
Exception stack trace:
Code:
net.sf.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) for com.unisys.pdg.common.data.BatchTask instance with identifier: 45
at net.sf.hibernate.persister.AbstractEntityPersister.check(AbstractEntityPersister.java(Compiled Code))
at net.sf.hibernate.persister.EntityPersister.update(EntityPersister.java(Compiled Code))
at net.sf.hibernate.persister.EntityPersister.update(EntityPersister.java(Compiled Code))
at net.sf.hibernate.impl.ScheduledUpdate.execute(ScheduledUpdate.java(Compiled Code))
at net.sf.hibernate.impl.SessionImpl.executeAll(SessionImpl.java(Compiled Code))
at net.sf.hibernate.impl.SessionImpl.execute(SessionImpl.java(Inlined Compiled Code))
at net.sf.hibernate.impl.SessionImpl.flush(SessionImpl.java(Compiled Code))
at com.unisys.pdg.common.dao._RootDAO.update(_RootDAO.java(Compiled Code))
at com.unisys.pdg.common.dao.base.BaseBatchTaskDAO.update(BaseBatchTaskDAO.java:96)
at com.unisys.pdg.common.batch.AbstractBatchTaskBean.doProcessPolicy(AbstractBatchTaskBean.java:77)
at com.unisys.pdg.gi.batch.EJSLocalStatelessGiBatchTask_4106ec18.doProcessPolicy(Unknown Source)
at com.unisys.pdg.common.batch.AbstractReceiveTaskMessageBean$2.perform(AbstractReceiveTaskMessageBean.java:139)
at com.unisys.pdg.common.batch.AbstractBatchMessageBean.performWithRetries(AbstractBatchMessageBean.java:61)
at com.unisys.pdg.common.batch.AbstractReceiveTaskMessageBean.handleMessage(AbstractReceiveTaskMessageBean.java:133)
at com.unisys.pdg.common.jms.ReceiveMessage.onMessage(ReceiveMessage.java:297)
at com.ibm.ejs.jms.listener.MDBWrapper$PriviledgedOnMessage.run(MDBWrapper.java:205)
at java.security.AccessController.doPrivileged(Native Method)
at com.ibm.ejs.jms.listener.MDBWrapper.callOnMessage(MDBWrapper.java:194)
at com.ibm.ejs.jms.listener.MDBWrapper.onMessage(MDBWrapper.java:172)
at com.ibm.mq.jms.MQSession.run(MQSession.java:1043)
at com.ibm.ejs.jms.JMSSessionHandle.run(JMSSessionHandle.java:922)
at com.ibm.ejs.jms.listener.ServerSession.connectionConsumerOnMessage(ServerSession.java:697)
at com.ibm.ejs.jms.listener.ServerSession.onMessage(ServerSession.java:482)
at com.ibm.ejs.jms.listener.ServerSession.dispatch(ServerSession.java:449)
at java.lang.reflect.Method.invoke(Native Method)
at com.ibm.ejs.jms.listener.ServerSessionDispatcher.dispatch(ServerSessionDispatcher.java:37)
at com.ibm.ejs.container.MDBWrapper.onMessage(MDBWrapper.java:91)
at com.ibm.ejs.container.MDBWrapper.onMessage(MDBWrapper.java:127)
at com.ibm.ejs.jms.listener.ServerSession.run(ServerSession.java:372)
at com.ibm.ws.util.ThreadPool$Worker.run(ThreadPool.java:593)
As you can see, it's all running inside IBM's websphere... (So I'm not managing my own threads, but WebSphere manages my threads, each task is an MDB instance).
Does anyone have a clue what can be going on here? To what extent can it be Hibernate, to what extent am I mis-understanding concurrency and softlocks in Hibernate? And to what extent is it WebSphere doing bad things and tripping me up?
I'm really stumped here :-(
With regards,
--Tim