[Zope-Checkins] SVN: Zope/trunk/ Merge from 2.7 branch:

Chris McDonough chrism at plope.com
Sun May 23 00:17:24 EDT 2004


Log message for revision 24889:
Merge from 2.7 branch:

- Add "instance-local" "period" to TransientObjectContainer.  This allows
  users to dial a knob which may (or may not) reduce the number of conflicts
  that happen during heavy sessioning usage by reducing the frequency at
  which buckets potentially expire at the cost of expiration time
  accuracy.  Previously, this setting was hardcoded to 20 (seconds) at
  module scope.

- Add 'session-resolution-seconds' to zope.conf.in/zopeschema.xml to
  control instance-local period for /temp_folder/session_data.

- Update TOC UI, interface, and help files to deal with instance-local
  period.

- Update OFS/Application to deal with instance-local period for default
  /temp/session_data TOC.

- Use __setstate__ for TOC upgrade instead of a dedicated _upgrade method
  (it was too hard to figure out where to call _upgrade from and when to
  call it).

- Perform a few formatting changes that should make it easier to merge the 2.7
  branch with the HEAD going forward.  I beseech those who make formatting
  changes to a branch or the HEAD make them to the other at that time
  as well, especially with the SVN/CVS split it's very painful to do merging
  when there are non-substantive differences between HEAD/maint.  When I was
  a child, I never thought I would need to use the word "beseech", however, it
  has indeed happened.




-=-
Modified: Zope/trunk/lib/python/OFS/Application.py
===================================================================
--- Zope/trunk/lib/python/OFS/Application.py	2004-05-23 02:41:28 UTC (rev 24888)
+++ Zope/trunk/lib/python/OFS/Application.py	2004-05-23 04:17:23 UTC (rev 24889)
@@ -1,4 +1,4 @@
-############################################################################
+##############################################################################
 #
 # Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
 #
@@ -375,9 +375,15 @@
             delnotify = getattr(config, 'session_delete_notify_script_path',
                                 None)
             default_limit = 1000
+            default_period_secs = 20
+            default_timeout_mins = 20
+            
             limit = (getattr(config, 'maximum_number_of_session_objects', None)
                      or default_limit)
-            timeout_spec = getattr(config, 'session_timeout_minutes', None)
+            timeout_spec = getattr(config, 'session_timeout_minutes',
+                                   default_timeout_mins)
+            period_spec = getattr(config, 'session_resolution_seconds',
+                                  default_period_secs)
 
             if addnotify and app.unrestrictedTraverse(addnotify, None) is None:
                 LOG('Zope Default Object Creation', WARNING,
@@ -395,7 +401,8 @@
                 'session_data', 'Session Data Container',
                 addNotification = addnotify,
                 delNotification = delnotify,
-                limit=limit)
+                limit=limit,
+                period_secs=period_spec)
 
             if timeout_spec is not None:
                 toc = TransientObjectContainer('session_data',
@@ -403,7 +410,8 @@
                                                timeout_mins = timeout_spec,
                                                addNotification = addnotify,
                                                delNotification = delnotify,
-                                               limit=limit)
+                                               limit=limit,
+                                               period_secs = period_spec)
 
             tf._setObject('session_data', toc)
             tf_reserved = getattr(tf, '_reserved_names', ())

