From fdf717d7e57a8a7f21c00f9431d5fe7b20d83e89 Mon Sep 17 00:00:00 2001 From: schlatterbeck Date: Fri, 22 Oct 2010 14:14:26 +0000 Subject: [PATCH] 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. git-svn-id: http://svn.roundup-tracker.org/svnroot/roundup/roundup/trunk@4557 57a73879-2fb5-44c3-a270-3262357dd7e2 --- CHANGES.txt | 7 ++++++- roundup/backends/rdbms_common.py | 5 +++++ test/db_test_base.py | 32 ++++++++++++++++++++++++++++++++ test/test_mysql.py | 11 +++++++++++ test/test_postgresql.py | 12 ++++++++++++ test/test_sqlite.py | 5 +++++ 6 files changed, 71 insertions(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index 08dfdb5..5cd7a38 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -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) diff --git a/roundup/backends/rdbms_common.py b/roundup/backends/rdbms_common.py index 13589a2..2637f88 100644 --- a/roundup/backends/rdbms_common.py +++ b/roundup/backends/rdbms_common.py @@ -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() diff --git a/test/db_test_base.py b/test/db_test_base.py index 7354e67..0ddc8f5 100644 --- a/test/db_test_base.py +++ b/test/db_test_base.py @@ -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 : diff --git a/test/test_mysql.py b/test/test_mysql.py index 75e862a..760550b 100644 --- a/test/test_mysql.py +++ b/test/test_mysql.py @@ -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__': diff --git a/test/test_postgresql.py b/test/test_postgresql.py index 3d2629c..52e82f7 100644 --- a/test/test_postgresql.py +++ b/test/test_postgresql.py @@ -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 : diff --git a/test/test_sqlite.py b/test/test_sqlite.py index 2ea8eea..cec5a9a 100644 --- a/test/test_sqlite.py +++ b/test/test_sqlite.py @@ -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__': -- 2.30.2