[Zope-Checkins] CVS: Zope/lib/python/Products/Transience/tests - testTimeoutRelated.py:1.8 testTransientObjectContainer.py:1.10

Chris McDonough chrism@zope.com
Thu, 20 Jun 2002 21:51:44 -0400


Update of /cvs-repository/Zope/lib/python/Products/Transience/tests
In directory cvs.zope.org:/tmp/cvs-serv20260/tests

Modified Files:
	testTimeoutRelated.py testTransientObjectContainer.py 
Log Message:
New TransientObjectContainer implementation.

Changes:

 - More stable under high usage, especially in the face of
   situations under which there are many ZODB conflict
   errors. The previous implementation had stability problems
   when many conflict errors were encountered; especially
   conflicts that were generated as a result of a simultaneous
   change to a subobject of the TOC (such as in the case of a Zope
   application which makes heavy use of both frames and
   sessions).
   
 - More conflict-resistant.  Instead of ignoring the likelihood
   that multiple threads will attempt to perform the same actions
   simultaneously in methods of the TOC (which often causes
   conflicts), the new implementation attempts to avoid conflicts
   by employing a chance-based housekeeping model.  In this model,
   one thread is "elected" by chance to do the kinds of tasks that
   cause the most conflicts.

 - Now uses a "timeslice" based model instead of a "ring" based
   model.  This also helps cut down on conflicts and makes
   the code slighly less obfuscated (not much, though! ;-)

 - Quite a few more comments in the code.

 - Changes to the sessioning stresstest (which exposed the
   bug that made me reimplement the TOC in the first place).

 - Updates to unit tests.

 - A "HowTransienceWorks.stx" document which attempts to
   explain how the code works.  It's not stellar, but
   it's a start.

 - Changes to the TransientObject class that the TOC
   hands out (typically as a "session data object"), in order
   to make invalidation less Rube-Goldberg-ish.

The structure of the TOC object has changed enough that in order to
maintain b/w compatibility, an in-place upgrade of "old" instances
is implied by running them with this code.   "Upgraded" instances
are not backwards-incompatible, however, so folks can hopefully
move back and forth between Zope versions without much hassle.



=== Zope/lib/python/Products/Transience/tests/testTimeoutRelated.py 1.7 => 1.8 ===
 
+if __name__=='__main__':
+    sys.path.insert(0, '..')
+    sys.path.insert(0, '../../..')
+
 import ZODB # in order to get Persistence.Persistent working
 from Testing import makerequest
 import Acquisition
@@ -74,7 +78,7 @@
         sdo = self.app.sm.new_or_existing('TempObject')
         la1 = sdo.getLastAccessed()
         fauxtime.sleep(WRITEGRANULARITY + 1)
-        sdo = self.app.sm['TempObject']
+        sdo = self.app.sm.get('TempObject')
         assert sdo.getLastAccessed() > la1, (sdo.getLastAccessed(), la1)
 
 class TestNotifications(TestBase):
@@ -90,13 +94,15 @@
         self.app.sm.setDelNotificationTarget(delNotificationTarget)
         sdo = self.app.sm.new_or_existing('TempObject')
         timeout = self.timeout * 60
-        fauxtime.sleep(timeout + (timeout * .33))
-        try: sdo1 = self.app.sm['TempObject']
-        except KeyError: pass
+        fauxtime.sleep(timeout + (timeout * .75))
+        sdo1 = self.app.sm.get('TempObject')
+        for x in range(1, 100):
+            # force the sdm to do housekeeping
+            self.app.sm._getCurrentBucket()
         now = fauxtime.time()
         k = sdo.get('endtime')
-        assert type(k) == type(now)
-        assert k <= now
+        assert (type(k) == type(now)), type(k)
+        assert k <= now, (k, now)
 
 def addNotificationTarget(item, context):
     item['starttime'] = fauxtime.time()


=== Zope/lib/python/Products/Transience/tests/testTransientObjectContainer.py 1.9 => 1.10 ===
 import sys, os, time, random, unittest
 
+if __name__ == "__main__":
+    sys.path.insert(0, '../../..')
+
 import ZODB
 from Products.Transience.Transience import TransientObjectContainer,\
      MaxTransientObjectsExceeded
