Code

clear the cache on commit for rdbms backends: Don't carry over cached
authorschlatterbeck <schlatterbeck@57a73879-2fb5-44c3-a270-3262357dd7e2>
Fri, 22 Oct 2010 14:14:26 +0000 (14:14 +0000)
committerschlatterbeck <schlatterbeck@57a73879-2fb5-44c3-a270-3262357dd7e2>
Fri, 22 Oct 2010 14:14:26 +0000 (14:14 +0000)
values from one transaction to the next (there may be other changes from
other transactions) see new ConcurrentDBTest for a read-modify-update
cycle that fails with the old caching behavior.

git-svn-id: http://svn.roundup-tracker.org/svnroot/roundup/roundup/trunk@4557 57a73879-2fb5-44c3-a270-3262357dd7e2

CHANGES.txt
roundup/backends/rdbms_common.py
test/db_test_base.py
test/test_mysql.py
test/test_postgresql.py
test/test_sqlite.py

index 08dfdb5e34fa46e97078ce2a4309b85287392a6f..5cd7a38847532617f90a90832e5df082ac8f2e65 100644 (file)
@@ -20,7 +20,12 @@ Fixed:
 - Some minor typos fixed in doc/customizing.txt (Thanks Ralf Hemmecke).
 - XML-RPC documentation now linked from the docs/index (Bernhard Reiter).
 - Fix setting of sys.path when importing schema.py, fixes issue2550675,
-  thanks to Bryce L Nordgren for reporting.
+  thanks to Bryce L Nordgren for reporting. (Ralf Schlatterbeck)
+- clear the cache on commit for rdbms backends: Don't carry over cached
+  values from one transaction to the next (there may be other changes
+  from other transactions) see new ConcurrentDBTest for a
+  read-modify-update cycle that fails with the old caching behavior.
+  (Ralf Schlatterbeck)
 
 2010-10-08 1.4.16 (r4541)
 
index 13589a222eae7958c8c84a25879da53d41374e18..2637f8868d04fe9d389ffdff70e5e002a2b89ca3 100644 (file)
@@ -1301,6 +1301,11 @@ class Database(FileStorage, hyperdb.Database, roundupdb.Database):
         # clear out the transactions
         self.transactions = []
 
+        # clear the cache: Don't carry over cached values from one
+        # transaction to the next (there may be other changes from other
+        # transactions)
+        self.clearCache()
+
     def sql_rollback(self):
         self.conn.rollback()
 
index 7354e67fff0d902caf09eb2819e8810646cee085..0ddc8f5d6d472ec50d161581480d713d2035aa04 100644 (file)
@@ -2172,4 +2172,36 @@ class ClassicInitTest(unittest.TestCase):
         except OSError, error:
             if error.errno not in (errno.ENOENT, errno.ESRCH): raise
 
+class ConcurrentDBTest(ClassicInitTest):
+    def testConcurrency(self):
+        # The idea here is a read-modify-update cycle in the presence of
+        # a cache that has to be properly handled. The same applies if
+        # we extend a String or otherwise modify something that depends
+        # on the previous value.
+
+        # set up and open a tracker
+        tracker = setupTracker(self.dirname, self.backend)
+        # open the database
+        self.db = tracker.open('admin')
+
+        prio = '1'
+        self.assertEqual(self.db.priority.get(prio, 'order'), 1.0)
+        def inc(db):
+            db.priority.set(prio, order=db.priority.get(prio, 'order') + 1)
+
+        inc(self.db)
+
+        db2 = tracker.open("admin")
+        self.assertEqual(db2.priority.get(prio, 'order'), 1.0)
+        db2.commit()
+        self.db.commit()
+        self.assertEqual(self.db.priority.get(prio, 'order'), 2.0)
+
+        inc(db2)
+        db2.commit()
+        db2.clearCache()
+        self.assertEqual(db2.priority.get(prio, 'order'), 3.0)
+        db2.close()
+
+
 # vim: set et sts=4 sw=4 :
index 75e862a7ef6fb08ab74a75a6332e326cb8c8f7b8..760550bd83bfee4d8b6b7a844d6f9141f62ddcf4 100644 (file)
@@ -23,6 +23,7 @@ from roundup.hyperdb import DatabaseError
 from roundup.backends import get_backend, have_backend
 
 from db_test_base import DBTest, ROTest, config, SchemaTest, ClassicInitTest
