CGIs and file exclusion

Tim Peters tim.peters at gmail.com
Sat Nov 6 01:47:04 EST 2004


[Michele Simionato]
> ...
> Maybe Diez B. Roggisch is right and a real database server is the simplest
> solution. However my first attempt with ZEO didn't worked either:

That's OK, nobody's first attempt with any database server works <0.6 wink>.

> $ cat zeoclient.py
> import ZODB, ZEO
> from ZEO.ClientStorage import ClientStorage
>
> def openzeo(host, port):
>    db = ZODB.DB(ClientStorage((host, port)))
>    conn = db.open()
>    return db, conn, conn.root()
>
> def store():
>    # I have a ZEO instance running on port 9999
>    print "Opening the db ..."
>    db, conn, root = openzeo("localhost", 9999)
>    print "Storing something ..."
>    root["somekey"] = "somedata"

It's important to note that store() always changes at least the root object.

>    get_transaction().commit()
>    print "Closing the db ..."
>    conn.close(); db.close()
>
> if __name__ == "__main__":
>    store()
> 
> $ echo Makefile
> default:
>        python zeoclient.py&
>        python zeoclient.py
> 
> $ make
> python zeoclient.py&
> python zeoclient.py
> Opening the db ...
> Opening the db ...
>
> Storing something ...
> Storing something ...
> Closing the db ...
> Traceback (most recent call last):
>  File "zeoclient.py", line 20, in ?
>    store()
>  File "zeoclient.py", line 15, in store
>    get_transaction().commit()
>  File "/usr/share/partecs/zope/lib/python/ZODB/Transaction.py", line
> 247, in commit
> ~/pt/python/zopexplore $
> ~/pt/python/zopexplore $     vote(self)
>  File "/usr/share/partecs/zope/lib/python/ZODB/Connection.py", line
> 699, in tpc_vote
>    s = vote(transaction)
>  File "/opt/zope/lib/python/ZEO/ClientStorage.py", line 841, in
> tpc_vote
>    return self._check_serials()
>  File "/opt/zope/lib/python/ZEO/ClientStorage.py", line 825, in
> _check_serials
>    raise s
> ZODB.POSException.ConflictError: database conflict error (oid
> 0000000000000000, serial was 035900d31b7fedaa, now 035900d2f6cd8799)
>
> (it works with a single process instead).

Yes, that's predictable too <wink>.
 
> Maybe I misunderstood how ZEO is intended to be used, as usual it is
> difficult to found the relevant documentation :-( Maybe I should ask
> on another list ...

zodb-dev at zope.org is the best place for ZODB/ZEO questions independent
of Zope use.  Note that you must subscribe to a zope.org list in order
to post to it (that's a Draconian but very effective anti-spam
policy).

In the case above, ZEO isn't actually relevant.  You'd see the same
thing if you had a single process with two threads, each using a
"direct" ZODB connection to the same database.

ZODB doesn't do object-level locking.  It relies on "optimistic
concurrency control" (a googlable phrase) instead, which is especially
appropriate for high-read low-write applications like most Zope
deployments.

In effect, that means it won't stop you from trying to do something
insane, but does stop you from *completing* it.  What you got above is
a "write conflict error", and is normal behavior.  What happens:

- Process A loads revision n of some particular object O.
- Process B loads the same revision n of O.
- Process A modifies O, creating revision n+1.
- Process A commits its change to O.  Revsion n+1 is then current.
- Process B modifies O, creating revision n+2.
- Process B *tries* to commit its change to O.

The implementation of commit() investigates, and effectively says
"Hmm.  Process B started with revision n of O, but revision n+1 is
currently committed.  That means B didn't *start* with the currently
committed revision of O, so B has no idea what might have happened in
revision n+1 -- B may be trying to commit an insane change as a
result.  Can't let that happen, so I'll raise ConflictError".  That
line of argument makes a lot more sense if more than one object is
involved, but maybe it's enough to hint at the possible problems.

Anyway, since your store() method always picks on the root object,
you're going to get ConflictErrors frequently.  It's bad application
design for a ZODB/ZEO app to have a "hot spot" like that.

In real life, all ZEO apps, and all multithreaded ZODB apps, always do
their work inside try/except structures.  When a conflict error
occurs, the except clause catches it, and generally tries the
transaction again.  In your code above, that isn't going to work well,
because there's a single object that's modified by every transaction
-- it will be rare for a commit() attempt not to give up with a
conflict error.

Perhaps paradoxically, it can be easier to get a real ZEO app working
well than one's first overly simple attempts -- ZODB effectively
*wants* you to scribble all over the database.



More information about the Python-list mailing list