Modified: Zope/trunk/lib/python/Products/Transience/Transience.py
===================================================================
--- Zope/trunk/lib/python/Products/Transience/Transience.py	2004-05-23 02:41:28 UTC (rev 24888)
+++ Zope/trunk/lib/python/Products/Transience/Transience.py	2004-05-23 04:17:23 UTC (rev 24889)
@@ -16,8 +16,6 @@
 $Id$
 """
 
-__version__='$Revision: 1.32.12.5 $'[11:-2]
-
 import math
 import time
 import random
@@ -54,7 +52,6 @@
 ACCESS_TRANSIENTS_PERM = 'Access Transient Objects'
 MANAGE_CONTAINER_PERM = 'Manage Transient Object Container'
 
-PERIOD = 20 # signifies "resolution" of transience machinery in seconds
 SPARE_BUCKETS = 15 # minimum number of buckets to keep "spare"
 BUCKET_CLASS = OOBTree # constructor for buckets
 STRICT = os.environ.get('Z_TOC_STRICT', '')
@@ -80,10 +77,11 @@
     'dtml/addTransientObjectContainer', globals())
 
 def constructTransientObjectContainer(self, id, title='', timeout_mins=20,
-    addNotification=None, delNotification=None, limit=0, REQUEST=None):
+    addNotification=None, delNotification=None, limit=0, period_secs=20,
+    REQUEST=None):
     """ """
     ob = TransientObjectContainer(id, title, timeout_mins,
-        addNotification, delNotification, limit=limit)
+        addNotification, delNotification, limit=limit, period_secs=period_secs)
     self._setObject(id, ob)
     if REQUEST is not None:
         return self.manage_main(self, REQUEST, update_menu=1)
@@ -133,10 +131,10 @@
     security.setDefaultAccess('deny')
 
     def __init__(self, id, title='', timeout_mins=20, addNotification=None,
-                 delNotification=None, limit=0):
+                 delNotification=None, limit=0, period_secs=20):
         self.id = id
         self.title=title
-        self._setTimeout(timeout_mins)
+        self._setTimeout(timeout_mins, period_secs)
         self._setLimit(limit)
         self.setDelNotificationTarget(delNotification)
         self.setAddNotificationTarget(addNotification)
@@ -144,13 +142,43 @@
 
     # helpers
 
-    def _setTimeout(self, timeout_mins):
+    def _setTimeout(self, timeout_mins, period_secs):
         if type(timeout_mins) is not type(1):
             raise TypeError, (escape(`timeout_mins`), "Must be integer")
-        self._timeout_secs = t_secs = timeout_mins * 60
-        # timeout_slices == fewest number of timeslices that's >= t_secs
-        self._timeout_slices=int(math.ceil(float(t_secs)/PERIOD))
 
+        if type(period_secs) is not type(1):
+            raise TypeError, (escape(`period_secs`), "Must be integer")
+
+        timeout_secs = timeout_mins * 60
+
+        # special-case 0-minute timeout value by ignoring period
+        if timeout_secs != 0:
+
+            if period_secs == 0:
+                raise ValueError('resolution cannot be 0')
+
+            if period_secs > timeout_secs:
+                raise ValueError(
+                    'resolution cannot be greater than timeout '
+                    'minutes * 60 ( %s > %s )' % (period_secs, timeout_secs))
+
+            # we need the timeout to be evenly divisible by the period
+            if timeout_secs % period_secs != 0:
+                raise ValueError(
+                    'timeout seconds (%s) must be evenly divisible '
+                    'by resolution (%s)' % (timeout_secs, period_secs)
+                    )
+
+        # our timeout secs is the number of seconds that an item should
+        # remain unexpired
+        self._timeout_secs = timeout_secs
+
+        # our _period is the number of seconds that constitutes a timeslice
+        self._period = period_secs
+
+        # timeout_slices == fewest number of timeslices that's >= timeout_secs
+        self._timeout_slices=int(math.ceil(float(timeout_secs)/period_secs))
+
     def _setLimit(self, limit):
         if type(limit) is not type(1):
             raise TypeError, (escape(`limit`), "Must be integer")
@@ -173,7 +201,10 @@
         # populate _data with some number of buckets, each of which
         # is "current" for its timeslice key
         if self._timeout_slices:
-            new_slices = getTimeslices(getCurrentTimeslice(), SPARE_BUCKETS*2)
+            new_slices = getTimeslices(
+                getCurrentTimeslice(self._period),
+                SPARE_BUCKETS*2,
+                self._period)
             for i in new_slices:
                 self._data[i] = BUCKET_CLASS()
             # create an Increaser for max timeslice
@@ -190,32 +221,26 @@
 
     def _getCurrentSlices(self, now):
         if self._timeout_slices:
-            begin = now - (PERIOD * self._timeout_slices)
+            begin = now - (self._period * self._timeout_slices)
             # add add one to _timeout_slices below to account for the fact that
             # a call to this method may happen any time within the current
             # timeslice; calling it in the beginning of the timeslice can lead
-            # to sessions becoming invalid a maximum of <PERIOD> seconds
+            # to sessions becoming invalid a maximum of self._period seconds
             # earlier than the requested timeout value. Adding one here can
             # lead to sessions becoming invalid *later* than the timeout value
-            # (also by a max of <PERIOD>), but in the common sessioning case,
-            # that seems preferable.
+            # (also by a max of self._period), but in the common sessioning
+            # case, that seems preferable.
             num_slices = self._timeout_slices + 1
         else:
             return [0] # sentinel for timeout value 0 (don't expire)
         DEBUG and TLOG('_getCurrentSlices, now = %s ' % now)
         DEBUG and TLOG('_getCurrentSlices, begin = %s' % begin)
         DEBUG and TLOG('_getCurrentSlices, num_slices = %s' % num_slices)
-        result = getTimeslices(begin, num_slices)
+        result = getTimeslices(begin, num_slices, self._period)
         DEBUG and TLOG('_getCurrentSlices, result = %s' % result)
         return result
 
     def _move_item(self, k, current_ts, default=None):
-        if not getattr(self, '_max_timeslice', None):
-            # in-place upgrade for old instances; this would usually be
-            # "evil" but sessions are all about write-on-read anyway,
-            # so it really doesn't matter.
-            self._upgrade()
-
         if self._timeout_slices:
 
             if self._roll(current_ts, 'replentish'):
@@ -289,12 +314,8 @@
         return item
 
     def _all(self):
-        if not getattr(self, '_max_timeslice', None):
-            # in-place upgrade for old instances
-            self._upgrade()
-
         if self._timeout_slices:
-            current_ts = getCurrentTimeslice()
+            current_ts = getCurrentTimeslice(self._period)
         else:
             current_ts = 0
 
@@ -352,7 +373,7 @@
 
     def __getitem__(self, k):
         if self._timeout_slices:
-            current_ts = getCurrentTimeslice()
+            current_ts = getCurrentTimeslice(self._period)
         else:
             current_ts = 0
         item = self._move_item(k, current_ts, _marker)
@@ -366,7 +387,7 @@
     def __setitem__(self, k, v):
         DEBUG and TLOG('__setitem__: called with key %s, value %s' % (k,v))
         if self._timeout_slices:
-            current_ts = getCurrentTimeslice()
+            current_ts = getCurrentTimeslice(self._period)
         else:
             current_ts = 0
         item = self._move_item(k, current_ts, _marker)
@@ -398,7 +419,7 @@
     def __delitem__(self, k):
         DEBUG and TLOG('__delitem__ called with key %s' % k)
         if self._timeout_slices:
-            current_ts = getCurrentTimeslice()
+            current_ts = getCurrentTimeslice(self._period)
         else:
             current_ts = 0
         item = self._move_item(k, current_ts)
@@ -418,7 +439,7 @@
     def get(self, k, default=None):
         DEBUG and TLOG('get: called with key %s, default %s' % (k, default))
         if self._timeout_slices:
-            current_ts = getCurrentTimeslice()
+            current_ts = getCurrentTimeslice(self._period)
         else:
             current_ts = 0
         item = self._move_item(k, current_ts, default)
@@ -431,7 +452,7 @@
     security.declareProtected(ACCESS_TRANSIENTS_PERM, 'has_key')
     def has_key(self, k):
         if self._timeout_slices:
-            current_ts = getCurrentTimeslice()
+            current_ts = getCurrentTimeslice(self._period)
         else:
             current_ts = 0
         DEBUG and TLOG('has_key: calling _move_item with %s' % str(k))
@@ -455,8 +476,8 @@
         is to minimize the chance that two threads will attempt to
         do housekeeping at the same time (causing conflicts).
         """
