[ZODB-Dev] avoiding conflicts.
Terry Kerr
terry@bizarsoftware.com.au
Tue, 11 Jun 2002 16:40:24 +1000
Hi,
Sorry for the large post.
I have a problem with conflicts with my zope app. I have a method that is
called from the web, and does quite a few things, updating attributes on global
parent objects as well as attributes on the object the method is defined on, and
the method also posts off information to other systems via a HTTP.
The problem I have is that this method can take up to 10 seconds to complete,
mainly due to the HTTP communication with the external systems, and during high
load periods, this method may be called by several different users every second,
thus, write conflicts emerge as the method updates the attributes on the global
parent objects. Zope's way of attempting to resolve the conflict is to re-run
the method. This works fine, and the conlfict is evenutally resolved even if
after several goes. The problem however is that because the method is run
several times, the information is posted to the external systems as many times
which in my case is very very bad!
I have done some things to help avoid conflicts like moving the chunks of code
that change attributes on parent objects at the beginning of the method and
followed the code with a get_transaction().commit(). This seems to help reduce
the number of conflict errors since the troublesome attrs are updated quickly
and commited. However, this is still not an ideal situation.
I have looked through ZODB developer docs, and have seen posts related to
conflict resolution like defining _p_resolveConflict(). However, this idea is
not really relivant to to my application because the attributes that I am
updating are not simple counters, they are large sometimes complex lits and
mappings.
One solution I guess would be to redesign the way I do this and have my troubles
attrs moved across to their own objects which has a _p_resolveConflict() to
resolve the conflicts. Although, I see this as a difficult task.
Running Zope in single thread mode is not an option due to scalibility ;-(
The solution I have been experiementing with is a simple thread lock on the
method to prevent more than one thread (application user) from running the
method concurrently. I have been told that this is a no-no, but I don't see why
I shouldn't do it. The key to doing this is syncroinising the database
connection immediately after the thread lock has been acquired, which I have not
seen any mension of in postings related to this issue. The code I am using is:
lock = thread.allocate_lock()
class myClass:
def myMethod(self):
lock.acquire()
self._p_jar.sync()
update some global attrs here()
many slow bits of code here()
get_transaction().commit()
lock.release()
return data
From my initial experiements, the code works fine and I get no conflicts. The
only problem I can see with this solution is that it won't be safe across ZEO
clients if I ever run the application in a ZEO environment. I guess that is a
fairly major limitation!
If all ZEO clients were running on the same machine to take advantage of a
multiple CPU machine, then I guess I could use a file lock in the file system
rather than a thread lock, but this wouldn't help if the ZEO clients were
running on different machines.
Any suggestions or comments would be much appreciated.
Please respond to terry@bizarsoftware.com.au since I am not on any lists.
terry
--
Terry Kerr (terry@bizarsoftware.com.au)
Chief Technical Officer
Bizar Software Pty Ltd (www.bizarsoftware.com.au)
+61 3 9530 9182