[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