-        low = now/PERIOD
-        high = self._max_timeslice()/PERIOD
+        low = now/self._period
+        high = self._max_timeslice()/self._period
         if high <= low:
             # we really need to win this roll because we have no
             # spare buckets (and no valid values to provide to randrange), so
@@ -481,7 +502,7 @@
             return # do nothing if no timeout
         
         max_ts = self._max_timeslice()
-        available_spares = (max_ts-now) / PERIOD
+        available_spares = (max_ts-now) / self._period
         DEBUG and TLOG('_replentish: now = %s' % now)
         DEBUG and TLOG('_replentish: max_ts = %s' % max_ts)
         DEBUG and TLOG('_replentish: available_spares = %s'
@@ -490,19 +511,19 @@
         if available_spares < SPARE_BUCKETS:
             if max_ts < now:
                 replentish_start = now
-                replentish_end = now + (PERIOD * SPARE_BUCKETS)
+                replentish_end = now + (self._period * SPARE_BUCKETS)
 
             else:
-                replentish_start = max_ts + PERIOD
-                replentish_end = max_ts + (PERIOD * SPARE_BUCKETS)
+                replentish_start = max_ts + self._period
+                replentish_end = max_ts + (self._period * SPARE_BUCKETS)
 
             DEBUG and TLOG('_replentish: replentish_start = %s' %
                            replentish_start)
             DEBUG and TLOG('_replentish: replentish_end = %s'
                            % replentish_end)
             # n is the number of buckets to create
-            n = (replentish_end - replentish_start) / PERIOD
-            new_buckets = getTimeslices(replentish_start, n)
+            n = (replentish_end - replentish_start) / self._period
+            new_buckets = getTimeslices(replentish_start, n, self._period)
             new_buckets.reverse()
             STRICT and _assert(new_buckets)
             DEBUG and TLOG('_replentish: adding %s new buckets' % n)
@@ -523,8 +544,8 @@
             return # dont do gc if there is no timeout
 
         if now is None:
-            now = getCurrentTimeslice() # for unit tests
-        max_ts = now  - (PERIOD * (self._timeout_slices + 1))
+            now = getCurrentTimeslice(self._period) # for unit tests
+        max_ts = now  - (self._period * (self._timeout_slices + 1))
 
         to_notify = []
 
@@ -633,16 +654,26 @@
     # TransientItemContainer methods
 
     security.declareProtected(MANAGE_CONTAINER_PERM, 'setTimeoutMinutes')
-    def setTimeoutMinutes(self, timeout_mins):
-        """ """
-        if timeout_mins != self.getTimeoutMinutes():
-            self._setTimeout(timeout_mins)
+    def setTimeoutMinutes(self, timeout_mins, period_secs=20):
+        """ The period_secs parameter is defaulted to preserve backwards API
+        compatibility.  In older versions of this code, period was
+        hardcoded to 20. """
+        timeout_secs = timeout_mins * 60
+        
+        if (timeout_mins != self.getTimeoutMinutes()
+            or period_secs != self.getPeriodSeconds()):
+            # do nothing unless something has changed
+            self._setTimeout(timeout_mins, period_secs)
             self._reset()
 
     def getTimeoutMinutes(self):
         """ """
         return self._timeout_secs / 60
 
+    def getPeriodSeconds(self):
+        """ """
+        return self._period
+
     security.declareProtected(MGMT_SCREEN_PERM, 'getSubobjectLimit')
     def getSubobjectLimit(self):
         """ """
@@ -681,11 +712,11 @@
         'manage_changeTransientObjectContainer')
     def manage_changeTransientObjectContainer(
         self, title='', timeout_mins=20, addNotification=None,
-        delNotification=None, limit=0, REQUEST=None
+        delNotification=None, limit=0, period_secs=20, REQUEST=None
         ):
         """ Change an existing transient object container. """
         self.title = title
