[Persistence-sig] Observing object changes

Shane Hathaway shane@zope.com
Tue, 13 Aug 2002 22:19:58 -0400 (EDT)


On Wed, 14 Aug 2002, holger krekel wrote:

> Lamy Jean-Baptiste wrote:
> > Here is the API i use:
> >
> > addevent   (obj, func) -- Add    the event FUNC (=an observer) to OBJ
> > removeevent(obj, func) -- Remove the event FUNC from OBJ
> >
> > I have already written a pure Python implementation of that (feel free to
> > ask for the code if you're interested); it works well but it is really an
> > ugly hack ! The principle is to change the class of the observed object to
> > a new class that define a __setattr__ method.
>
> This is not enough.  Not every modifiction of an object goes through
> '__setattr__', e.g.
>
>     myobj.somelist.append(42)
>
> would modify 'myobj' but you wouldn't notice. So everything an object
> passes out (like its attribute) would need to have a thin wrapper which
> holds a reference to "its" object and notifies upon change.  This is
> likely to be a recursive process.

This is a classic problem in ZODB applications.  The normal ZODB solution
looks like this:

  myobj.somelist.append(42)
  myobj._p_changed = 1

an alternative:

  myobj.somelist.append(42)
  myobj.somelist = myobj.somelist

Most ZODB programmers can accept this; it's not too bad.  But here's the
thing that concerns me: both of the above well-known idioms are still not
quite correct.  If, for whatever reason, an exception occurs between
appending to the list and notifying the persistence framework, and the
transaction aborts, the list may be left in an inconsistent state.  The
list won't be rolled back if its containing object wasn't changed in some
other way.  If 42 is an important number, this might be a serious problem.
;-)

If you just reverse the two statements, the lurking bug goes away.  But it
still bothers me because the programmer has to be so careful to get it
just right.  I hope this SIG will look into ways of solving this (for all
kinds of persistence, not just ZODB).

Shane