[ZODB-Dev] How can reading trigger a ConflictError?

gward@mems-exchange.org gward@mems-exchange.org
Thu, 26 Apr 2001 11:59:12 -0400


Hi --

I'm trying to understand how *reading* a ZEO database can trigger a
ConflictError.  Specifically, I have one process reading and one process
writing the database.  (Actually, both open it the same way, so in principle
both are writers.  But in practice, only one of the processes actually
writes to the database or commits changes.)  (Side note: is it possible to
have a read-only connection to a ZEO database?  If I were using FileStorage,
I suppose I would set read_only=1 when calling the FileStorage constructor,
but I don't see a similar option in ClientStorage's constructor.)

So here's the scenario:

  * process A starts and opens a connection to the ZEO server

  * process A changes an object.  (Specifically, it appends an item to a
    PersistentList buried fairly deep in the database.  For concreteness,
    the PersistentList that I'm appending to is
    user_db['gward'].history.events.) (PersistentList is AMK's analog to
    PersistentMapping; 'user_db' is one of our root objects.)

  * process B starts and opens a connection to the ZEO server

  * process A commits its change

  * process B attempts to access the object to which the changed
    PersistentList belongs, user_db['gward'].history (by "access"
    I just mean load enough of the object to call its repr() method,
    because I'm doing this in an interactive interpreter session).

  * process B blows up with a ConflictError, and the OID in the
    exception is the OID of the changed PersistentList,
    user_db['gward'].history.events

Getting the ConflictError isn't too sensitive to the order stated here; it
looks like as long as process B attempts to access user_db['gward'].history
after process A has committed its change, I get it.

Also, it's easy to recover: after seeing the ConflictError in process B,
calling get_transaction().abort() magically syncs process B's picture of the
database with the server and I can happily access the object that was
causing problems.

So why does it work like this?  I thought the whole point of transactions
was to isolate concurrent processes from each other; shouldn't process B
happily see the "old" picture of the database until it does something to
resync (eg. commit or abort a transaction), at which point it sees the "new"
picture as committed by A?  Or is this a naive understanding of
transactions?  Or do I just not understand ZODB/ZEO's transaction model?
(Is it documented anywhere yet?)

Thanks --

        Greg