-        self.setTimeoutMinutes(timeout_mins)
+        self.setTimeoutMinutes(timeout_mins, period_secs)
         self.setSubobjectLimit(limit)
         if not addNotification:
             addNotification = None
@@ -699,49 +730,61 @@
                 self, REQUEST, manage_tabs_message='Changes saved.'
                 )
         
-    def _upgrade(self):
-        # inplace upgrade for versions of Transience in Zope versions less
+    def __setstate__(self, state):
+        # upgrade versions of Transience in Zope versions less
         # than 2.7.1, which used a different transience mechanism.  Note:
         # this will not work for upgrading versions older than 2.6.0,
         # all of which used a very different transience implementation
-        if not getattr(self, '_max_timeslice', None):
-            new_slices = getTimeslices(getCurrentTimeslice(), SPARE_BUCKETS*2)
+        # can't make __len__ an instance variable in new-style classes
+
+        # f/w compat: 2.8 cannot use __len__ as an instance variable
+        if not state.has_key('_length'):
+            length = state.get('__len__', Length())
+            self._length = self.getLen = length
+
+        # TOCs prior to 2.7.1 took their period from a global
+        if not state.has_key('_period'):
+            self._period = 20 # this was the default for all prior releases
+
+        # TOCs prior to 2.7.1 used a different set of data structures
+        # for efficiently keeping tabs on the maximum slice
+        if not state.has_key('_max_timeslice'):
+            new_slices = getTimeslices(
+                getCurrentTimeslice(self._period),
+                SPARE_BUCKETS*2,
+                self._period)
             for i in new_slices:
                 if not self._data.has_key(i):
                     self._data[i] = BUCKET_CLASS()
             # create an Increaser for max timeslice
             self._max_timeslice = Increaser(max(new_slices))
 
