In fact, the "_v_database_connection" is *NOT* necessary to abort(rollback) commit the transaction:
I have procedure WYJATEK in Oracle that simply raises exception. I've created StoredProcedure in Zope called 'wyjatek' and navigated to 'Test' page. Then I've restarted Zope, and after restart I've just hit 'Test' button. Below is full pdb session (Zope running with one thread only) starting from : /opt/Zope/2.8.8/lib/python/transaction/_transaction.py(492)abort() In short: there are two resource managers registered: 1. <Connection at b62f228c> 2. <MultiObjectResourceAdapter for <Procedure at wyjatek> at -1241707860> When 1. is aborted, then in it's _abort method I have: self._registered_objects == [<Procedure at wyjatek>] and the code below is executed: for obj in self._registered_objects: oid = obj._p_oid (...) self._cache.invalidate(oid) Later, 2. is being aborted and in SP.py _abort I have: self._v_db.db.rollback() AttributeError: '_v_db' So there is no rollback to Oracle. SP.py __call__ is: self._register() # uses: transaction.get().register(Surrogate(self)) try: # Note, this does not do result promotion like the DA query will # which is probably bad. OracleDates in particular look like # DateTimes but arent at all the same! results = apply ( self._v_proc, args, kw ) return results #return self._lobConvert(results) except: logger.error('Error in dco2 procedure') self._v_proc = None raise # Reraise error Is this because of: self._cache.invalidate(oid)? Maciej Wisniowski Full pdb session: 2006-11-21 08:04:16 INFO Zope Ready to handle requests 2006-11-21 08:04:16 DEBUG txn.16386 new transaction 2006-11-21 08:04:16 INFO _transaction abort 2006-11-21 08:04:16 INFO _transaction [] (...) 2006-11-21 08:04:24 DEBUG txn.16386 new transaction 2006-11-21 08:04:24 DEBUG DCOracle2 - DCOracle2.py Created new DCO2 connection <Products.DCOracle2.DCOracle2.DCOracle2.connection instance at 0xb5fd0dac> 2006-11-21 08:04:24 ERROR DCOracle2 - SP.py Error in dco2 procedure 2006-11-21 08:04:24 ERROR Zope.SiteErrorLog http://eurolinux:8091/testOracleDRTest/wyjatek/manage_testZOracleStoredProce... Traceback (most recent call last): File "/opt/Zope/2.8.8/lib/python/ZPublisher/Publish.py", line 114, in publish request, bind=1) File "/opt/Zope/2.8.8/lib/python/ZPublisher/mapply.py", line 88, in mapply if debug is not None: return debug(object,args,context) File "/opt/Zope/2.8.8/lib/python/ZPublisher/Publish.py", line 40, in call_object result=apply(object,args) # Type s<cr> to step into published object. File "/opt/Zope/instancje/oracletest288/Products/DCOracle2/SP.py", line 341, in manage_testZOracleStoredProcedure v = apply(self,[],REQUEST.form) File "/opt/Zope/instancje/oracletest288/Products/DCOracle2/SP.py", line 312, in __call__ results = apply(self._v_proc,args,kw) File "/opt/Zope/instancje/oracletest/Products/DCOracle2/DCOracle2/DCOracle2.py", line 1563, in __call__ cursor.execute(sql,__plist=args) File "/opt/Zope/instancje/oracletest/Products/DCOracle2/DCOracle2/DCOracle2.py", line 1023, in execute result = self._cursor.execute() DatabaseError: (20001, 'ORA-20001: BlaBlaBla\nORA-06512: at "TEST.WYJATEKFUNC", line 6\nORA-06512: at line 1') 2006-11-21 08:04:24 INFO _transaction abort 2006-11-21 08:04:24 INFO _transaction [<Connection at b62f228c>, <MultiObjectResourceAdapter for <Procedure at wyjatek> at -1241707860>] --Return--
/usr/local/lib/python2.3/pdb.py(992)set_trace()->None -> Pdb().set_trace() (Pdb) n /opt/Zope/2.8.8/lib/python/transaction/_transaction.py(492)abort() -> for rm in self._resources: (Pdb) l 487 self._synchronizers.map(lambda s: s.beforeCompletion(self)) 488 489 tb = None 490 logger.info(str(self._resources)) 491 import pdb;pdb.set_trace() 492 -> for rm in self._resources: 493 try: 494 rm.abort(self) 495 except: 496 if tb is None: 497 t, v, tb = sys.exc_info() (Pdb) p self._resources [<Connection at b62f228c>, <MultiObjectResourceAdapter for <Procedure at wyjatek> at -1241707860>] (Pdb) n /opt/Zope/2.8.8/lib/python/transaction/_transaction.py(493)abort() -> try: (Pdb) n /opt/Zope/2.8.8/lib/python/transaction/_transaction.py(494)abort() -> rm.abort(self) (Pdb) s --Call-- /opt/Zope/2.8.8/lib/python/ZODB/Connection.py(338)abort() -> def abort(self, transaction): (Pdb) l 333 ########################################################################## 334 335 ########################################################################## 336 # Data manager (ISavepointDataManager) methods 337 338 -> def abort(self, transaction): 339 """Abort a transaction and forget all changes.""" 340 341 # The order is important here. We want to abort registered 342 # objects before we process the cache. Otherwise, we may un-add 343 # objects added in savepoints. If they've been modified since (Pdb) n /opt/Zope/2.8.8/lib/python/ZODB/Connection.py(348)abort() -> self._abort() (Pdb) l 343 # objects added in savepoints. If they've been modified since 344 # the savepoint, then they won't have _p_oid or _p_jar after 345 # they've been unadded. This will make the code in _abort 346 # confused. 347 348 -> self._abort() 349 350 if self._savepoint_storage is not None: 351 self._abort_savepoint() 352 353 self._tpc_cleanup() (Pdb) s --Call-- /opt/Zope/2.8.8/lib/python/ZODB/Connection.py(355)_abort() -> def _abort(self): (Pdb) n /opt/Zope/2.8.8/lib/python/ZODB/Connection.py(358)_abort() -> for obj in self._registered_objects: (Pdb) l 353 self._tpc_cleanup() 354 355 def _abort(self): 356 """Abort a transaction and forget all changes.""" 357 358 -> for obj in self._registered_objects: 359 oid = obj._p_oid 360 assert oid is not None 361 if oid in self._added: 362 del self._added[oid] 363 del obj._p_jar (Pdb) p self._registered_objects [<Procedure at wyjatek>] (Pdb) n /opt/Zope/2.8.8/lib/python/ZODB/Connection.py(359)_abort() -> oid = obj._p_oid (Pdb) n /opt/Zope/2.8.8/lib/python/ZODB/Connection.py(360)_abort() -> assert oid is not None (Pdb) n /opt/Zope/2.8.8/lib/python/ZODB/Connection.py(361)_abort() -> if oid in self._added: (Pdb) p self._added {} (Pdb) l 356 """Abort a transaction and forget all changes.""" 357 358 for obj in self._registered_objects: 359 oid = obj._p_oid 360 assert oid is not None 361 -> if oid in self._added: 362 del self._added[oid] 363 del obj._p_jar 364 del obj._p_oid 365 else: 366 (Pdb) n /opt/Zope/2.8.8/lib/python/ZODB/Connection.py(386)_abort() -> self._cache.invalidate(oid) (Pdb) l 381 # go ahead and invalidate now. Fortunately, it's 382 # pretty unlikely that the object we are invalidating 383 # was invalidated by another thread, so the risk of a 384 # reread is pretty low. 385 386 -> self._cache.invalidate(oid) 387 388 def _tpc_cleanup(self): 389 """Performs cleanup operations to support tpc_finish and tpc_abort.""" 390 self._conflicts.clear() 391 if not self._synch: (Pdb) s /opt/Zope/2.8.8/lib/python/ZODB/Connection.py(358)_abort() -> for obj in self._registered_objects: (Pdb) n --Return-- /opt/Zope/2.8.8/lib/python/ZODB/Connection.py(358)_abort()->None -> for obj in self._registered_objects: (Pdb) n /opt/Zope/2.8.8/lib/python/ZODB/Connection.py(350)abort() -> if self._savepoint_storage is not None: (Pdb) l 345 # they've been unadded. This will make the code in _abort 346 # confused. 347 348 self._abort() 349 350 -> if self._savepoint_storage is not None: 351 self._abort_savepoint() 352 353 self._tpc_cleanup() 354 355 def _abort(self): (Pdb) n /opt/Zope/2.8.8/lib/python/ZODB/Connection.py(353)abort() -> self._tpc_cleanup() (Pdb) s --Call-- /opt/Zope/2.8.8/lib/python/ZODB/Connection.py(388)_tpc_cleanup() -> def _tpc_cleanup(self): (Pdb) n /opt/Zope/2.8.8/lib/python/ZODB/Connection.py(390)_tpc_cleanup() -> self._conflicts.clear() (Pdb) l 385 386 self._cache.invalidate(oid) 387 388 def _tpc_cleanup(self): 389 """Performs cleanup operations to support tpc_finish and tpc_abort.""" 390 -> self._conflicts.clear() 391 if not self._synch: 392 self._flush_invalidations() 393 self._needs_to_join = True 394 self._registered_objects = [] 395 (Pdb) n /opt/Zope/2.8.8/lib/python/ZODB/Connection.py(391)_tpc_cleanup() -> if not self._synch: (Pdb) n /opt/Zope/2.8.8/lib/python/ZODB/Connection.py(393)_tpc_cleanup() -> self._needs_to_join = True (Pdb) n /opt/Zope/2.8.8/lib/python/ZODB/Connection.py(394)_tpc_cleanup() -> self._registered_objects = [] (Pdb) n --Return-- /opt/Zope/2.8.8/lib/python/ZODB/Connection.py(394)_tpc_cleanup()->None -> self._registered_objects = [] (Pdb) n --Return-- /opt/Zope/2.8.8/lib/python/ZODB/Connection.py(353)abort()->None -> self._tpc_cleanup() (Pdb) n /opt/Zope/2.8.8/lib/python/transaction/_transaction.py(492)abort() -> for rm in self._resources: (Pdb) n /opt/Zope/2.8.8/lib/python/transaction/_transaction.py(493)abort() -> try: (Pdb) n /opt/Zope/2.8.8/lib/python/transaction/_transaction.py(494)abort() -> rm.abort(self) (Pdb) p rm <MultiObjectResourceAdapter for <Procedure at wyjatek> at -1241707860> (Pdb) s --Call-- /opt/Zope/2.8.8/lib/python/transaction/_transaction.py(563)abort() -> def abort(self, txn): (Pdb) l 558 self.ncommitted += 1 559 560 def tpc_vote(self, txn): 561 self.manager.tpc_vote(txn) 562 563 -> def abort(self, txn): 564 tb = None 565 for o in self.objects: 566 try: 567 self.manager.abort(o, txn) 568 except: (Pdb) n /opt/Zope/2.8.8/lib/python/transaction/_transaction.py(564)abort() -> tb = None (Pdb) n /opt/Zope/2.8.8/lib/python/transaction/_transaction.py(565)abort() -> for o in self.objects: (Pdb) p self.objects [<Shared.DC.ZRDB.TM.Surrogate instance at 0xb5fd0c6c>] (Pdb) n /opt/Zope/2.8.8/lib/python/transaction/_transaction.py(566)abort() -> try: (Pdb) n /opt/Zope/2.8.8/lib/python/transaction/_transaction.py(567)abort() -> self.manager.abort(o, txn) (Pdb) s --Call-- /opt/Zope/instancje/oracletest288/Products/DCOracle2/SP.py(158)abort() -> def abort(self, *ignored): (Pdb) l 153 154 if self._v_finalize: 155 try: self._finish() 156 finally: self._v_registered=0 157 158 -> def abort(self, *ignored): 159 try: self._abort() 160 finally: self._v_registered=0 161 162 tpc_abort = abort 163 (Pdb) p self._v_db *** AttributeError: <exceptions.AttributeError instance at 0xb5fd09ec> (Pdb) n /opt/Zope/instancje/oracletest288/Products/DCOracle2/SP.py(159)abort() -> try: self._abort() (Pdb) s --Call-- /opt/Zope/instancje/oracletest288/Products/DCOracle2/SP.py(219)_abort() -> def _abort(self, *ignored): (Pdb) l 214 self._v_db.db.commit() 215 except Exception, e: 216 logger.error( 'Exception in SP.py, _finish: %s' % (e) ) 217 print 'Exception in SP.py, _finish: %s' % (e) 218 219 -> def _abort(self, *ignored): 220 try: 221 logger.info('_abort') 222 self._v_db.db.rollback() 223 except Exception, e: 224 logger.error( 'Exception in SP.py, _abort: %s' % (e) ) (Pdb) p self._v_db *** AttributeError: <exceptions.AttributeError instance at 0xb603988c> (Pdb) n /opt/Zope/instancje/oracletest288/Products/DCOracle2/SP.py(220)_abort() -> try: (Pdb) n /opt/Zope/instancje/oracletest288/Products/DCOracle2/SP.py(221)_abort() -> logger.info('_abort') (Pdb) n 2006-11-21 08:08:18 INFO DCOracle2 - SP.py _abort /opt/Zope/instancje/oracletest288/Products/DCOracle2/SP.py(222)_abort() -> self._v_db.db.rollback() (Pdb) n AttributeError: '_v_db' /opt/Zope/instancje/oracletest288/Products/DCOracle2/SP.py(222)_abort() -> self._v_db.db.rollback() (Pdb) l 217 print 'Exception in SP.py, _finish: %s' % (e) 218 219 def _abort(self, *ignored): 220 try: 221 logger.info('_abort') 222 -> self._v_db.db.rollback() 223 except Exception, e: 224 logger.error( 'Exception in SP.py, _abort: %s' % (e) ) 225 226 def description(self): 227 if getattr(self,"_v_proc",None) is None: self._connect() (Pdb) n /opt/Zope/instancje/oracletest288/Products/DCOracle2/SP.py(223)_abort() -> except Exception, e: (Pdb) n /opt/Zope/instancje/oracletest288/Products/DCOracle2/SP.py(224)_abort() -> logger.error( 'Exception in SP.py, _abort: %s' % (e) ) (Pdb) n 2006-11-21 08:08:52 ERROR DCOracle2 - SP.py Exception in SP.py, _abort: _v_db --Return--
/opt/Zope/instancje/oracletest288/Products/DCOracle2/SP.py(224)_abort()->None -> logger.error( 'Exception in SP.py, _abort: %s' % (e) ) (Pdb) n
/opt/Zope/instancje/oracletest288/Products/DCOracle2/SP.py(160)abort() -> finally: self._v_registered=0 (Pdb) n --Return--
/opt/Zope/instancje/oracletest288/Products/DCOracle2/SP.py(160)abort()->None -> finally: self._v_registered=0 (Pdb) n
/opt/Zope/2.8.8/lib/python/transaction/_transaction.py(565)abort() -> for o in self.objects: (Pdb) n /opt/Zope/2.8.8/lib/python/transaction/_transaction.py(575)abort() -> if tb is not None: (Pdb) n --Return-- /opt/Zope/2.8.8/lib/python/transaction/_transaction.py(575)abort()->None -> if tb is not None: (Pdb) n /opt/Zope/2.8.8/lib/python/transaction/_transaction.py(492)abort() -> for rm in self._resources: (Pdb) n /opt/Zope/2.8.8/lib/python/transaction/_transaction.py(501)abort() -> if self._manager: (Pdb) n /opt/Zope/2.8.8/lib/python/transaction/_transaction.py(502)abort() -> self._manager.free(self) (Pdb) l 497 t, v, tb = sys.exc_info() 498 self.log.error("Failed to abort resource manager: %s", 499 rm, exc_info=sys.exc_info()) 500 501 if self._manager: 502 -> self._manager.free(self) 503 504 self._synchronizers.map(lambda s: s.afterCompletion(self)) 505 506 self.log.debug("abort") 507 (Pdb) p self._manager <transaction._manager.ThreadTransactionManager object at 0xb715aaec> (Pdb) n /opt/Zope/2.8.8/lib/python/transaction/_transaction.py(504)abort() -> self._synchronizers.map(lambda s: s.afterCompletion(self)) (Pdb) n /opt/Zope/2.8.8/lib/python/transaction/_transaction.py(506)abort() -> self.log.debug("abort") (Pdb) c 2006-11-21 08:09:33 DEBUG txn.16386 abort 2006-11-21 08:09:33 DEBUG txn.16386 new transaction 2006-11-21 08:09:33 INFO _transaction abort 2006-11-21 08:09:33 INFO _transaction [] --Return-- /usr/local/lib/python2.3/pdb.py(992)set_trace()->None -> Pdb().set_trace()