Hi,
 
 
We have a Doctor class whch is having N-1 relation with a Department class. In our test code when multiple threads tried to load and update different Doctor objects simultaneously, all but one are getting LockNotGranted(persis t.deadlock) exceptions.  The objects are loaded in Shared mode and cache-type is set to unlimited. Going through the castor code our finding is that:
 
Suppose there are two Doctors Doc1 & Doc2 both belonging to the same Department Dept1.
 
When a transaction T1 loads Doc1, Doc2 is also loaded because of its relation with the same Department and thus the T1 gets a read lock on Doc2 also.
 
When another concurrent Transaction T2 loads Doc2, Doc1 is also loaded because of its relation with the same Department and thus the T2 gets a read lock on Doc1 also.
 
When T1 requests for a write lock (upgrade using db.lock()) on Doc1, the request cannot be satisfied immediately as T2 is having read lock on Doc1
 
When T2 requests for a write lock (upgrade using db.lock()) on Doc2, the request is not satisfied as T1 is having read lock on Doc2, and here a dead lock is detected (in detectDeadLock() method of ObjectLock.java)
 
 
 
MAPPING.XML
 
<mapping>
 
 
 
  <!--  Mapping for Department-->
 <class name="Department" identity="id" key-generator="MAX" access="shared" >
 
 <cache-type type="unlimited" />
 <description>User</description>
  <map-to table="Department" />
 
  <field name="id" type ="long">
   <sql name="pk_department_id" type="bigint"/>
  </field>
  <field name="deptName" type="string">
   <sql name="deptName" type="varchar" dirty="check"/>
  </field>
 
  <field name="doctors" type="Doctor" required="false" collection="arraylist">
   <sql many-key="dept_id"/>
  </field>
 </class>
 

  <!--  Mapping for Doctor-->
 <class name="Doctor" identity="id" key-generator="MAX" access="shared" >
 
 <cache-type type="unlimited" />
 <description>Doctor</description>
  <map-to table="doctor" />
 
  <field name="id" type ="long">
   <sql name="pk_doctor_id" type="bigint"/>
  </field>
  <field name="userName" type="string">
   <sql name="UserName" type="varchar"  dirty="check"/>
  </field>
  
  <field name="dept" type="Department">
   <sql name="dept_id" />
  </field>
 </class>
  </mapping>
 
 
TEST.JAVA
 
public class Test
{
 

    public static final String DatabaseFile = "/config/database.xml";
 
   
    public static final String MappingFile = "/config/mapping.xml";
 

    public static final String Usage = "Usage: example jdo";
 

    private static Mapping  _mapping;
 

    private static JDO      _jdo;
 
  static boolean bStart=false;
 
  static long id=1;
 
    public static void main( String[] args ) throws Exception
    {
   new Test();
//   new Test();   
//   new Test();
  }
  
    public Test() throws Exception
    {
 

    try
   {
    // Load the mapping file
        _mapping = new Mapping( getClass().getClassLoader() );
        _mapping.loadMapping( getClass().getResource( MappingFile ) );
 
        _jdo = new JDO();
        _jdo.setConfiguration( getClass().getResource( DatabaseFile ).toString() );
        _jdo.setDatabaseName( "mqtest" );
 
    OurThread th1 = new OurThread();
    OurThread th2 = new OurThread();
    OurThread th3 = new OurThread();
    OurThread th4 = new OurThread();
    OurThread th5 = new OurThread();
    do{
     bStart=false;
     Thread.yield();
    }while(th1.bReached==false || th2.bReached==false || th3.bReached==false || th4.bReached==false || th5.bReached==false );
 
    bStart=true;
 
    th1.join();
    th2.join();
    th3.join();
    th4.join();
    th5.join();
   }
   catch (Exception e)
   {
    System.out.println("Exception Main"+e.getMessage());
   }
    }
 
  public Random rand=new Random();
 
  class OurThread extends Thread
  {
   public boolean bReached=false;
   public OurThread()
   {
    bReached=false;
    start();
   }
  