-        # can't make __len__ an instance variable in new-style classes
-        if not getattr(self, '_length', None):
-            length = self.__dict__.get('__len__', Length())
-            self._length = self.getLen = length
-
-        # we should probably delete older attributes such as
+        # we should probably delete older attributes from state such as
         # '_last_timeslice', '_deindex_next',and '__len__' here but we leave
         # them in order to allow people to switch between 2.6.0->2.7.0 and
         # 2.7.1+ as necessary (although that has not been tested)
+        self.__dict__.update(state)
     
-def getCurrentTimeslice():
+def getCurrentTimeslice(period):
     """
     Return an integer representing the 'current' timeslice.
     The current timeslice is guaranteed to be the same integer
-    within a 'slice' of time based on a divisor of 'period'.
-    'period' is the number of seconds in a slice.
+    within a 'slice' of time based on a divisor of 'self._period'.
+    'self._period' is the number of seconds in a slice.
     """
     now = time.time()
-    low = int(math.floor(now)) - PERIOD + 1
+    low = int(math.floor(now)) - period + 1
     high = int(math.ceil(now)) + 1
     for x in range(low, high):
-        if x % PERIOD == 0:
+        if x % period == 0:
             return x
 
-def getTimeslices(begin, n):
+def getTimeslices(begin, n, period):
     """ Get a list of future timeslice integers of 'n' size in descending
     order """
     l = []
     for x in range(n):
-        l.insert(0, begin + (x * PERIOD))
+        l.insert(0, begin + (x * period))
     return l
 
 def _assert(case):

Modified: Zope/trunk/lib/python/Products/Transience/dtml/addTransientObjectContainer.dtml
===================================================================
--- Zope/trunk/lib/python/Products/Transience/dtml/addTransientObjectContainer.dtml	2004-05-23 02:41:28 UTC (rev 24888)
+++ Zope/trunk/lib/python/Products/Transience/dtml/addTransientObjectContainer.dtml	2004-05-23 04:17:23 UTC (rev 24889)
@@ -62,7 +62,7 @@
 <TR>
   <TD ALIGN="LEFT" VALIGN="TOP">
     <div class="form-label">
-      Data object timeout in minutes
+      Data object timeout (in minutes)
    </div>
     <div class="form-help">
       ("0" means no expiration)
@@ -76,6 +76,20 @@
 <TR>
   <TD ALIGN="LEFT" VALIGN="TOP">
     <div class="form-label">
