[Zodb-checkins] SVN: ZODB/trunk/src/ There is a new pool-timeout database configuration option to specify that

Jim Fulton jim at zope.com
Sun Jan 4 16:06:28 EST 2009


Log message for revision 94510:
  There is a new pool-timeout database configuration option to specify that
  connections unused after the given time interval should be garbage
  colection.  This will provide a means of dealing with extra
  connections that are created in rare circumstances and that would
  consume an unreasonable amount of memory.
  

Changed:
  U   ZODB/trunk/src/CHANGES.txt
  U   ZODB/trunk/src/ZODB/DB.py
  U   ZODB/trunk/src/ZODB/component.xml
  U   ZODB/trunk/src/ZODB/config.py
  U   ZODB/trunk/src/ZODB/tests/testConfig.py

-=-
Modified: ZODB/trunk/src/CHANGES.txt
===================================================================
--- ZODB/trunk/src/CHANGES.txt	2009-01-04 21:03:35 UTC (rev 94509)
+++ ZODB/trunk/src/CHANGES.txt	2009-01-04 21:06:28 UTC (rev 94510)
@@ -63,6 +63,12 @@
   wastage.  Now, when connections are placed on the stack, they sink
   below existing connections that have more active objects.
 
+- There is a new pool-timeout database configuration option to specify that
+  connections unused after the given time interval should be garbage
+  colection.  This will provide a means of dealing with extra
+  connections that are created in rare circumstances and that would
+  consume an unreasonable amount of memory.
+
 3.9.0a8 (2008-12-15)
 ====================
 

Modified: ZODB/trunk/src/ZODB/DB.py
===================================================================
--- ZODB/trunk/src/ZODB/DB.py	2009-01-04 21:03:35 UTC (rev 94509)
+++ ZODB/trunk/src/ZODB/DB.py	2009-01-04 21:06:28 UTC (rev 94510)
@@ -12,9 +12,8 @@
 #
 ##############################################################################
 """Database objects
+"""
 
-$Id$"""
-
 import warnings
 
 import cPickle
@@ -70,7 +69,7 @@
     connectionDebugInfo() can still gather statistics.
     """
 
-    def __init__(self, size, timeout=None):
+    def __init__(self, size, timeout):
         # The largest # of connections we expect to see alive simultaneously.
         self._size = size
 
@@ -95,8 +94,7 @@
     def setTimeout(self, timeout):
         old = self._timeout
         self._timeout = timeout
-        if timeout is not None and old != timeout and (
-            old is None or old > timeout):
+        if timeout < old:
             self._reduce_size()
 
     def getSize(self):
@@ -112,7 +110,7 @@
 class ConnectionPool(AbstractConnectionPool):
 
     # XXX WTF, passing time.time() as a default?
-    def __init__(self, size, timeout=time.time()):
+    def __init__(self, size, timeout=1<<31):
         super(ConnectionPool, self).__init__(size, timeout)
 
         # A stack of connections available to hand out.  This is a subset
@@ -245,7 +243,7 @@
 
     # see the comments in ConnectionPool for method descriptions.
 
-    def __init__(self, size, timeout=time.time()):
+    def __init__(self, size, timeout=1<<31):
         super(KeyedConnectionPool, self).__init__(size, timeout)
         self.pools = {}
 
@@ -305,9 +303,8 @@
         for pool in self.pools.itervalues():
             result.extend(pool.available)
         return tuple(result)
-        
-        
 
+
 def toTimeStamp(dt):
     utc_struct = dt.utctimetuple()
     # if this is a leapsecond, this will probably fail.  That may be a good
