-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 2 posts ] 
Author Message
 Post subject: Timestamp optimistic-lock problem
PostPosted: Sun Oct 09, 2005 1:59 pm 
Beginner
Beginner

Joined: Wed Jul 14, 2004 11:51 am
Posts: 43
Hibernate version:3.0
Mysql 4.1.13

Hi,

I'm having problem with my timestamp optimistic-lock automatic versioning.

My problem is that (for some weird reason) I don't always get a StaleObjectStateException when concurrent modifications are made. Here is a simple test case that illustrates this:

Code:
    protected void setUp() throws Exception {
        super.setUp();
        referenceProduct = ProductTestCase.createMinimalProduct();
        String referenceId = referenceProduct.getId();
        System.err.println("RP: " + referenceProduct.getTimeStamp());
       
        //Fetch 2 instances of a product
        p1 = ProductPersistenceFacade.getInstance().fetchProduct(referenceId, localizationContext);       
        p2 = ProductPersistenceFacade.getInstance().fetchProduct(referenceId, localizationContext);
       
        //Populate 2 different items
        i1 = ItemTestCase.populateMinimalItem();
        i1.setItemName("FIRST TRANSACTION");
        i2 = ItemTestCase.populateMinimalItem();
        i2.setItemName("SECOND TRANSACTION");
    }

    protected void tearDown() throws Exception {
        super.tearDown();

        Product deletedProduct = ProductPersistenceFacade.getInstance().fetchProduct(referenceProduct.getId(), localizationContext);
        ProductPersistenceFacade.getInstance().deleteProduct(deletedProduct);
    }


    public void testConcurrentModifications() throws NoLocalizablePropertyException, InstanceNotLocalizedException, SystemException, ConcurrencyException {
       p1.addItems(i1);
        p1 = ProductPersistenceFacade.getInstance().saveProduct(p1);
       assertEquals(1, p1.getItems().size());       
        System.err.println("P1: " + p1.getTimeStamp());
       
        p2.addItems(i2);
        boolean exception = false;
        try {
            System.err.println("P2 before: " + p2.getTimeStamp());
            p2 = ProductPersistenceFacade.getInstance().saveProduct(p2);
            System.err.println("P2 after: " + p2.getTimeStamp());
        } catch(ConcurrencyException ce) {
            //OK
            exception = true;
        }
        assertTrue(exception);
    }


The saveProduct method looks like this:

Code:
   /**
    * This method saves a Product in the datasource.
    *
    * @ejb.interface-method view-type="remote"
    * @ejb.transaction type="NotSupported"
    */
    public Product saveProduct(Product instance)
     throws org.mdarad.framework.exception.ConcurrencyException, org.mdarad.framework.exception.SystemException {
        if ("".equals(instance.getId())) instance.setId(null);

        try {

            // Initialize the hibernate session
            Session session = currentSession();
            // Start an hibernate transaction
            Transaction tx = session.beginTransaction();

            try {
                // Schedule the value object for persistence
                session.saveOrUpdate(instance);

                // Commit hibernate transaction
                tx.commit();
            } catch (StaleObjectStateException sose) {
                tx.rollback();
                throw new org.mdarad.framework.exception.ConcurrencyException("The object has been modified by another user between the time the fetch and the save were completed");
            } catch (HibernateException he) {
                tx.rollback();
                throw new org.mdarad.framework.exception.SystemException(he);
            } finally {
                // Close the hibernate session
                closeSession();
            }

        } catch (HibernateException he) {
            throw new org.mdarad.framework.exception.SystemException(he);
        }


        return instance;
    }


Now when I launch this test normally, test test fails on the last assertTrue and I get the following output:

Quote:
RP: 2005-10-09 13:44:23.728
P1: 2005-10-09 13:44:23.999
P2 before: 2005-10-09 13:44:23.0
P2 after: 2005-10-09 13:44:24.099


The test should pass for there should be a ConcurrencyException thrown.

Now this is where it becomes weird...if I do the same test but insert a Thread.sleep(1000) before the p1 = ProductPersistenceFacade.getInstance().saveProduct(p1);, the test will pass. It is as though if there needed to be at least a certain amount of time between the 2 saves before it is considered a concurrent exception.

Is this a known bug or a workaround of some sort?

Thanks,

François[/code]


Top
 Profile  
 
 Post subject:
PostPosted: Mon Oct 10, 2005 11:49 am 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 3:00 pm
Posts: 1816
Location: Austin, TX
I assume you are using jvm-based timestamps here? This simply uses System.currentTimeMillis() which is known to be not the most accurate. I would guess that the sleep() just allows the jvm to increment the System.currentTimeMillis().

This is the main problem with using timestamp based values for optimistic locking in highly concurrent systems. You need to ensure that a given row is not updated more oftent that the underlying system recognizes changes in the "current time".


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 2 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.