+      Timeout resolution (in seconds)
+   </div>
+    <div class="form-help">
+      (accept the default if you're not sure)
+    </div>
+  </TD>
+  <TD ALIGN="LEFT" VALIGN="TOP">
+    <INPUT TYPE="TEXT" NAME="period_secs:int" SIZE="10" value="20">
+  </TD>
+</TR>
+
+<TR>
+  <TD ALIGN="LEFT" VALIGN="TOP">
+    <div class="form-label">
       Maximum number of subobjects
    </div>
     <div class="form-help">

Modified: Zope/trunk/lib/python/Products/Transience/dtml/manageTransientObjectContainer.dtml
===================================================================
--- Zope/trunk/lib/python/Products/Transience/dtml/manageTransientObjectContainer.dtml	2004-05-23 02:41:28 UTC (rev 24888)
+++ Zope/trunk/lib/python/Products/Transience/dtml/manageTransientObjectContainer.dtml	2004-05-23 04:17:23 UTC (rev 24889)
@@ -5,7 +5,6 @@
            help_topic='Transience-change.stx'
 	   )">
 
-
 <form action="manage_changeTransientObjectContainer" method="post">
 
 <p class="form-help">
@@ -13,7 +12,9 @@
 Transient data will persist, but only for a user-specified period of time
 (the "data object timeout") after which it will be flushed.
 </p>
+
 <dtml-call nudge><!-- turn the buckets if necessary -->
+
 <p class="form-label">
 <font color="green">
 <dtml-let l=getLen>
@@ -43,7 +44,7 @@
 <tr>
   <td align="left" valign="top">
     <div class="form-label">
-     Data object timeout value in minutes
+     Data object timeout value (in minutes)
     </div>
     <div class="form-help">
       ("0" means no expiration)
@@ -58,6 +59,26 @@
 <tr>
   <td align="left" valign="top">
     <div class="form-label">
+     Timeout resolution (in seconds)
+    </div>
+    <div class="form-help">
+      Defines what the "resolution" of item timeout is. Setting this higher
+      allows the transience machinery to do fewer "writes" at the expense of
+      causing items to time out later than the "Data object timeout value" by
+      a factor of (at most) this many seconds.  This number must divide evenly
+      into the number of timeout seconds ("Data object timeout value" * 60)
+      and cannot be set higher than the timeout value in seconds.
+    </div>
+  </td>
+  <td align="left" valign="top">
+    <input type="text" name="period_secs:int" size=10
+     value=&dtml-getPeriodSeconds;>
+  </td>
+</tr>
+
+<tr>
+  <td align="left" valign="top">
+    <div class="form-label">
      Maximum number of subobjects
     </div>
     <div class="form-help">
@@ -100,7 +121,23 @@
   </td>
 </tr>
 
+<dtml-let l=getLen>
+<dtml-if l>
 <tr>
+<td colspan=2>
+<br/>
+<p class="form-label">
+<font color="red">WARNING!</font>
+All data objects existing in this transient object container
+will be deleted when the data object timeout or expiration resolution
+is changed.
+</p>
+</tr>
+</td>
+</dtml-if>
+</dtml-let>
+
+<tr>
   <td></td>
   <td>
   <input class="form-element" type="submit" name="submit" value=" Change ">
@@ -109,12 +146,5 @@
 </table>
 </form>
 
-<p class="form-label">
-<font color="red">WARNING!</font>
-The data objects existing in this transient object container
-will be deleted when the data object timeout is changed.
-</p>
 
-
-
 <dtml-var manage_page_footer>

Modified: Zope/trunk/lib/python/Products/Transience/help/Transience-add.stx
===================================================================
--- Zope/trunk/lib/python/Products/Transience/help/Transience-add.stx	2004-05-23 02:41:28 UTC (rev 24888)
+++ Zope/trunk/lib/python/Products/Transience/help/Transience-add.stx	2004-05-23 04:17:23 UTC (rev 24889)
@@ -23,13 +23,23 @@
 
        The title of the object.
 
