cayenne-user mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Paulo Andrade <p...@mega.ist.utl.pt>
Subject Re: Optimistic locking and concurrency within the same application
Date Sat, 30 Oct 2010 14:53:52 GMT
So I went ahead and did a bit of testing:

I created an app that would execute the following code (the model as an entity named "Counter"
with a "value" property marked for locking):

----------
	ObjectContext context = DataContext.createDataContext();
		
	Counter counter = context.newObject(Counter.class);
	counter.setValue(3);
		
	context.commitChanges();
		
	 // Create our two peer contexts
	ObjectContext context1 = DataContext.createDataContext();
	ObjectContext context2 = DataContext.createDataContext();
	    
	    // Instantiate object on context1
	    Counter counter1 = (Counter) context1.localObject(counter.getObjectId(), null);
	    LOG.debug("Context1 read counter with value "+ counter.getValue());
	    
	    // Instantiate object on context2
	    Counter counter2 = (Counter) context2.localObject(counter.getObjectId(), null);
	    LOG.debug("Context2 read counter with value "+ counter.getValue());
	    
	    // Context1 makes changes
	    counter1.setValue(counter1.getValue() + 1);
	    LOG.debug("Counter1 incremented to value " +counter1.getValue());
	    
	    // Context2 makes changes
	    counter2.setValue(counter2.getValue() + 1);
	    LOG.debug("Counter2 incremented to value " +counter2.getValue());
	    
	    // Context1 commit
	    context1.commitChanges();
	    
	    // Context2 commit
	    context2.commitChanges();
----------

Now what I would like is for context2.commitChanges() to fail, since this commit would write
the value 4 which is already on the database thus failling to increment the value.

If we take a look at the query logging you can see that the last commit has a 4 on the where
clause (which I feel should be a 3).

----------
[INFO] access.QueryLogger +++ Connecting: SUCCESS.
[INFO] access.QueryLogger --- transaction started.
[INFO] access.QueryLogger Detected and installed adapter: org.apache.cayenne.dba.postgres.PostgresAdapter
[INFO] access.QueryLogger SELECT nextval('pk_counter')
[INFO] access.QueryLogger --- will run 1 query.
[INFO] access.QueryLogger INSERT INTO Counter (id, value) VALUES (?, ?)
[INFO] access.QueryLogger [batch bind: 1->id:240, 2->value:3]
[INFO] access.QueryLogger === updated 1 row.
[INFO] access.QueryLogger +++ transaction committed.
[DEBUG] pages.Index Context1 read counter with value 3
[DEBUG] pages.Index Context2 read counter with value 3
[INFO] access.QueryLogger --- will run 1 query.
[INFO] access.QueryLogger --- transaction started.
[INFO] access.QueryLogger SELECT t0.value, t0.id FROM Counter t0 WHERE t0.id = ? [bind: 1->id:240]
- prepared in 10 ms.
[INFO] access.QueryLogger === returned 1 row. - took 18 ms.
[INFO] access.QueryLogger +++ transaction committed.
[DEBUG] pages.Index Counter1 incremented to value 4
[INFO] access.QueryLogger --- will run 1 query.
[INFO] access.QueryLogger --- transaction started.
[INFO] access.QueryLogger SELECT t0.value, t0.id FROM Counter t0 WHERE t0.id = ? [bind: 1->id:240]
[INFO] access.QueryLogger === returned 1 row. - took 1 ms.
[INFO] access.QueryLogger +++ transaction committed.
[DEBUG] pages.Index Counter2 incremented to value 4
[INFO] access.QueryLogger --- will run 1 query.
[INFO] access.QueryLogger --- transaction started.
[INFO] access.QueryLogger UPDATE Counter SET value = ? WHERE id = ?
[INFO] access.QueryLogger [batch bind: 1->value:4, 2->id:240]
[INFO] access.QueryLogger === updated 1 row.
[INFO] access.QueryLogger +++ transaction committed.
[INFO] access.QueryLogger --- will run 1 query.
[INFO] access.QueryLogger --- transaction started.
[INFO] access.QueryLogger UPDATE Counter SET value = ? WHERE id = ?
[INFO] access.QueryLogger [batch bind: 1->value:4, 2->id:240]
[INFO] access.QueryLogger === updated 1 row.
[INFO] access.QueryLogger +++ transaction committed.
----------

Even more surprisingly is that even with Level 1 - No Cache Sharing the same behavior applies.

So do you guys feel this is normal behavior? How can I code this in a way that the second
commitChanges would fail?

Best regards,
Paulo Andrade

On Oct 28, 2010, at 11:50 AM, Paulo Andrade wrote:

> Hello,
> 
> I'm new to Cayenne, and coming form EOF I'm find everything very easy to understand.
> 
> Everything is so similar that I'm wondering that what I consider to be flaws in EOF also
happen in Cayenne.
> 
> For a long description of the problem here's a blog post:
> 
> http://terminalapp.net/dr-optimistic-locking/
> 
> The summary is this:
> 
> EOF stores snapshots in the ObjectStoreCoordinator which are shared by EOEditingContexts
(ObjectContexts in Cayenne), these snapshots are updated on fetches (queries in Cayenne) and
saves (commits in Cayenne).
> 
> So take an Cayenne application with a Level 2 cache (Local VM Caching), two ObjectStores
(oc1 and oc2) and a Counter object with and intValue attribute marked for optimistic locking.
> 
> What should happen in the following code:
> 
> -------------
> Counter oc1Counter, oc2Counter; // assume both exist and refer to the same entity in
the DB, each in their own context
> 
> int i = oc1Counter.intValue();
> int j = oc2Counter.intValue(); 
> // both i and j have a value of 3
> 
> // now we increment oc1's counter
> oc1Counter.setIntValue( i+1 ); // sets to 4
> 
> oc1.commitChanges(); // saves oc1Counter with a value of 4 to disk and updates the snapshot
> 
> // now increment oc2's counter
> oc1Counter.setIntValue( j+1 ); // sets to 4 again
> 
> oc2.commitChanges(); // **
> ——————
> 
> ** now what do you think should happen here?
> In EOF the save succeeds and the previous change is overwritten without me knowing about
it. Will Cayenne do the same?
> 
> Best regards,
> Paulo Andrade
> 


Mime
  • Unnamed multipart/alternative (inline, None, 0 bytes)
View raw message