def test_conflict_del_4(self): """Check conflict detection. We modify and delete the same object in different transactions, simulating separate processes.""" foo = Foo('foo-first') self.dm.root['foo'] = foo transaction.commit() conn1 = testing.getConnection(testing.DBNAME) dm1 = datamanager.PJDataManager(conn1) conn2 = testing.getConnection(testing.DBNAME) dm2 = datamanager.PJDataManager(conn2) self.assertEqual(dm2.root['foo'].name, 'foo-first') del dm2.root['foo'] self.assertEqual(dm1.root['foo'].name, 'foo-first') dm1.root['foo'].name = 'foo-second' #Finish in order 1 - 2 # well, try to... dm1.tpc_finish will block until dm2 is done dm1.tpc_begin(None) dm2.tpc_begin(None) @testing.run_in_thread def background_commit(): dm1.tpc_finish(None) with self.assertRaises(interfaces.ConflictError): dm2.tpc_finish(None) transaction.abort() conn2.close() conn1.close()
def test_conflict_del_1(self): """Check conflict detection. We modify and delete the same object in different transactions, simulating separate processes.""" txn = transaction.manager.get() foo = Foo('foo-first') self.dm.root['foo'] = foo transaction.commit() conn1 = testing.getConnection(testing.DBNAME) conn2 = testing.getConnection(testing.DBNAME) dm2 = datamanager.PJDataManager(conn2) self.assertEqual(dm2.root['foo'].name, 'foo-first') del dm2.root['foo'] dm1 = datamanager.PJDataManager(conn1) self.assertEqual(dm1.root['foo'].name, 'foo-first') dm1.root['foo'].name = 'foo-second' #Finish in order 2 - 1 with self.assertRaises(interfaces.ConflictError): transaction.commit() transaction.abort() conn2.close() conn1.close()
def test_conflict_del_1(self): """Check conflict detection. We modify and delete the same object in different transactions, simulating separate processes.""" foo = Foo('foo-first') self.dm.root['foo'] = foo transaction.commit() conn1 = testing.getConnection(testing.DBNAME) dm1 = datamanager.PJDataManager(conn1) self.assertEqual(dm1.root['foo'].name, 'foo-first') dm1.root['foo'].name = 'foo-second' conn2 = testing.getConnection(testing.DBNAME) dm2 = datamanager.PJDataManager(conn2) self.assertEqual(dm2.root['foo'].name, 'foo-first') del dm2.root['foo'] #Finish in order 2 - 1 dm2.tpc_begin(None) dm2.commit(None) dm2.tpc_vote(None) dm2.tpc_finish(None) dm1.tpc_begin(None) with self.assertRaises(interfaces.ConflictError): dm1.commit() transaction.abort() conn2.close() conn1.close()
def test_conflict_tracebacks(self): """Verify conflict tracebacks are captured properly and reset on the next transaction.""" ctb = datamanager.CONFLICT_TRACEBACK_INFO.traceback self.assertIsNone(ctb) foo = Foo('foo-first') self.dm.root['foo'] = foo transaction.commit() conn2 = testing.getConnection(testing.DBNAME) dm2 = datamanager.PJDataManager(conn2) del dm2.root['foo'] conn1 = testing.getConnection(testing.DBNAME) dm1 = datamanager.PJDataManager(conn1) dm1.root['foo'].name = 'foo-second' ctb = datamanager.CONFLICT_TRACEBACK_INFO.traceback self.assertIsNone(ctb) # Finish in order 2 - 1 with self.assertRaises(interfaces.ConflictError): transaction.commit() # verify by length that we have the full traceback ctb = datamanager.CONFLICT_TRACEBACK_INFO.traceback self.assertIsNotNone(ctb) # Make all work self.assertTrue(len(ctb) > 20) self.assertIn('Beacon:', ctb[-1]) transaction.abort() # start another transaction and verify the traceback # is reset datamanager.PJDataManager(conn2) ctb = datamanager.CONFLICT_TRACEBACK_INFO.traceback self.assertIsNone(ctb) conn2.close() conn1.close()
def test_conflict_del_2(self): """Check conflict detection. We modify and delete the same object in different transactions, simulating separate processes.""" foo = Foo('foo-first') self.dm.root['foo'] = foo transaction.commit() conn1 = testing.getConnection(testing.DBNAME) dm1 = datamanager.PJDataManager(conn1) self.assertEqual(dm1.root['foo'].name, 'foo-first') dm1.root['foo'].name = 'foo-second' conn2 = testing.getConnection(testing.DBNAME) dm2 = datamanager.PJDataManager(conn2) self.assertEqual(dm2.root['foo'].name, 'foo-first') del dm2.root['foo'] #Finish in order 1 - 2 # well, try to... dm1.tpc_finish will block until dm2 is done @testing.run_in_thread def background_commit(): with self.assertRaises(interfaces.ConflictError): dm1.commit(None) dm2.commit(None) transaction.abort() conn2.close() conn1.close()
def test_conflict_commit_1(self): """Test conflict on commit The typical detail string for such failures is: DETAIL: Reason code: Canceled on identification as a pivot, during commit attempt. """ # We will not reproduce the full scenario with pjpersist, however we # will pretend the right exception is thrown by commit. # # First, get the error, that psycopg throws in such case # The example is taken from https://wiki.postgresql.org/wiki/SSI conn1 = self.conn conn2 = testing.getConnection(testing.DBNAME) with conn1.cursor() as cur: cur.execute("DROP TABLE IF EXISTS mytab") cur.execute( "CREATE TABLE mytab (class int NOT NULL, value int NOT NULL )") cur.execute( "INSERT INTO mytab VALUES (1, 10), (1, 20), (2, 100), (2, 200)" ) conn1.commit() with conn1.cursor() as cur1, conn2.cursor() as cur2: cur1.execute("SELECT SUM(value) FROM mytab WHERE class = 1") cur1.execute("INSERT INTO mytab VALUES (2, 30)") cur2.execute("SELECT SUM(value) FROM mytab WHERE class = 2") cur2.execute("INSERT INTO mytab VALUES (1, 300)") conn2.commit() conn2.close() # Now datamanager, holding conn1 is in doomed state. it is expected to # fail on commit attempt. txn = transaction.get() txn.join(self.dm) with self.assertRaises(interfaces.ConflictError): transaction.commit()
def test_conflict_commit_1(self): """Test conflict on commit The typical detail string for such failures is: DETAIL: Reason code: Canceled on identification as a pivot, during commit attempt. """ # We will not reproduce the full scenario with pjpersist, however we will # pretend the right exception is thrown by commit. # # First, get the error, that psycopg throws in such case # The example is taken from https://wiki.postgresql.org/wiki/SSI import psycopg2 conn1 = self.conn conn2 = testing.getConnection(testing.DBNAME) with conn1.cursor() as cur: cur.execute("DROP TABLE IF EXISTS mytab") cur.execute("CREATE TABLE mytab (class int NOT NULL, value int NOT NULL )") cur.execute("INSERT INTO mytab VALUES (1, 10), (1, 20), (2, 100), (2, 200)") conn1.commit() with conn1.cursor() as cur1, conn2.cursor() as cur2: cur1.execute("SELECT SUM(value) FROM mytab WHERE class = 1") cur1.execute("INSERT INTO mytab VALUES (2, 30)") cur2.execute("SELECT SUM(value) FROM mytab WHERE class = 2") cur2.execute("INSERT INTO mytab VALUES (1, 300)") conn2.commit() conn2.close() # Now datamanager, holding conn1 is in doomed state. it is expected to # fail on commit attempt. txn = transaction.get() txn.join(self.dm) with self.assertRaises(interfaces.ConflictError): transaction.commit()
def setUp(self): super(DirtyTestCase, self).setUp() # get rid of the previous transaction transaction.abort() tpc_patch = mock.patch( "pjpersist.datamanager.PJ_TWO_PHASE_COMMIT_ENABLED", True) no_prep_patch = mock.patch( "pjpersist.datamanager." "CALL_TPC_PREPARE_ON_NO_WRITE_TRANSACTION", False) log_patch = mock.patch( "pjpersist.datamanager.LOG_READ_WRITE_TRANSACTION", True) self.patches = [tpc_patch, no_prep_patch, log_patch] for p in self.patches: p.start() # First PJDataManager instantiation creates tables, what makes the dm # dirty, which we want to avoid here. self.conn = testing.getConnection(testing.DBNAME) self.dm = datamanager.PJDataManager(self.conn)