-     - **Data object timeout in minutes**
+     - **Data object timeout (in minutes)**
 
        The minimum number of minutes that objects in the container will
        persist for.  Objects in the container are passively deleted, so
        they may not be deleted exactly after this number of minutes elapses.
        A setting of "0" indicates that objects should not expire.
 
+     - **Timeout resolution (in seconds)**
+
+       Defines what the "resolution" of item timeout is. Setting this
+       higher allows the transience machinery to do fewer "writes" at
+       the expense of causing items to time out later than the "Data
+       object timeout value" by a factor of (at most) this many
+       seconds. This number must divide evenly into the number of
+       timeout seconds ("Data object timeout value" * 60) and cannot
+       be set higher than the timeout value in seconds.
+       
      - **Maximum number of subobjects **
 
        The maximum number of subobjects that this container may

Modified: Zope/trunk/lib/python/Products/Transience/help/Transience-change.stx
===================================================================
--- Zope/trunk/lib/python/Products/Transience/help/Transience-change.stx	2004-05-23 02:41:28 UTC (rev 24888)
+++ Zope/trunk/lib/python/Products/Transience/help/Transience-change.stx	2004-05-23 04:17:23 UTC (rev 24889)
@@ -23,7 +23,7 @@
 
        The title of the object.
 
-     - **Data object timeout in minutes**
+     - **Data object timeout (in minutes)**
 
        The minimum number of minutes that objects in the container will
        persist for.  Objects in the container are passively deleted, so
@@ -34,6 +34,16 @@
 
        If the timeout value is "0", objects will not time out.
 
+     - **Timeout resolution (in seconds)**
+
+       Defines what the "resolution" of item timeout is. Setting this
+       higher allows the transience machinery to do fewer "writes" at
+       the expense of causing items to time out later than the "Data
+       object timeout value" by a factor of (at most) this many
+       seconds. This number must divide evenly into the number of
+       timeout seconds ("Data object timeout value" * 60) and cannot
+       be set higher than the timeout value in seconds.
+
      - **Maximum number of subobjects **
 
        The maximum number of subobjects that this container may
@@ -92,11 +102,6 @@
                  from zLOG import LOG
                  LOG(100, 'test', 'id: %s' % item.getId())
 
-  Environment Variables
-
-    You can control some transient object settings with environment
-    variables. See 'doc/ENVIORNMENT.txt' for more informatin.
-
   See Also
 
      - "Transience API":TransienceInterfaces.py

Modified: Zope/trunk/lib/python/Products/Transience/help/TransienceInterfaces.py
===================================================================
--- Zope/trunk/lib/python/Products/Transience/help/TransienceInterfaces.py	2004-05-23 02:41:28 UTC (rev 24888)
+++ Zope/trunk/lib/python/Products/Transience/help/TransienceInterfaces.py	2004-05-23 04:17:23 UTC (rev 24889)
@@ -80,10 +80,12 @@
         Permission -- 'Create Transient Objects'
         """
 
-    def setTimeoutMinutes(self, timeout_mins):
+    def setTimeoutMinutes(self, timeout_mins, period=20):
         """
         Set the number of minutes of inactivity allowable for subobjects
-        before they expire.
+        before they expire (timeout_mins) as well as the 'timeout resolution'
+        in seconds (period).  'timeout_mins' * 60 must be evenly divisible
+        by the period.  Period must be less than 'timeout_mins' * 60.
 
         Permission -- 'Manage Transient Object Container'
         """
@@ -96,6 +98,13 @@
         Permission -- 'View management screens'
         """
 
