[Zodb-checkins] CVS: Zope/lib/python/ZODB/tests - testZODB.py:1.9.2.1
Shane Hathaway
shane@zope.com
Tue, 11 Feb 2003 12:01:11 -0500
Update of /cvs-repository/Zope/lib/python/ZODB/tests
In directory cvs.zope.org:/tmp/cvs-serv18227/tests
Modified Files:
Tag: shane-conflict-handling-branch
testZODB.py
Log Message:
Added a test that demonstrates how ZODB allows bare "except" clauses to
generate inconsistent data. The next task is to make ZODB raise a conflict
error when this happens.
=== Zope/lib/python/ZODB/tests/testZODB.py 1.9 => 1.9.2.1 ===
--- Zope/lib/python/ZODB/tests/testZODB.py:1.9 Fri Jan 17 12:23:16 2003
+++ Zope/lib/python/ZODB/tests/testZODB.py Tue Feb 11 12:01:10 2003
@@ -174,6 +174,94 @@
conn2.close()
+ def checkConflictingTransactionCommit(self):
+ # Verify Zope doesn't commit a transaction with conflicting data.
+ # In this test, the "real data" doesn't conflict, but the "index"
+ # does. This simulates what happens in ZCatalog.
+ from ZODB.POSException import ConflictError
+ conn = self._db.open()
+ try:
+ root = conn.root()
+ # real_data contains { key -> value }
+ real_data = PersistentMapping()
+ root['real_data'] = real_data
+ # index contains { value -> [key,] }
+ index = PersistentMapping()
+ root['index'] = index
+
+ # Populate the data with a->false and b->true.
+ real_data['a'] = PersistentMapping({'indexed_value': 'false'})
+ real_data['b'] = PersistentMapping({'indexed_value': 'true'})
+ index['true'] = PersistentMapping({'b': 1})
+ index['false'] = PersistentMapping({'a': 1})
+ get_transaction().commit()
+
+ conn2 = self._db.open()
+ try:
+ # Open another connection.
+ conn2.setLocalTransaction()
+ conn2.getTransaction().begin()
+ root2 = conn2.root()
+ real_data2 = root2['real_data']
+ index2 = root2['index']
+
+ # Change the 'b' value in one transaction.
+ real_data['b']['indexed_value'] = 'false'
+ del index['true']['b']
+ index['false']['b'] = 1
+ get_transaction().commit()
+
+ # now try to change the 'a' value in a simultaneous
+ # transaction. The data itself will not conflict,
+ # but the index will.
+ real_data2['a']['indexed_value'] = 'true'
+
+ try:
+ del index2['false']['a']
+ index2['true']['a'] = 1
+ except:
+ # This bare except clause is intentional.
+ # The purpose of this test is to verify ZODB doesn't
+ # commit conflicting transactions even though bare
+ # except clauses are present in application code.
+ pass
+
+ # Precondition: real_data2['a'] is still ready to commit.
+ self.assertEqual(real_data2['a']['indexed_value'], 'true')
+ self.assertEqual(real_data2['a']._p_changed, 1)
+
+ # Precondition: index2 values are *not* ready to commit.
+ # (In fact, they are probably ghosted.)
+ self.assertNotEqual(index2._p_changed, 1)
+ self.assertNotEqual(index2['false']._p_changed, 1)
+ self.assertNotEqual(index2['true']._p_changed, 1)
+
+ # Now, if ZODB commits this transaction, the index will be
+ # out of sync with the data. Verify ZODB refuses to
+ # commit it.
+ self.assertRaises(ConflictError,
+ conn2.getTransaction().commit)
+ # Verify ZODB continues to refuse it.
+ self.assertRaises(ConflictError,
+ conn2.getTransaction().commit)
+
+ # Abort the transaction and verify the state of
+ # the database. Both 'a' and 'b' have the value 'false'.
+ conn2.getTransaction().begin()
+ self.assertEqual(real_data2['a']['indexed_value'], 'false')
+ self.assertEqual(real_data2['b']['indexed_value'], 'false')
+ self.assertEqual(len(index2['false']), 2)
+ self.assertEqual(index2['false']['a'], 1)
+ self.assertEqual(index2['false']['b'], 1)
+ self.assertEqual(len(index2['true']), 0)
+ finally:
+ conn2.close()
+
+ finally:
+ conn.close()
+
+
+
def test_suite():
return unittest.makeSuite(ZODBTests, 'check')