[ZODB-Dev] ZODB4 project plan

Christian Reis kiko@async.com.br
Tue, 14 Jan 2003 23:36:12 -0200


--JwB53PgKC5A7+0Ej
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline

On Tue, Dec 03, 2002 at 11:07:32AM -0500, Shane Hathaway wrote:
> I just checked in code to ZODB (on a branch) that enables you to change 
> the policy.  Check out shane-local-transactions-branch.  From the 
> checkin message:
> 
> The Connection class now has a method setLocalTransaction().
> (Suggestions for a better method name accepted.)  Once your app calls
> this, your app is expected to use connection.getTransaction() or
> ob._p_jar.getTransaction() instead of the get_transaction() function
> to commit or abort the transaction.  Doing this enables your
> application to have multiple writable connections open in a single
> thread, which is especially usefully in GUI applications.

Shane, we've been integrating these changes into our product this week
and (I repeat!) it's been working really cool. Needless to say, the ZODB
rocks; I would never have imagined it would have been simple to change
its transaction semantics. 

I've found this change is also a great way of explaining to people
(without resorting to complications wrt threading) how the ZODB conflict
management works, and I'm attaching a small testcase for others to look
at and understand how ConflictErrors actually occur.

One thing in the script I *don't* understand, however, is a
ReadConflictError I get. The situation is best exemplified in the
testcase, but it seems to be an issue with how BTrees work. I add an
item to the root on connection 1, and commit() the change. If I try to
access this item on the root for connection 2 (root2['item']) it raises
the exception. Is this expected?

I suppose it is a way of catching conflicts early on -- since we already
have a key called `item' on connection1's object, it wouldn't make sense
to allow us to create one on connection2 and then have commit() bail out
on us. Am I right?

However, the funny part is that if I *access* the object in connection2
prior to committing it in connection1, all is well and safe. Is this
because the transaction machinery understands that we know about the
item in both connections before the first transaction goes in?

Take care,
--
Christian Reis, Senior Engineer, Async Open Source, Brazil.
http://async.com.br/~kiko/ | [+55 16] 261 2331 | NMFL

--JwB53PgKC5A7+0Ej
Content-Type: text/plain; charset=iso-8859-1
Content-Disposition: attachment; filename="zodb-localtransaction-test.py"
Content-Transfer-Encoding: quoted-printable
X-MIME-Autoconverted: from 8bit to quoted-printable by anthem.async.com.br id h0F1aMX26342

import os
import sys

import ZODB
from ZEO.ClientStorage import ClientStorage
from ZODB.FileStorage import FileStorage
from ZODB.DB import DB
from Persistence import Persistent

print 'Using ZODB from', ZODB.__path__

FS =3D 'foo.fs'
storage =3D FileStorage(FS)
db =3D DB(storage)
conn1 =3D db.open()
conn2 =3D db.open()

# Enables Shane's tr=E9s-cool extension
conn1.setLocalTransaction()
conn2.setLocalTransaction()

# One root for each connection
r1 =3D conn1.root()
r2 =3D conn2.root()

if r1.has_key('item'):
    del r1['item']
    conn1.getTransaction().commit()
    conn2.sync()
   =20
print r1.get('item')

# Comment the next line out for:
#
# Traceback (most recent call last):
#   File "testing.py", line 41, in ?
#     assert r2.get('item') =3D=3D None
#   File "/usr/local/lib/python2.1/site-packages/ZODB/Connection.py", lin=
e
# 531, in setstate
#     raise ReadConflictError(object=3Dobject)
# ZODB.POSException.ReadConflictError: database read conflict error (oid
# 0000000000000000, class Persistence.PersistentMapping)
print r2.get('item')

# Start off by assigning an item=20
# and testing it on both connections
r1['item'] =3D 1
conn1.getTransaction().commit()
assert r1.get('item') =3D=3D 1
assert r2.get('item') =3D=3D None

# After sync, the second connection
# sees the changes performed in conn1
conn2.sync()
assert r2.get('item') =3D=3D 1

r2['item'] =3D 2
conn2.getTransaction().commit()

# Same check - conn2 is updated, conn1 is not.
assert r1.get('item') =3D=3D 1
assert r2.get('item') =3D=3D 2

# Upon sync, all is nice
conn1.sync()
assert r1.get('item') =3D=3D 2
assert r2.get('item') =3D=3D 2

# Finally, try to do two updates at a time.
r1['item'] =3D 10
r2['item'] =3D 1
conn1.getTransaction().commit()
try:
    conn2.getTransaction().commit()
except ZODB.POSException.ConflictError:
    print "Conflict caught for connection 2!"

--JwB53PgKC5A7+0Ej--