  public void run()
  {
   Database  db1=null;
   try
   {
    db1 = _jdo.getDatabase();
    db1.begin();
    long did=++id;
    Doctor doc=(Doctor)db1.load(Doctor.class,new Long(did),Database.Shared);
    System.out.println("Loaded "+doc.getUserName());
    
    //db1.lock(doc);
    rand.setSeed(System.currentTimeMillis());
    doc.setUserName("Doc"+did+" ("+(Math.abs(rand.nextInt())%1000)+")");
    String sName=doc.getUserName();
 
    
    bReached=true;
    while (!bStart) Thread.yield();
 
    System.out.println("Committing "+Thread.currentThread());
    db1.commit();
    db1.close();
    System.out.println("Successfully updated "+sName);
   }
   catch (Exception e)
   {
    System.out.println("Exception Thread "+e.getMessage());
    //e.printStackTrace();
   }
   finally
   {
     try{db1.close();}catch(Exception e){}
   }
  }
  }
}
 
DOCTOR.JAVA
 
import java.util.*;
public class Doctor extends User{
 
 private Department dept  = null;
 
 private String userName = null;
 
 public String getUserName() {
  return userName;
 }
 
 public  void setUserName(String aUserName) {
  userName = aUserName;
 }
 
 
 
 public Department getDept() {
  return dept;
 }
 
 public void setDept(Department aDept) {
  if (dept!=aDept) dept = aDept;
 }
 
}
 
DEPARTMENT.JAVA
 
import java.util.*;
public class Department extends BaseJDO{
 
 private String deptName = null;
 private ArrayList doctors = new ArrayList();
 
 public String getDeptName() {
  return deptName;
 }
 
 public  void setDeptName(String aDeptName) {
  deptName = aDeptName;
 }
 

 public ArrayList getDoctors() {
  return doctors;
 }
 

 public void addDoctor(Doctor aDoctor) {
  if (aDoctor!=null && !doctors.contains(aDoctor))
  {
   doctors.add(aDoctor);
   aDoctor.setDept(this);
  }
 }
}
 
 
THIS IS THE OUTPUT
 
Loaded Doc5 (916)                                                              �
Loaded Doc4 (393)
Loaded Doc6 (601)
Loaded Doc 3
Loaded Doc 2
Committing Thread[Thread-2,5,main]
Committing Thread[Thread-1,5,main]
Committing Thread[Thread-4,5,main]
Committing Thread[Thread-5,5,main]
Committing Thread[Thread-3,5,main]
Exception Thread Nested error: org.exolab.castor.jdo.LockNotGrantedException: persist.deadlock :(org.exolab.castor.jdo.engine.TransactionContextImpl@9ce060) is having readlock on (Doctor/2/0 R/-) and is currently waiting for (Doctor/5/4 R/-) whose readlock is owned by (org.exolab.castor.jdo.engine.TransactionContextImpl@4ecfdd)
Exception Thread Nested error: org.exolab.castor.jdo.LockNotGrantedException: persist.deadlock :(org.exolab.castor.jdo.engine.TransactionContextImpl@9ce060) is having readlock on (Doctor/6/1 R/-) and is currently waiting for (Doctor/5/4 R/-) whose readlock is owned by (org.exolab.castor.jdo.engine.TransactionContextImpl@476128)
Exception Thread Nested error: org.exolab.castor.jdo.LockNotGrantedException: persist.deadlock :org.exolab.castor.jdo.engine.TransactionContextImpl@9ce060) is
having readlock on (Doctor/3/3 R/-) and is currently waiting for (Doctor/5/4 R/-) whose readlock is owned by (org.exolab.castor.jdo.engine.TransactionContextImpl@8bf072)
Exception Thread Nested error: org.exolab.castor.jdo.LockNotGrantedException: persist.deadlock :(org.exolab.castor.jdo.engine.TransactionContextImpl@9ce060) is
having readlock on (Doctor/4/2 R/-) and is currently waiting for (Doctor/5/4 R/-) whose readlock is owned by (org.exolab.castor.jdo.engine.TransactionContextImpl@3b8b49)
Successfully updated Doc5 (539)
We are in urgent requirement of a solution and the we are not in a position to set cache-type to none due to performance issues.
 
Arun Sudhakaran,
Softex Computer Consultants.
E-Mail : [EMAIL PROTECTED]

Reply via email to