+    def getPeriodSeconds(self):
+        """
+        Return the 'timeout resolution' in seconds.
+
+        Permission -- 'View management screens'
+        """
+
     def getAddNotificationTarget(self):
         """
         Returns the current 'after add' function, or None.

Modified: Zope/trunk/lib/python/Products/Transience/tests/testTransientObjectContainer.py
===================================================================
--- Zope/trunk/lib/python/Products/Transience/tests/testTransientObjectContainer.py	2004-05-23 02:41:28 UTC (rev 24888)
+++ Zope/trunk/lib/python/Products/Transience/tests/testTransientObjectContainer.py	2004-05-23 04:17:23 UTC (rev 24889)
@@ -34,7 +34,9 @@
                                           
         self.errmargin = .20
         self.timeout = 120
-        self.t = TransientObjectContainer('sdc', timeout_mins=self.timeout/60)
+        self.period = 20
+        self.t = TransientObjectContainer('sdc', timeout_mins=self.timeout/60,
+                                          period_secs=self.period)
 
     def tearDown(self):
         self.t = None
@@ -267,7 +269,7 @@
         self.assertEqual(len(self.t.keys()), 0)
 
         # 2 minutes
-        self.t._setTimeout(self.timeout/60*2)
+        self.t._setTimeout(self.timeout/60*2, self.period)
         self.t._reset()
         for x in range(10, 110):
             self.t[x] = x
@@ -279,7 +281,7 @@
         self.assertEqual(len(self.t.keys()), 0)
 
         # 3 minutes
-        self.t._setTimeout(self.timeout/60*3)
+        self.t._setTimeout(self.timeout/60*3, self.period)
         self.t._reset()
         for x in range(10, 110):
             self.t[x] = x
@@ -318,7 +320,8 @@
 
     def testLen(self):
         # This test must not time out else it will fail.
-        self.t._setTimeout(self.timeout)  # make timeout extremely unlikely
+        # make timeout extremely unlikely by setting it very high
+        self.t._setTimeout(self.timeout, self.period)
         added = {}
         r = range(10, 1010)
         for x in r:
@@ -340,8 +343,9 @@
 
     def testGetTimeoutMinutesWorks(self):
         self.assertEqual(self.t.getTimeoutMinutes(), self.timeout / 60)
-        self.t._setTimeout(10)
+        self.t._setTimeout(10, 30)
         self.assertEqual(self.t.getTimeoutMinutes(), 10)
+        self.assertEqual(self.t.getPeriodSeconds(), 30)
 
     def test_new(self):
         t = self.t.new('foobieblech')
@@ -369,7 +373,7 @@
         self.assertRaises(MaxTransientObjectsExceeded, self._maxOut)
 
     def testZeroTimeoutMeansPersistForever(self):
-        self.t._setTimeout(0)
+        self.t._setTimeout(0, self.period)
         self.t._reset()
         for x in range(10, 110):
             self.t[x] = x

Modified: Zope/trunk/lib/python/Zope/Startup/zopeschema.xml
===================================================================
--- Zope/trunk/lib/python/Zope/Startup/zopeschema.xml	2004-05-23 02:41:28 UTC (rev 24888)
+++ Zope/trunk/lib/python/Zope/Startup/zopeschema.xml	2004-05-23 04:17:23 UTC (rev 24889)
@@ -627,6 +627,16 @@
      <metadefault>20</metadefault>
   </key>
 
+  <key name="session-resolution-seconds" datatype="integer"
+       default="20">
+     <description>
+     An integer value representing the number of seconds to be used as the
+     "timeout resolution" of the '/temp_folder/session_data' transient
+     object container in Zope's object database.
+     </description>
+     <metadefault>20</metadefault>
+  </key>
+
   <key name="suppress-all-access-rules" datatype="boolean"
        default="off" handler="suppress_all_access_rules">
      <description>

Modified: Zope/trunk/skel/etc/zope.conf.in
===================================================================
--- Zope/trunk/skel/etc/zope.conf.in	2004-05-23 02:41:28 UTC (rev 24888)
+++ Zope/trunk/skel/etc/zope.conf.in	2004-05-23 04:17:23 UTC (rev 24889)
@@ -548,6 +548,20 @@
 #    session-timeout-minutes 30
 
 
+# Directive: session-resolution-seconds
+#
+# Description:
+#    An integer value representing the number of seconds to be used as the
+#    "timeout resolution" of the '/temp_folder/session_data' transient
+#    object container.
+#
+# Default: 20
+#
+# Example:
+#
+#    session-resolution-seconds 60
+
+
 # Directive: suppress-all-access-rules
 #
 # Description:




More information about the Zope-Checkins mailing list