Hi again Tomasz,

Based on the discussion so far (the fact that you only do one fetch, one 
thread, only once), I now doubt that this is your problem. The bug my patch 
is for is purely a concurrency problem as far as I can tell.

I guess it is at least worth a try, so I have attached the patch for you.

I had to rip it out of a larger patch, but it is pretty simple and is only one 
file so I think I got it correctly. If you do have any problems, let me know.

Good luck,
Sam

On July 8, 2004 07:00 am, Tomasz Piontek wrote:
> Hi,
>
> I don't know whether it's exactly the same problem, but is very similar
> to yours.
> In my case there is no necessity to start many threads. Even in
> one-threaded application I get following exception:
>
> org.exolab.castor.jdo.PersistenceException: Lock conflict: attempt to
> load object of type dbItem with identity 1 in two different locks modes
> (exclusive and non exclusive) in the same transaction
>     at
> org.exolab.castor.persist.TransactionContext.load(TransactionContext.java:6
>27) at
> org.exolab.castor.persist.TransactionContext.load(TransactionContext.java:5
>55) at org.exolab.castor.persist.ClassMolder.load(ClassMolder.java:764) at
> org.exolab.castor.persist.LockEngine.load(LockEngine.java:356) at
> org.exolab.castor.persist.TransactionContext.load(TransactionContext.java:6
>49) at
> org.exolab.castor.persist.TransactionContext.load(TransactionContext.java:5
>55) at org.exolab.castor.persist.ClassMolder.load(ClassMolder.java:793) at
> org.exolab.castor.persist.LockEngine.load(LockEngine.java:356) at
> org.exolab.castor.persist.TransactionContext.load(TransactionContext.java:6
>49) at org.exolab.castor.persist.QueryResults.fetch(QueryResults.java:229)
> at
> org.exolab.castor.jdo.engine.OQLQueryImpl$OQLEnumeration.hasMore(OQLQueryIm
>pl.java:627) at
> org.exolab.castor.jdo.engine.OQLQueryImpl$OQLEnumeration.hasMore(OQLQueryIm
>pl.java:610) at LockTest.getItem(LockTest.java:176)
>     at LockTest.runWriter(LockTest.java:118)
>     at LockTest.run(LockTest.java:105)
>
> Maybe I do something wrong or a stupid error, but it doesn't work in one
> thread :-(
> It is somehow connected with 1-N relatationships, because my simple test
> program stopped working when I added 1-N relationship.
>
> I tested the problem with castor-0.9.5.3.
> BTW does anyone know when the next castor version will be released?
> According to the web-page it should be done on "June, 7 2004".
>
> If it is possible please send your patch to me. I will recheck the
> problem with patched 0.9.5.1.
> All the best,
> Tomasz
>
> Sam Maloney wrote:
> >Hi Tomasz,
> >
> >I found and fixed recently a bug that caused that exact error.
> >
> >The bug would happen in this situation:
> >
> >Any tree of mapped objects where there is at least one 1:N relationships
> > to a dependant object. ie:
> >
> >Store <-->> Register (depends=Store)
> >
> >These can even be empty (minus the Pk). All that is needed to cause this
> > is a 1:N relationship where the Ns are dependant on the 1 AND the
> > parent/reverse relationship (in the map) from the N to the 1 MUST be
> > there for the bug to happen.
> >
> >In any case with such a setup (which is most likely very common, and
> > appears to be your case), the bug would happen simply by spawning two or
> > more threads concurrently that fetch the same object at the same time,
> > even both in the same access mode (exclusive or dblocked).
> >
> >ie:
> >-----
> >public static void testLockProblem() {
> >     Runnable runner = new Runnable() {
> >             public void run() {
> >                     _testLockProblem();
> >             }
> >     };
> >
> >     new Thread(runner, "test runner 1").start();
> >     new Thread(runner, "test runner 2").start();
> >}
> >
> >public static void _testLockProblem() {
> >     try {
> >             db.begin();
> >             db.load(Store.class, new Long(0), Database.DbLocked);
> >
> >             // Wait to simulate processing, thus holding the lock, thus enabling
> >             // concurrency to happen for sure.
> >             Object obj = new Object();
> >             synchronized(obj){
> >                     try {
> >                             obj.wait(1000);
> >                     } catch (InterruptedException e) {
> >                     }
> >             }
> >
> >             db.commit();
> >     } catch (Exception e) {
> >             throw new RuntimeException(e);
> >     }
> >}
> >-----
> >
> >If you run the above test on a simple tree as described above and this
> > problem happens every single time you run the test, then most likely it
> > is the exact same problem.
> >
> >If you confirm this is the case, then msg the list again and we can
> > discuss the patch. It is tested and works perfect, however it is an
> > unofficial patch against version 0.9.5.1.  So mention which version you
> > are using in your reply.
> >
> >Later,
> >Sam
> >
> >On July 7, 2004 06:14 am, Tomasz Piontek wrote:
> >>Hello,
> >>
> >>I would be very grateful if anyone could explain to me why the following
> >>code complains about the "LockConflict"?
> >>I make only one query in Exclusive mode, so why the QueryResults tries
> >>to lock the object again and causes LockConflict?
> >>
> >>I read the documentation and in my opinion the conflict is caused
> >>because the query was made (as it was specified in Exclusive mode) and
> >>when the object is loaded by QueryResults it is done in default mode (I
> >>didn't specified "access" attribute in mapping file) which is Shared one.
> >>
> >>Could anyone tell me what did I wrong. I want to query from my code
> >>objects as well in ReadOnly as in Exclusive mode. I don't want to use
> >>Shared mode.
> >>
> >>It's not a productive code, I simplified my code as it was possible.
> >>
> >>   public void manual()
> >>   {
> >>       JDO jdo = null;
> >>       Database db = null;
> >>       QueryResults result = null;
> >>               try {
> >>           jdo = new JDO();
> >>           jdo.setDatabaseName( "grms-devel_jobreg" );
> >>           jdo.setConfiguration( "./etc/jobreg_database.xml" );
> >>                     db = jdo.getDatabase();
> >>                     db.begin();
> >>                     OQLQuery query = db.getOQLQuery("select job from
> >>grms.jobreg.JobInfo job where job.id=$1");
> >>           query.bind("JOB_ID0");
> >>                     result = query.execute( Database.Exclusive);
> >>                     JobInfo job = null;
> >>           if( result.hasMore())
> >>               job = (JobInfo)result.next();
> >>
> >>            //some updates
> >>                     System.out.println( "JOB_NAME = " + job.jobId);
> >>                     db.commit();
> >>           db.close();
> >>       } catch ( Exception e) {
> >>           e.printStackTrace();
> >>       }
> >>   }
> >>
> >>The executions finishes with following exception:
> >>
> >>org.exolab.castor.jdo.PersistenceException: Lock conflict: attempt to
> >>load object of type grms.jobreg.JobInfo with identity 1 in two different
> >>locks modes (exclusive and non exclusive) in the same transaction
> >>       at
> >>org.exolab.castor.persist.TransactionContext.load(TransactionContext.java
> >>:6 27) at
> >>org.exolab.castor.persist.TransactionContext.load(TransactionContext.java
> >>:5 55) at
> >> org.exolab.castor.persist.ClassMolder.load(ClassMolder.java:764) at
> >> org.exolab.castor.persist.LockEngine.load(LockEngine.java:356) at
> >> org.exolab.castor.persist.TransactionContext.load(TransactionContext.jav
> >>a:6 49) at
> >>org.exolab.castor.persist.TransactionContext.load(TransactionContext.java
> >>:5 55) at
> >> org.exolab.castor.persist.ClassMolder.load(ClassMolder.java:793) at
> >> org.exolab.castor.persist.LockEngine.load(LockEngine.java:356) at
> >> org.exolab.castor.persist.TransactionContext.load(TransactionContext.jav
> >>a:6 49) at
> >>org.exolab.castor.persist.QueryResults.fetch(QueryResults.java:229)
> >>       at
> >>org.exolab.castor.jdo.engine.OQLQueryImpl$OQLEnumeration.hasMore(OQLQuery
> >>Im pl.java:627) at
> >>org.exolab.castor.jdo.engine.OQLQueryImpl$OQLEnumeration.hasMore(OQLQuery
> >>Im pl.java:610) at
> >>grms.tests.jobreg.RepositoryThreads.manual(RepositoryThreads.java:198)
> >>       at
> >>grms.tests.jobreg.RepositoryThreads.main(RepositoryThreads.java:212)
> >>
> >>and the mapping file more less is:
> >>
> >><?xml version="1.0" encoding="UTF-8"?>
> >>
> >><mapping>
> >>   <description>Castor generated mapping file</description>
> >>
> >>   <class name="grms.jobreg.JobInfo" identity="dbId" key-generator="MAX">
> >>   <description>Default mapping for class
> >> grms.jobreg.JobInfo</description> <map-to xml="job-info"
> >> table="grms_repository_job"/>
> >>       <field name="dbId" type="int">
> >>       <bind-xml name="db-id" node="attribute"/>
> >>       <sql name="job_rep_job_id" type="integer"/>
> >>       </field>
> >>
> >>
> >>       <field name="id" type="java.lang.String">
> >>       <bind-xml name="id" node="element"/>
> >>       <sql name="job_id" type="char"/>
> >>       </field>
> >>
> >>       <field name="fileDirs" type="grms.jobreg.InOutFileDir"
> >>collection="array">
> >>       <bind-xml name="out-files" node="element"/>
> >>       <sql many-key="job_rep_job_id"/>
> >>       </field>
> >>
> >>       <field name="jobHistory" type="grms.jobreg.JobHistory"
> >>collection="array">
> >>       <bind-xml name="job-history" node="element"/>
> >>       <sql many-key="job_rep_job_id"/>
> >>       </field>
> >>
> >>       <field name="jobNotifications" type="grms.jobreg.JobNotification"
> >>collection="array">
> >>       <bind-xml name="job-notifications" node="element"/>
> >>       <sql many-key="job_rep_job_id"/>
> >>       </field>
> >>   </class>
> >>
> >>   <class name="grms.jobreg.JobHistory" identity="dbId"
> >>key-generator="MAX" depends="grms.jobreg.JobInfo">
> >>   <description>Default mapping for class
> >>grms.jobreg.JobHistory</description>
> >>   <map-to xml="job-history" table="grms_repository_history"/>
> >>       <field name="dbId" type="integer">
> >>       <bind-xml name="db-id" node="attribute"/>
> >>               <sql name="job_rep_history_id" type="integer"/>
> >>           </field>
> >>
> >>           <field name="homeDirectory" type="java.lang.String">
> >>       <bind-xml name="home-directory" node="element"/>
> >>       <sql name="home_directory" type="char"/>
> >>       </field>
> >>
> >>       <field name="jobInfo" type="grms.jobreg.JobInfo">
> >>       <bind-xml name="job-info" node="attribute"/>
> >>       <sql name="job_rep_job_id"/>
> >>       </field>
> >>
> >>   </class>
> >>
> >>
> >>   <class name="grms.jobreg.InOutFileDir" identity="dbId"
> >>key-generator="MAX" depends="grms.jobreg.JobInfo">
> >>       <description>Default mapping for class InOutFile</description>
> >>       <map-to table="grms_repository_filedir"/>
> >>
> >>           <field name="dbId" type="integer" direct="true">
> >>       <bind-xml name="db-id" node="attribute"/>
> >>               <sql name="job_rep_filedir_id" type="integer"/>
> >>           </field>
> >>
> >>           <field name="jobInfo" type="grms.jobreg.JobInfo"
> >> direct="true"> <bind-xml reference="true" node="attribute"/>
> >>               <sql name="job_rep_job_id"/>
> >>           </field>
> >>   </class>
> >>
> >>
> >>   <class name="grms.jobreg.JobNotification" identity="dbId"
> >>key-generator="MAX" depends="grms.jobreg.JobInfo">
> >>   <description>Default mapping for class
> >>grms.jobreg.JobNotification</description>
> >>   <map-to table="grms_repository_notif"/>
> >>       <field name="dbId" type="integer">
> >>       <bind-xml name="db-id" node="attribute"/>
> >>               <sql name="job_rep_notif_id" type="integer"/>
> >>           </field>
> >>
> >>       <field name="jobNotificationUsers"
> >>type="grms.jobreg.JobNotificationUser" collection="array">
> >>       <bind-xml name="job-notification-users" node="element"/>
> >>       <sql many-key="job_rep_notif_id"/>
> >>       </field>
> >>
> >>       <field name="JobInfo" type="grms.jobreg.JobInfo">
> >>               <bind-xml reference="true" node="attribute"/>
> >>       <sql name="job_rep_job_id"/>
> >>           </field>
> >>  </class>
> >>
> >>  <class name="grms.jobreg.JobNotificationUser" identity="dbId"
> >>key-generator="MAX" depends="grms.jobreg.JobNotification">
> >>   <description>Default mapping for class
> >>grms.jobreg.JobNotificationUser</description>
> >>   <map-to table="grms_repository_notif_dn"/>
> >>       <field name="dbId" type="int">
> >>       <bind-xml name="db-id" node="attribute"/>
> >>       <sql name="job_rep_notif_dn_id" type="integer"/>
> >>       </field>
> >>
> >>       <field name="JobNotification" type="grms.jobreg.JobNotification">
> >>               <bind-xml reference="true" node="attribute"/>
> >>       <sql name="job_rep_notif_id"/>
> >>           </field>
> >>
> >>  </class>
> >></mapping>
> >>
> >>I will be very grateful for any hints how to solve the problem and for
> >>information what is the casue of it.
> >>
> >>All the best,
> >>Tomasz
> >
> >-----------------------------------------------------------
> >If you wish to unsubscribe from this mailing, send mail to
> >[EMAIL PROTECTED] with a subject of:
> >        unsubscribe castor-dev
Index: LockEngine.java
===================================================================
RCS file: /cvsroot/castor/src/org/exolab/castor/persist/LockEngine.java,v
retrieving revision 1.2
retrieving revision 1.4
diff -u -3 -p -u -r1.2 -r1.4
--- LockEngine.java	30 Jun 2004 18:39:22 -0000	1.2
+++ LockEngine.java	9 Jul 2004 15:12:03 -0000	1.4
@@ -40,7 +40,7 @@
  *
  * Copyright 1999 (C) Intalio, Inc. All Rights Reserved.
  *
- * $Id: LockEngine.java,v 1.2 2004/06/30 18:39:22 samm Exp $
+ * $Id: LockEngine.java,v 1.4 2004/07/09 15:12:03 samm Exp $
  */
 
 
@@ -99,7 +99,7 @@ import org.exolab.castor.util.Messages;
  *
  * @author <a href="[EMAIL PROTECTED]">Assaf Arkin</a>
  * @author <a href="[EMAIL PROTECTED]">Thomas Yip</a>
- * @version $Revision: 1.2 $ $Date: 2004/06/30 18:39:22 $
+ * @version $Revision: 1.4 $ $Date: 2004/07/09 15:12:03 $
  */
 public final class LockEngine {
 
@@ -905,8 +905,12 @@ public final class LockEngine {
         ObjectLock lock;
         TypeInfo   typeInfo;
         typeInfo = (TypeInfo) _typeInfo.get( oid.getName() );
-        lock = typeInfo.release( oid, tx );
+
+        lock = (ObjectLock)typeInfo.locks.get(oid);
+        
+        //FIXME: Perhaps this should be synchronized on 'locks'.
         lock.getOID().setDbLock( false );
+        lock = typeInfo.release( oid, tx );
 
     }
 
@@ -1165,6 +1169,7 @@ public final class LockEngine {
             ObjectLock entry = null;
             boolean newentry = false;
             boolean failed = true;
+            boolean update_entry_oid = false;
             // sync on "locks" is, unfortunately, necessary if we employ
             // some LRU mechanism, especially if we allow NoCache, to avoid
             // duplicated LockEntry exist at the same time.
@@ -1188,7 +1193,8 @@ public final class LockEngine {
                     entry = new ObjectLock( oid );
                     locks.put( oid, entry );
                 } else {
-                    oid = entry.getOID();
+//                    oid = entry.getOID();
+                    update_entry_oid = true;
                 }
                 entry.enter();
             }
@@ -1217,6 +1223,11 @@ public final class LockEngine {
                     throw new IllegalArgumentException( "lockType "+lockAction+" is undefined!");
                 }
                 failed = false;
+
+                if (update_entry_oid) {
+                    entry.setOID(oid);
+                }
+
                 return entry;
             } finally {
                 synchronized( locks ) {
----------------------------------------------------------- 
If you wish to unsubscribe from this mailing, send mail to
[EMAIL PROTECTED] with a subject of:
        unsubscribe castor-dev

Reply via email to