@@ -379,6 +376,7 @@
 
     def __init__(self, storage,
                  pool_size=7,
+                 pool_timeout=1<<31,
                  cache_size=400,
                  cache_size_bytes=0,
                  historical_pool_size=3,
@@ -416,7 +414,7 @@
         self._r = x.release
 
         # pools and cache sizes
-        self.pool = ConnectionPool(pool_size)
+        self.pool = ConnectionPool(pool_size, pool_timeout)
         self.historical_pool = KeyedConnectionPool(historical_pool_size,
                                                    historical_timeout)
         self._cache_size = cache_size
@@ -838,7 +836,7 @@
         finally:
             self._r()
 
-    def setHistoricalCacheSize(self, size):       
+    def setHistoricalCacheSize(self, size):
         self._a()
         try:
             self._historical_cache_size = size
@@ -848,7 +846,7 @@
         finally:
             self._r()
 
-    def setHistoricalCacheSizeBytes(self, size):       
+    def setHistoricalCacheSizeBytes(self, size):
         self._a()
         try:
             self._historical_cache_size_bytes = size

Modified: ZODB/trunk/src/ZODB/component.xml
===================================================================
--- ZODB/trunk/src/ZODB/component.xml	2009-01-04 21:03:35 UTC (rev 94509)
+++ ZODB/trunk/src/ZODB/component.xml	2009-01-04 21:06:28 UTC (rev 94510)
@@ -250,6 +250,11 @@
         and exceeding twice pool-size connections causes a critical
         message to be logged.
       </description>
+    <key name="pool-timeout" datatype="time-interval"/>
+      <description>
+        The minimum interval that an unused (non-historical)
+        connection should be kept.
+      </description>
     <key name="historical-pool-size" datatype="integer" default="3"/>
       <description>
         The expected maximum total number of historical connections

Modified: ZODB/trunk/src/ZODB/config.py
===================================================================
--- ZODB/trunk/src/ZODB/config.py	2009-01-04 21:03:35 UTC (rev 94509)
+++ ZODB/trunk/src/ZODB/config.py	2009-01-04 21:06:28 UTC (rev 94510)
@@ -92,6 +92,10 @@
     def open(self, databases=None):
         section = self.config
         storage = section.storage.open()
+        options = {}
+        if section.pool_timeout is not None:
+            options['pool_timeout'] = section.pool_timeout
+
         try:
             return ZODB.DB(
                 storage,
@@ -104,7 +108,7 @@
                 historical_timeout=section.historical_timeout,
                 database_name=section.database_name,
                 databases=databases,
-                )
+                **options)
         except:
             storage.close()
             raise
@@ -127,7 +131,7 @@
                     base = factory.open()
                 else:
                     raise ValueError("Too many base storages defined!")
-        
+
         from ZODB.DemoStorage import DemoStorage
         return DemoStorage(self.config.name, base=base, changes=changes)
 
@@ -139,7 +143,7 @@
         if self.config.packer:
             m, name = self.config.packer.rsplit('.', 1)
             options['packer'] = getattr(__import__(m, {}, {}, ['*']), name)
-            
+
         return FileStorage(self.config.path,
                            create=self.config.create,
                            read_only=self.config.read_only,
@@ -169,7 +173,7 @@
             options['blob_cache_size'] = self.config.blob_cache_size
         if self.config.blob_cache_size_check is not None:
             options['blob_cache_size_check'] = self.config.blob_cache_size_check
-                    
+
         return ClientStorage(
             L,
             blob_dir=self.config.blob_dir,

Modified: ZODB/trunk/src/ZODB/tests/testConfig.py
===================================================================
--- ZODB/trunk/src/ZODB/tests/testConfig.py	2009-01-04 21:03:35 UTC (rev 94509)
+++ ZODB/trunk/src/ZODB/tests/testConfig.py	2009-01-04 21:06:28 UTC (rev 94510)
@@ -19,9 +19,10 @@
 import ZODB.config
 import ZODB.POSException
 import ZODB.tests.util
+from zope.testing import doctest
 
 class ConfigTestBase(ZODB.tests.util.TestCase):
-        
+
     def _opendb(self, s):
         return ZODB.config.databaseFromString(s)
 
@@ -128,11 +129,35 @@
             os.path.abspath('blobs'))
         self.assertRaises(ClientDisconnected, self._test, cfg)
 
+def db_connection_pool_timeout():
+    """
+Test that the database pool timeout option works:
 
+    >>> db = ZODB.config.databaseFromString('''
+    ...     <zodb>
+    ...       <mappingstorage/>
+    ...     </zodb>
+    ... ''')
+    >>> db.pool._timeout == 1<<31
+    True
+
+    >>> db = ZODB.config.databaseFromString('''
+    ...     <zodb>
+    ...       pool-timeout 600
+    ...       <mappingstorage/>
+    ...     </zodb>
+    ... ''')
+    >>> db.pool._timeout == 600
+    True
+
+    """
+
+
 def test_suite():
     suite = unittest.TestSuite()
     suite.addTest(unittest.makeSuite(ZODBConfigTest))
     suite.addTest(unittest.makeSuite(ZEOConfigTest))
+    suite.addTest(doctest.DocTestSuite())
     return suite
 
 



More information about the Zodb-checkins mailing list