Re: Correctly implementing optimistic concurrency management in Cayenne for web-based computing

From: Eric Lazarus (ericllazaru..ahoo.com)
Date: Thu May 04 2006 - 16:35:20 EDT

  • Next message: Lachlan Deck: "EmbeddedDriver performance help"

    Bryan, I am not sure I know the answer to your
    question. I need to explore the answer to that.

    Ok, as of today’s small changes, we now clear the
    changed elsewhere set at the very start of the page
    cycle, which is before any updates are made to the
    application domain objects. Today’s testing seemed to
    suggest that we still have some unexpected concurrency
    conflicts but the window is much smaller so we believe
    we are getting fewer of them.

    We determine if we want to commit or roll back just
    before rendering the HTML, after the main changes have
    been made to the object model.

    One concern that we have is that the rendering process
    itself can be responsible for changes to the object
    model, for example, lazy initiation of an instance
    variable will sometimes happen as a result of
    rendering. What would happen to those changes? Will
    they hang around in a session and cause problems
    somehow? What problems?

    Seems to me that we should consider committing after
    render also, not just before. Does that sound right?
    What happens if we hit an update conflict at that
    point? I will be hard to display the message in the
    browser if the rendering is complete so I am not sure
    what to do to inform the user that there was a problem
    at that point.

    Eric

    --- Bryan Lewis <brya..aine.rr.com> wrote:

    > I'm missing something. Why not use Cayenne's
    > built-in optimistic
    > locking support, with a timestamp or other
    > versioning attribute in each
    > important entity?
    >
    >
    > Eric Lazarus wrote:
    >
    > >Anyone want to write an article on: Correctly
    > >implementing optimistic concurrency management in
    > >Cayenne for web-based computing? We will soon have
    > a
    > >commercial application doing this! I would think it
    > >would be something that lots of web-developers
    > should
    > >want to do if they are building a web-based
    > >application with a domain object model.
    > >
    > >Anyway, we still have a few small problems with it.
    > >We obtain a set of ObjectIds for all objects
    > changed
    > >in our session, and another set of ObjectIds for
    > all
    > >objects changed in other sessions. When these sets
    > >have a non-empty intersection we have an update
    > >conflict
    > >and rollback our changes, and issue a message
    > >containing the offending objects.
    > >
    > >The Ids changed in our session come from
    > >DataContext.modifiedObjects()
    > >The Ids changed in other sessions come from a set
    > >maintained by our
    > >DataContextDelegate, which adds the ids of objects
    > >passed to its
    > >"shouldMergeChanges() method. This set is cleared
    > each
    > >time we commit
    > >or rollback our changes.
    > >
    > >Perhaps we should shorten the period of time when
    > >conflicts can occur by clearing the
    > changed-elsewhere
    > >set at the beginning of the page submit cycle
    > rather
    > >than at the end of the pervious submit. How can we
    > >tell if that will give us the correct semantics? We
    > >want to make sure that we are not overwriting new
    > data
    > >with
    > >old data. Would we be messing that up if we clear
    > he
    > >changed-elsewhere set just before
    > >we begin updating objects in the object model?
    > >
    > >What is most troubling for us right now is that we
    > are
    > >hitting conflicts on objects that we are not
    > >intentionally modifying, and are having trouble
    > >tracking down the code that is causing these
    > objects
    > >to wind up in the "modified"
    > >collections.
    > >
    > >All of our persistent objects descend from the
    > class
    > >PAXPersistentObject, which extends
    > CayenneDataObject.
    > >I used this to try to track down the
    > >updates by overriding writeProperty(),
    > >addToManyTarget(), removeToManyTarget(),
    > >setToOneDependentTarget(), and
    > setPersistenceState()
    > >produce a log of ObjectIds
    > >and their modified fields (or PersistenceState).
    > >
    > >Am I logging in on the correct methods? What else
    > >might be called that would cause an object to be
    > put
    > >into modifiedObjects and/or passed to the data
    > context
    > >delegate?
    > >
    > >The problem is that objects are winding up causing
    > >update conflicts without showing up in this log.
    > >Strangely we never see setPersistanceState set the
    > >state to 'Modified' - the only values we see are
    > >Hollow (5) and Committed (3).
    > >
    > >Is there some better way to find which fields and
    > >records in our database are
    > >being modified ? It would be great if we could find
    > a
    > >spot to put a breakpoint
    > >where a stack trace could lead to the code that is
    > >doing the modification, and
    > >where the objectid and property name are availible
    > so
    > >that we can filter out the many fields that we are
    > not
    > >conserned with.
    > >
    > >To summarize we have 2 questions.
    > >(1) What is the best way to find places in our code
    > >that
    > > are causing un-intended updates?
    > >(2) Are we constructing the update conflict set
    > >correctly?
    > >
    > >Below you will find the relevent portions of our
    > code.
    > >Can you help ?
    > >
    > >If notice we are missusing Cayenne in some way,
    > please
    > >let us know. We love it and want to use it right.
    > >
    > >Dan and Eric
    > >
    > >
    > >/* This is called after all updates are complete,
    > just
    > >before the page
    > > is rendered. It will rollback when an update
    > >conflict is found, and
    > > commit otherwise.
    >
    > > */
    > >
    > >public void preRenderNotification(EditorContext
    > >aContext) {
    > > try {
    > > Set aSet= getUpdateConflictSet(aContext) ;
    > > if(aSet.size()>0)
    > rollbackChanges(aContext,aSet) ;
    > > else getDataContext(aContext).commitChanges() ;
    > > } catch(Exception e) {
    > > getAppModel(aContext).addMessage(e, "Error In
    > >Database Update") ;
    > > e.printStackTrace() ;
    > >getDataContext(aContext).rollbackChanges() ;
    > > }
    > >}
    > >
    > >/* this calculates the conflict set as the
    > >intersection of
    > > the 'changed here' and the 'changed elsewhere'
    > sets
    > > */
    > >
    > >public static Set
    > getUpdateConflictSet(EditorContext
    > >aContext) {
    > > Collection
    >
    >modified_Objects=getDataContext(aContext).modifiedObjects();
    > > Set changedHereIDs =
    > >getIdSetFromObjectCollection(modified_Objects);
    > > Set changedOtherIDs=
    > >getAChangeElseWhereSet(aContext);
    > > changedOtherIDs.retainAll(changedHereIDs);
    > > return changedOtherIDs;
    > >}
    > >
    > >/* This just gets the object ids from a collection
    > of
    > >data objects */
    > >
    > >public static Set
    > >getIdSetFromObjectCollection(Collection
    > >modifiedObjects) {
    > > Set aSet=new HashSet();
    > > Object anArray[]=modifiedObjects.toArray();
    > > for(int i=0;i<anArray.length;i++) {
    > > DataObject aDataObject=(DataObject)anArray[i] ;
    > > aSet.add(aDataObject.getObjectId()) ;
    > > }
    > > return aSet;
    > >}
    > >
    > >/* This does the rollback, and outputs the error
    > >message */
    > >
    > >public void rollbackChanges(EditorContext aContext,
    > >Set aChangeElseWhereSet)
    > >{
    > > System.out.println("%%%%%%%%%%%%% Can not
    > Commit
    > >
    > >%%%%%%%%%%%%%%%%");
    > > getDataContext(aContext).rollbackChanges();
    > > AppModel
    > >anAppModel=(AppModel)aContext.getModelValue() ;
    > > Iterator i=aChangeElseWhereSet.iterator();
    > > while(i.hasNext())
    > >anAppModel.addMessage("Conflicting object
    > >id="+i.next()) ;
    > > aChangeElseWhereSet.clear();
    > >}
    > >
    > >/* this gets the 'ChangeElsewhere' set that is
    > shared
    >
    === message truncated ===

    __________________________________________________
    Do You Yahoo!?
    Tired of spam? Yahoo! Mail has the best spam protection around
    http://mail.yahoo.com



    This archive was generated by hypermail 2.0.0 : Thu May 04 2006 - 16:35:45 EDT