+from db_test_base import ConcurrentDBTest
 
 
 class mysqlOpener:
@@ -63,6 +64,15 @@ class mysqlClassicInitTest(mysqlOpener, ClassicInitTest):
         ClassicInitTest.tearDown(self)
         self.nuke_database()
 
+class mysqlConcurrencyTest(mysqlOpener, ConcurrentDBTest):
+    backend = 'mysql'
+    def setUp(self):
+        mysqlOpener.setUp(self)
+        ClassicInitTest.setUp(self)
+    def tearDown(self):
+        ClassicInitTest.tearDown(self)
+        self.nuke_database()
+
 from session_common import RDBMSTest
 class mysqlSessionTest(mysqlOpener, RDBMSTest):
     def setUp(self):
@@ -92,6 +102,7 @@ def test_suite():
         suite.addTest(unittest.makeSuite(mysqlSchemaTest))
         suite.addTest(unittest.makeSuite(mysqlClassicInitTest))
         suite.addTest(unittest.makeSuite(mysqlSessionTest))
+        suite.addTest(unittest.makeSuite(mysqlConcurrencyTest))
     return suite
 
 if __name__ == '__main__':
index 3d2629c32bf947f37d397d37444d9da830a33e8f..52e82f7a8420f054e6adc9ada52cc1bd0e999004 100644 (file)
@@ -22,6 +22,7 @@ import unittest
 from roundup.hyperdb import DatabaseError
 
 from db_test_base import DBTest, ROTest, config, SchemaTest, ClassicInitTest
+from db_test_base import ConcurrentDBTest
 
 from roundup.backends import get_backend, have_backend
 
@@ -57,6 +58,16 @@ class postgresqlROTest(postgresqlOpener, ROTest):
         ROTest.tearDown(self)
         postgresqlOpener.tearDown(self)
 
+class postgresqlConcurrencyTest(postgresqlOpener, ConcurrentDBTest):
+    backend = 'postgresql'
+    def setUp(self):
+        postgresqlOpener.setUp(self)
+        ConcurrentDBTest.setUp(self)
+
+    def tearDown(self):
+        ConcurrentDBTest.tearDown(self)
+        postgresqlOpener.tearDown(self)
+
 class postgresqlSchemaTest(postgresqlOpener, SchemaTest):
     def setUp(self):
         postgresqlOpener.setUp(self)
@@ -102,6 +113,7 @@ def test_suite():
     suite.addTest(unittest.makeSuite(postgresqlSchemaTest))
     suite.addTest(unittest.makeSuite(postgresqlClassicInitTest))
     suite.addTest(unittest.makeSuite(postgresqlSessionTest))
+    suite.addTest(unittest.makeSuite(postgresqlConcurrencyTest))
     return suite
 
 # vim: set et sts=4 sw=4 :
index 2ea8eea29c10b9fe1f155df21db00f5c2d1dba61..cec5a9ad037ca35d41829ba21941596046773903 100644 (file)
@@ -21,6 +21,7 @@ import unittest, os, shutil, time
 from roundup.backends import get_backend, have_backend
 
 from db_test_base import DBTest, ROTest, SchemaTest, ClassicInitTest, config
+from db_test_base import ConcurrentDBTest
 
 class sqliteOpener:
     if have_backend('sqlite'):
@@ -41,6 +42,9 @@ class sqliteSchemaTest(sqliteOpener, SchemaTest):
 class sqliteClassicInitTest(ClassicInitTest):
     backend = 'sqlite'
 
+class sqliteConcurrencyTest(ConcurrentDBTest):
+    backend = 'sqlite'
+
 from session_common import RDBMSTest
 class sqliteSessionTest(sqliteOpener, RDBMSTest):
     pass
@@ -57,6 +61,7 @@ def test_suite():
     suite.addTest(unittest.makeSuite(sqliteSchemaTest))
     suite.addTest(unittest.makeSuite(sqliteClassicInitTest))
     suite.addTest(unittest.makeSuite(sqliteSessionTest))
+    suite.addTest(unittest.makeSuite(sqliteConcurrencyTest))
     return suite
 
 if __name__ == '__main__':