[Cosmo-dev] Interesting idea to avoid EIM conflicts

Phillip J. Eby pje at telecommunity.com
Tue Feb 13 16:07:50 PST 2007


At 02:21 PM 2/13/2007 -0800, Brian Moseley wrote:
>On 2/13/07, Morgen Sagen <morgen at osafoundation.org> wrote:
>
>>Alternatively, whenever Chandler syncs it could always set the
>>lastModifiedBy to "me" just before sending out an item.
>
>this is what i was expecting to happen without thinking about it in detail.
>
>>But
>>lastModifiedBy should never participate in a conflict -- it's more
>>metadata than data.
>
>right, so isn't the solution as simple as not having lastModifiedBy be
>considered when checking to see if an item has changed?

That's kind of like asking if we can ignore the namespace URIs when 
comparing two XML files.  :)  While in principle it might be possible, it 
sort of goes against the point of the information model in question.

Chandler conflict resolution is done purely in terms of opaque EIM data; it 
doesn't "know" the semantics of the fields it's comparing.  Here is the 
code that merges changes and computes conflicts for a single item (with 
some debug prints and other extraneous matters deleted):


     def merge(self, rsInternal, inboundDiff=eim.RecordSet(),
         send=True, receive=True, filter=None):

         if filter is None:
             filter = lambda rs: rs
         else:
             filter = filter.sync_filter

         pending = self.pending
         rsExternal = self.agreed + pending + inboundDiff
         internalDiff = filter(rsInternal - self.agreed)
         externalDiff = rsExternal - self.agreed

         ncd = internalDiff | filter(externalDiff)
         self.agreed += (internalDiff | externalDiff)

         dSend = clean_diff(rsExternal, ncd)
         dApply = clean_diff(rsInternal, ncd)

         if send:
             rsExternal += dSend

         # The following line determines the conflicts... don't blink!
         self.pending = rsExternal - self.agreed

         return dSend, dApply, self.pending

As you can see, there is nothing in this code that even looks at individual 
fields.  It simply performs abstract set arithmetic operations against 
opaque record types.  It is effectively equivalent to an XML "diff" program 
that compares two files based on the XML content model, not on the basis of 
text or on the basis of a particular XML schema.  The conflicting change 
set is determined by simply diffing the server (or peer) state with a new 
"agreed" state, arrived at by only applying non-conflicting changes.

This code doesn't know *anything* about the data it operates on, except 
that it's in EIM, and there is no reasonable way to add such 
knowledge.  Knowledge of the semantics is the job of the Translator 
classes, which import or export EIM records.

This approach allows us to share this conflict handling code (not to 
mention the EIMML read/write code) for any and all parcels, now and in the 
future.  The tradeoff is that you must model anything you want to share, 
using EIM in a way that reflects your intended semantics, given the nature 
of EIM conflict resolution.  (As opposed to having a kind of "it's EIM, 
except when it's not" model.)

It's certainly *possible* that we could add something to the above code 
that hooks back into the translator layer, but it's sort of like trying to 
patch your compiler so that 2+2 sometimes equals 5.  The above code depends 
on a lot of invariants about the relationship between "plus", "minus", 
"union", and similar recordset operators, and hasn't been designed or 
reviewed for what kind of things could happen if translators were allowed 
to change these invariants on a whim -- just like XML processing tools are 
generally not designed to operate on XML that is not well-formed.



More information about the cosmo-dev mailing list