[Zope] minimizing conflict errors

2005-11-20 Thread Dennis Allison
I have a DTML method which provides the primary navigation control in a 
portion of our system and so is very heavily used.  It is a primary source
of conflict errors and so is being rethought.

Zope 2.8.4, ZEO 3.4.2, ZODB 3.4.2, Python 2.4.2 or 2.3.5 
MySQL 4.0.20, MySQL-Python 1.2.0, MYSQLDA 2.0.9

ZODB 3.4.2 does not raise a conflict error on a read-read conflict, one of 
the reasons fr moving to Zope 2.8.4.

The structure of the naviagation method is simple enough. Everything is 
wrapped in a dtml-let which sets a number of parameters mostly by 
reading them from the SESSION (with an interface function) or plucking 
them from the relational database with a query.

In the scope of the let is dtml code which, when rendered, provides the 
various navigation links.  In various sections there are additional 
dtml-let blocks and additional queries to the relational database
and several dtml-in loops.

Looking at the code, I don't understand why I am seeing conflicts.
As I understand things, neither variables in the dtml-let space nor
the REQUEST/RESPONSE space are stored in the ZODB so modifications to 
them don't look like writes to the conflict mechanism.  Am I incorrect 
in my understanding?





___
Zope maillist  -  Zope@zope.org
http://mail.zope.org/mailman/listinfo/zope
**   No cross posts or HTML encoding!  **
(Related lists - 
 http://mail.zope.org/mailman/listinfo/zope-announce
 http://mail.zope.org/mailman/listinfo/zope-dev )


Re: [Zope] minimizing conflict errors

2005-11-20 Thread Tino Wildenhain
Am Sonntag, den 20.11.2005, 09:16 -0800 schrieb Dennis Allison:
 I have a DTML method which provides the primary navigation control in a 
 portion of our system and so is very heavily used.  It is a primary source
 of conflict errors and so is being rethought.
 
 Zope 2.8.4, ZEO 3.4.2, ZODB 3.4.2, Python 2.4.2 or 2.3.5 
 MySQL 4.0.20, MySQL-Python 1.2.0, MYSQLDA 2.0.9
 
 ZODB 3.4.2 does not raise a conflict error on a read-read conflict, one of 
 the reasons fr moving to Zope 2.8.4.
 
 The structure of the naviagation method is simple enough. Everything is 
 wrapped in a dtml-let which sets a number of parameters mostly by 
 reading them from the SESSION (with an interface function) or plucking 
 them from the relational database with a query.
 
 In the scope of the let is dtml code which, when rendered, provides the 
 various navigation links.  In various sections there are additional 
 dtml-let blocks and additional queries to the relational database
 and several dtml-in loops.
 
 Looking at the code, I don't understand why I am seeing conflicts.
 As I understand things, neither variables in the dtml-let space nor
 the REQUEST/RESPONSE space are stored in the ZODB so modifications to 
 them don't look like writes to the conflict mechanism.  Am I incorrect 
 in my understanding?

For what are you using the SESSION storage and for what do you
need the ZSQL method calls in your navigation?
What is in your ZODB?

Regards
Tino

___
Zope maillist  -  Zope@zope.org
http://mail.zope.org/mailman/listinfo/zope
**   No cross posts or HTML encoding!  **
(Related lists - 
 http://mail.zope.org/mailman/listinfo/zope-announce
 http://mail.zope.org/mailman/listinfo/zope-dev )


Re: [Zope] minimizing conflict errors

2005-11-20 Thread Chris McDonough


On Nov 20, 2005, at 12:16 PM, Dennis Allison wrote:

The structure of the naviagation method is simple enough.  
Everything is

wrapped in a dtml-let which sets a number of parameters mostly by
reading them from the SESSION (with an interface function) or plucking
them from the relational database with a query.

In the scope of the let is dtml code which, when rendered, provides  
the

various navigation links.  In various sections there are additional
dtml-let blocks and additional queries to the relational database
and several dtml-in loops.

Looking at the code, I don't understand why I am seeing conflicts.
As I understand things, neither variables in the dtml-let space nor
the REQUEST/RESPONSE space are stored in the ZODB so modifications to
them don't look like writes to the conflict mechanism.  Am I incorrect
in my understanding?


Yes, but that's understandable.  It's not exactly obvious.

The sessioning machinery is one of the few places in Zope where it's  
necessary for the code to do what's known as a write on read in the  
ZODB database.


Even if you're just reading from a session, looking up a session,  
or doing anything otherwise related to sessioning, it's possible for  
your code to generate a ZODB write.
This is why you get conflicts even if you're just reading; whenever  
you access the sessioning machinery, you are potentially (but not  
always) causing a ZODB write.  All writes can potentially cause a  
conflict error.


While this might sound fantastic, it's pretty much impossible to  
avoid when using ZODB as a sessioning backend.  The sessioning  
machinery has been tuned to generate as few conflicts as possible,  
and you can help it by doing your own timeout, resolution, and  
housekeeping tuning as has been suggested.  MVCC gets rid of read  
conflicts.  But it's not possible to completely avoid write conflicts  
under the current design.


Here's why.  The sessioning machinery is composed of three major data  
structures:


- an index of timeslice to bucket. A timeslice is an integer  
representing

  some range of time (the range of time is variable, depending on the
  resolution, but out of the box, it represents 20 seconds).
This mapping

  is an IOBTree.

- A bucket is a mapping from a browser id to session data  
object (aka

  transient object).  This mapping is an OOBTree.

- three increasers which mark the last timeslice in which  
something was done

  (called the garbage collector, called the finalizer, etc).

The point of sessioning is to provide a writable namespace assigned  
to a single user that expires after some period of inactivity by that  
user.  To this end, we need to keep track of when the last time the  
user accessed the session was.  This is the point of the index.


When a user accesses his session, we may need to move his session  
data object (identified by his browser id) from one bucket  
(representing an older timeslice) to another (representing a newer  
timeslice).  This needs to happen *even if your code doesn't write  
anything to his session*, because it represents a session access, and  
the session is defined by total inactivity (not just write  
inactivity).  Likewise, when a user runs code that requires access to  
a session, but that user does not yet have a session data object, a  
write may need to occur.  So seemingly innocuous accesses to session  
data can cause a write.  Consider, in a Python script:


req = context.REQUEST
REQUEST.SESSION

Looks pretty harmless and unlikely to cause a write.  However, that's  
not true.  If the bucket in which the user's session data object is  
found is not associated with the current timeslice, we need to move  
his data object to the bucket that *is* associated with the current  
timeslice, which is a write operation in order to make note of the  
fact that his session is now current.


Likewise with:

req = context.REQUEST
a = REQUEST.SESSION.get('foo')

Even though this appears to be only a read, the sessioning  
machinery itself may need to perform a write operation to move the  
user's data object to the current bucket.


Jacking up the resolution time increases the period of time  
represented by a single timeslice, so fewer total writes need to be  
performed to keep a session current.   Turning on external  
housekeeping doesn't prevent this normal movement of data objects  
between buckets, it just causes another process that cleans up  
stale data from happening during normal sessioning operations.


The sessioning machinery attempts to minimize conflicts.  The 2.8  
version of the temporarystorage does MVCC, which essentially  
eliminates read conflict errors.  The transience machinery includes  
significantly complicated logic to attempt to prevent conflict errors  
from occurring including code that attempts to prevent two threads  
from doing housekeeping at once as well as application level conflict  
resolution for simultaneous writes to the same session data object.