@@ -23,7 +26,7 @@
 import time as oldtime
 import fauxtime
 
-class TestTransientObjectContainer(TestCase):
+class TestBase(TestCase):
     def setUp(self):
         Products.Transience.Transience.time = fauxtime
         Products.Transience.TransientObject.time = fauxtime
@@ -36,6 +39,7 @@
         Products.Transience.Transience.time = oldtime
         Products.Transience.TransientObject.time = oldtime
 
+class TestTransientObjectContainer(TestBase):
     def testGetItemFails(self):
         self.assertRaises(KeyError, self._getitemfail)
 
@@ -280,6 +284,36 @@
             except KeyError:
                 if self.t.has_key(x): assert 1==2,"failed to delete %s" % x
 
+    def testChangingTimeoutWorks(self):
+        # 1 minute
+        for x in range(10, 110):
+            self.t[x] = x
+        fauxtime.sleep(self.timeout * (self.errmargin+1))
+        assert len(self.t.keys()) == 0, len(self.t.keys())
+
+        # 2 minutes
+        self.t._setTimeout(self.timeout/60*2)
+        self.t._reset()
+        for x in range(10, 110):
+            self.t[x] = x
+        fauxtime.sleep(self.timeout)
+        assert len(self.t.keys()) == 100, len(self.t.keys())
+        fauxtime.sleep(self.timeout * (self.errmargin+1))
+        assert len(self.t.keys()) == 0, len(self.t.keys())
+
+        # 3 minutes
+        self.t._setTimeout(self.timeout/60*3)
+        self.t._reset()
+        for x in range(10, 110):
+            self.t[x] = x
+        fauxtime.sleep(self.timeout)
+        assert len(self.t.keys()) == 100, len(self.t.keys())
+        fauxtime.sleep(self.timeout)
+        assert len(self.t.keys()) == 100, len(self.t.keys())
+        fauxtime.sleep(self.timeout * (self.errmargin+1))
+        assert len(self.t.keys()) == 0, len(self.t.keys())
+
+
     def testItemsGetExpired(self):
         for x in range(10, 110):
             self.t[x] = x
@@ -326,14 +360,14 @@
         fauxtime.sleep(self.timeout * (self.errmargin+1))
         assert len(self.t.keys()) == 0, len(self.t.keys())
 
-    def testGetItemDelaysTimeout(self):
+    def testGetDelaysTimeout(self):
         for x in range(10, 110):
             self.t[x] = x
         # current bucket will become old after we sleep for a while.
         fauxtime.sleep(self.timeout/2)
         # these items will be added to the new current bucket by getitem
         for x in range(10, 110):
-            self.t[x]
+            self.t.get(x)
         fauxtime.sleep(self.timeout/2)
         assert len(self.t.keys()) == 100, len(self.t.keys())
         for x in range(10, 110):
@@ -344,7 +378,7 @@
             self.t[x] = x
         # current bucket will become old after we sleep for a while.
         fauxtime.sleep(self.timeout/2)
-        # these items will be added to the new current bucket by getitem
+        # these items will be added to the new current bucket by setitem
         for x in range(10, 110):
             self.t[x] = x + 1
         fauxtime.sleep(self.timeout/2)
@@ -352,19 +386,6 @@
         for x in range(10, 110):
             assert self.t[x] == x + 1
 
-    def testGetDelaysTimeout(self):
-        for x in range(10, 110):
-            self.t[x] = x
-        # current bucket will become old after we sleep for a while.
-        fauxtime.sleep(self.timeout/2)
-        # these items will be added to the new current bucket by getitem
-        for x in range(10, 110):
-            self.t.get(x)
-        fauxtime.sleep(self.timeout/2)
-        assert len(self.t.keys()) == 100, len(self.t.keys())
-        for x in range(10, 110):
-            assert self.t[x] == x
-
     def testLen(self):
         added = {}
         r = range(10, 1010)
@@ -428,6 +449,7 @@
 
 def test_suite():
     testsuite = makeSuite(TestTransientObjectContainer, 'test')
+    #testsuite = makeSuite(TestBase, 'test')
     alltests = TestSuite((testsuite,))
     return alltests